diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java index 83fa87ff2f3000993c32e2c9e5798f62357e9727..c7951081c7ccc80c47ed8a76dc2db42e87eb8bc2 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java @@ -2,12 +2,14 @@ package cz.fidentis.analyst.symmetry; import java.io.Serializable; import java.util.List; +import javax.vecmath.Point3d; import javax.vecmath.Vector3d; /** * Symmetry plane. * * @author Natalia Bebjakova + * @author Dominik Racek */ public class Plane implements Serializable { @@ -103,12 +105,21 @@ public class Plane implements Serializable { } public Vector3d getNormal() { - return normal; + return new Vector3d(normal); } public double getDistance() { return distance; } + + /** + * Translate the plane along its normal + * + * @param value + */ + public void translate(double value) { + this.distance += value; + } protected void setNormal(Vector3d normal) { this.normal = normal; @@ -117,4 +128,32 @@ public class Plane implements Serializable { protected void setDistance(double dist) { this.distance = dist; } + + /** + * Calculates an intersection of a plane and a line given by two points + * + * @param p1 first point of the line + * @param p2 second point of the line + * @return The point of intersection of null if no point found + */ + public Point3d getIntersectionWithLine(Point3d p1, Point3d p2) { + double distance1 = ((normal.x * p1.x) + (normal.y * p1.y) + (normal.z * p1.z) + distance) + / Math.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z); + + double distance2 = ((normal.x * p2.x) + (normal.y * p2.y) + (normal.z * p2.z) + distance) + / Math.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z); + + double t = distance1 / (distance1 - distance2); + + if (distance1 * distance2 > 0) { + return null; + } + + Point3d output = new Point3d(p2); + output.sub(p1); + output.scale(t); + output.add(p1); + + return output; + } } diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSection.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSection.java index 186d429cec9e952caa8a09320bff7cbd17a23a41..aedee2ece3a5f0b183c5505f7af6a75d9a66a0a2 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSection.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSection.java @@ -1,15 +1,19 @@ package cz.fidentis.analyst.visitors.mesh; +import cz.fidentis.analyst.Logger; import cz.fidentis.analyst.mesh.MeshVisitor; import cz.fidentis.analyst.mesh.core.MeshFacet; import cz.fidentis.analyst.mesh.core.MeshTriangle; +import cz.fidentis.analyst.symmetry.Plane; import javax.vecmath.Point3d; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** - * A visitor that calculates the cross-section of a face and a cutting plane. + * A visitor that calculates the cross-section of a face and a cutting plane using the triangles. * <p> * This visitor is thread-safe. * </p> @@ -18,39 +22,142 @@ import java.util.List; */ public class CrossSection extends MeshVisitor { private List<Point3d> points; - private MeshFacet plane; + private Set<Point3d> usedPoints; + private Plane plane; + + private Set<MeshTriangle> visited; /** * Constructor * @param plane the cutting plane */ - public CrossSection(MeshFacet plane) { + public CrossSection(Plane plane) { this.plane = plane; this.points = new ArrayList<>(); + this.usedPoints = new HashSet<>(); + } + + private List<MeshTriangle> getValidNeighboringTriangles(MeshFacet facet, MeshTriangle tri) { + List<MeshTriangle> output = facet.getNeighboringTriangles(tri); + output.removeIf(n -> ((visited.contains(n)) + || (!n.checkIntersectionWithPlane(plane.getNormal(), plane.getDistance())))); + return output; + } + + private List<MeshTriangle> getValidAdjacentTriangles(MeshFacet facet, MeshTriangle tri) { + List<MeshTriangle> output = facet.getAdjacentTriangles(tri); + output.removeIf(n -> ((visited.contains(n)) + || (!n.checkIntersectionWithPlane(plane.getNormal(), plane.getDistance())))); + return output; + } + + private void addPoint(MeshTriangle tri, MeshTriangle previous, boolean direction) { + List<Point3d> commonEdge = tri.getCommonPoints(previous); + if (commonEdge.size() == 2) { + Point3d p = plane.getIntersectionWithLine(commonEdge.get(0), commonEdge.get(1)); + if (p == null) { + return; + } + + if (!usedPoints.contains(p)) { + usedPoints.add(p); + + if (direction) { + points.add(p); + } else { + points.add(0, p); + } + } + } + } + + private void visitMeshFacetRec(MeshFacet facet, MeshTriangle tri, MeshTriangle previous, boolean direction) { + visited.add(tri); + + // Depending on the direction, add the point to the start or to the end of points + addPoint(tri, previous, direction); + + // Recursively call to the end + // Get all neighbors that have not been visited and are intercepted + List<MeshTriangle> neighbors = getValidNeighboringTriangles(facet, tri); + + // If no valid neighbours are found, try to look further + if (neighbors.size() == 0) { + neighbors = getValidAdjacentTriangles(facet, tri); + } + + if (neighbors.size() == 1) { + //Only one neighbor - go there + visitMeshFacetRec(facet, neighbors.get(0), tri, direction); + } else if (neighbors.size() == 2) { + //Two neighbors - they are valid to each other, but one of them has another valid neighbor. + // Select the one that only has the other as a neighbor so the route is linear. + List<MeshTriangle> valid = getValidNeighboringTriangles(facet, neighbors.get(0)); + + if (valid.size() == 1) { + visitMeshFacetRec(facet, neighbors.get(0), tri, direction); + } else { + visitMeshFacetRec(facet, neighbors.get(1), tri, direction); + } + } } @Override public void visitMeshFacet(MeshFacet facet) { - //TODO Dont check every triangle, find first and then check it's neighbors and new neighbors and so on + Logger out = Logger.measureTime(); + + this.visited = new HashSet<>(); + synchronized (this) { - Point3d last = null; + + // Find the first triangle + MeshTriangle first = null; for (MeshTriangle tri : facet) { - if (tri.checkIntersectionWithPlane(plane)) { - Point3d p = tri.getVertex1(); - - // Check for duplicates - if (last != null) { - if (last == p) { - continue; - } - } + if (tri.checkIntersectionWithPlane(plane.getNormal(), plane.getDistance())) { + first = tri; + break; + } + } - points.add(p); - last = p; + if (first == null) { + return; + } + + // Set found and visited and add to points + visited.add(first); + + // Find the valid neighboring triangles and act based on the amount + List<MeshTriangle> neighbors = getValidNeighboringTriangles(facet, first); + + if (neighbors.size() == 1) { + //Only one neighbor - go there + visitMeshFacetRec(facet, neighbors.get(0), first, true); + } else if (neighbors.size() == 2) { + //Two neighbors - one on each side + visitMeshFacetRec(facet, neighbors.get(0), first, true); + visitMeshFacetRec(facet, neighbors.get(1), first, false); + } else if (neighbors.size() == 3) { + //Three neighbors - select the two with only one valid neighbor + List<MeshTriangle> valid = getValidNeighboringTriangles(facet, neighbors.get(0)); + if (valid.size() == 1) { + //Index 0 is one of the correct ones, find the other + visitMeshFacetRec(facet, neighbors.get(0), first, true); + List<MeshTriangle> valid1 = getValidNeighboringTriangles(facet, neighbors.get(1)); + + if (valid1.size() == 1) { + visitMeshFacetRec(facet, neighbors.get(1), first, false); + } else { + visitMeshFacetRec(facet, neighbors.get(2), first, false); + } + } else { + //Index 0 is not one of the correct ones, 1 and 2 are + visitMeshFacetRec(facet, neighbors.get(1), first, true); + visitMeshFacetRec(facet, neighbors.get(2), first, false); } } } + out.printDuration("Cross section with triangle method"); } public List<Point3d> getPoints() { diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSectionZigZag.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSectionZigZag.java new file mode 100644 index 0000000000000000000000000000000000000000..c4f96c24048ba2c0206096676961e0ef5cdd5d2a --- /dev/null +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSectionZigZag.java @@ -0,0 +1,150 @@ +package cz.fidentis.analyst.visitors.mesh; + +import cz.fidentis.analyst.Logger; +import cz.fidentis.analyst.mesh.MeshVisitor; +import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshTriangle; +import cz.fidentis.analyst.symmetry.Plane; + +import javax.vecmath.Point3d; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +/** + * A visitor that calculates the cross-section of a face and a cutting plane using the edges between triangles. + * <p> + * This visitor is thread-safe. + * </p> + * + * @author Dominik Racek + */ +public class CrossSectionZigZag extends MeshVisitor { + private List<Point3d> points; + private Set<Point3d> usedPoints; + private Set<MeshTriangle> visited; + + private Plane plane; + + public CrossSectionZigZag(Plane plane) { + this.plane = plane; + this.points = new ArrayList<>(); + this.usedPoints = new HashSet<>(); + } + + private void addPoint(Point3d p, boolean direction) { + if (!usedPoints.contains(p)) { + usedPoints.add(p); + + if (direction) { + points.add(p); + } else { + points.add(0, p); + } + } + } + + private void visitMeshFacetRec(MeshFacet facet, int p1, int p2, MeshTriangle previous, boolean direction) { + List<MeshTriangle> triangles = facet.getAdjacentTriangles(p1, p2); + triangles.remove(previous); + if (triangles.isEmpty()) { + return; + } + + //Find the next intersected edge + MeshTriangle current = triangles.get(0); + + if (visited.contains(current)) { + return; + } + visited.add(current); + + //Find the index of the last vertex of current (first ones being p1 and p2) + int p3; + if (current.index1 != p1 && current.index1 != p2) { + p3 = current.index1; + } else if (current.index2 != p1 && current.index2 != p2) { + p3 = current.index2; + } else { + p3 = current.index3; + } + + //Next intersected edge will be between p1-p3 or p2-p3 + Point3d intersection = plane.getIntersectionWithLine(facet.getVertex(p1).getPosition(), + facet.getVertex(p3).getPosition()); + + if (intersection == null) { + intersection = plane.getIntersectionWithLine(facet.getVertex(p2).getPosition(), + facet.getVertex(p3).getPosition()); + + addPoint(intersection, direction); + visitMeshFacetRec(facet, p2, p3, current, direction); + } else { + addPoint(intersection, direction); + visitMeshFacetRec(facet, p1, p3, current, direction); + } + + } + + @Override + public void visitMeshFacet(MeshFacet facet) { + Logger out = Logger.measureTime(); + + visited = new HashSet<>(); + + synchronized (this) { + + // Find the first triangle + MeshTriangle first = null; + for (MeshTriangle tri : facet) { + if (tri.checkIntersectionWithPlane(plane.getNormal(), plane.getDistance())) { + first = tri; + break; + } + } + + if (first == null) { + return; + } + + visited.add(first); + + //Figure out which lines are intersected + Point3d intersection1 = plane.getIntersectionWithLine(first.getVertex1(), first.getVertex2()); + Point3d intersection2 = plane.getIntersectionWithLine(first.getVertex2(), first.getVertex3()); + Point3d intersection3 = plane.getIntersectionWithLine(first.getVertex3(), first.getVertex1()); + + if (intersection1 != null) { + //Intersection 1 + visitMeshFacetRec(facet, first.index1, first.index2, first, true); + if (intersection2 != null) { + //Intersection 1 and 2 + visitMeshFacetRec(facet, first.index2, first.index3, first, false); + } else if (intersection3 != null) { + //Intersection 1 and 3 + visitMeshFacetRec(facet, first.index3, first.index1, first, false); + } + } else if (intersection2 != null) { + //Intersection 2 + visitMeshFacetRec(facet, first.index2, first.index3, first, true); + if (intersection3 != null) { + //Intersection 2 and 3 + visitMeshFacetRec(facet, first.index3, first.index1, first, false); + } + + } else if (intersection3 != null) { + //Intersection 3 only + visitMeshFacetRec(facet, first.index3, first.index1, first, true); + } + //No intersection + } + + out.printDuration("Cross section with zigzag method"); + } + + public List<Point3d> getPoints() { + return points; + } +} diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePlane.java b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePlane.java index 795bddc0c7d9bec925588f81aca244f363ade894..dbd1322e021b3e72b531099f60f46ffd3ae0e589 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePlane.java +++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePlane.java @@ -4,6 +4,8 @@ import cz.fidentis.analyst.mesh.core.MeshFacet; import cz.fidentis.analyst.mesh.core.MeshModel; import cz.fidentis.analyst.symmetry.Plane; +import javax.vecmath.Vector3d; + /** * A cutting plane. * @@ -36,6 +38,30 @@ public class DrawablePlane extends DrawableMesh { if (plane == null) { throw new IllegalArgumentException("plane"); } - this.plane = plane; + this.plane = new Plane(plane); + } + + public Plane getPlane() { + return plane; + } + + /** + * Translate the plane along its normal + * + * @param value + */ + public void translatePlane(double value) { + // Move real plane + this.plane.translate(value); + + // Move Drawable plane + Vector3d move = this.plane.getNormal(); + move.x *= value; + move.y *= value; + move.z *= value; + + for (int i = 0; i < 4; ++i) { + getFacets().get(0).getVertex(i).getPosition().sub(move); + } } } diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/PolylinePanel.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/PolylinePanel.java index 2fde656939aa0d5a79537f6cc2aa372d937fb224..8d70c78428f7ab1a5f674aa10c9d7fcde19f6d22 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/PolylinePanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/PolylinePanel.java @@ -7,8 +7,6 @@ import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Stroke; import java.awt.geom.Line2D; -import java.util.Collections; -import java.util.Comparator; import java.util.List; import javax.swing.JPanel; import javax.vecmath.Point3d; @@ -38,33 +36,11 @@ public class PolylinePanel extends JPanel { private double scale = Double.POSITIVE_INFINITY; - /** - * Comparator for Point3d based on Y value - * - * @author Dominik Racek - */ - public class CompareY implements Comparator<Point3d> { - - /** - * Compare two Points3d objects - */ - public int compare(final Point3d a, final Point3d b) { - if (a.y < b.y) { - return -1; - } else if (a.y > b.y) { - return 1; - } else { - return 0; - } - } - } - /** * Constructor for one face */ public PolylinePanel(List<Point3d> values) { this.primaryPoints = values; - Collections.sort(this.primaryPoints, new CompareY()); } /** @@ -73,8 +49,6 @@ public class PolylinePanel extends JPanel { public PolylinePanel(List<Point3d> primaryPoints, List<Point3d> secondaryPoints) { this.primaryPoints = primaryPoints; this.secondaryPoints = secondaryPoints; - Collections.sort(this.primaryPoints, new CompareY()); - Collections.sort(this.secondaryPoints, new CompareY()); } protected void drawFace(Graphics2D g2, List<Point3d> values, Color faceColor, boolean isPrimary) { @@ -168,7 +142,6 @@ public class PolylinePanel extends JPanel { */ public void setPrimaryPoints(List<Point3d> points) { this.primaryPoints = points; - Collections.sort(this.primaryPoints, new CompareY()); repaint(); } @@ -180,7 +153,6 @@ public class PolylinePanel extends JPanel { */ public void setPrimaryMirrorPoints(List<Point3d> points) { this.primaryMirrorPoints = points; - Collections.sort(this.primaryMirrorPoints, new CompareY()); repaint(); } @@ -191,7 +163,6 @@ public class PolylinePanel extends JPanel { */ public void setSecondaryPoints(List<Point3d> points) { this.secondaryPoints = points; - Collections.sort(this.secondaryPoints, new CompareY()); repaint(); } @@ -203,7 +174,6 @@ public class PolylinePanel extends JPanel { */ public void setSecondaryMirrorPoints(List<Point3d> points) { this.secondaryMirrorPoints = points; - Collections.sort(this.secondaryMirrorPoints, new CompareY()); repaint(); } diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java index dec51a69aea0a9a49959c7563accd11b4c05558e..67463fc3ff3f2a238150ba9d2a3061c70d1969a8 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java @@ -3,6 +3,7 @@ package cz.fidentis.analyst.symmetry; import cz.fidentis.analyst.canvas.Canvas; import cz.fidentis.analyst.core.ControlPanelAction; import cz.fidentis.analyst.visitors.mesh.CrossSection; +import cz.fidentis.analyst.visitors.mesh.CrossSectionZigZag; import org.openide.filesystems.FileChooserBuilder; import javax.swing.JTabbedPane; @@ -36,6 +37,8 @@ public class ProfilesAction extends ControlPanelAction { private List<Point3d> primaryMirrorPoints; private List<Point3d> secondaryMirrorPoints; + private double lastSliderValue = 0.5; + /** * Constructor. * @@ -119,24 +122,24 @@ public class ProfilesAction extends ControlPanelAction { private void recomputePrimaryProfile() { //Main profile - CrossSection cs = new CrossSection(getScene().getDrawableCuttingPlane(0).getFacets().get(0)); + CrossSectionZigZag cs = new CrossSectionZigZag(getScene().getDrawableCuttingPlane(0).getPlane()); getPrimaryDrawableFace().getModel().compute(cs); this.primaryPoints = cs.getPoints(); //Mirror profile - CrossSection mcs = new CrossSection(getScene().getDrawableMirrorPlane(0).getFacets().get(0)); + CrossSectionZigZag mcs = new CrossSectionZigZag(getScene().getDrawableMirrorPlane(0).getPlane()); getPrimaryDrawableFace().getModel().compute(mcs); this.primaryMirrorPoints = mcs.getPoints(); } private void recomputeSecondaryProfile() { //Main profile - CrossSection cs = new CrossSection(getScene().getDrawableCuttingPlane(1).getFacets().get(0)); + CrossSectionZigZag cs = new CrossSectionZigZag(getScene().getDrawableCuttingPlane(1).getPlane()); getSecondaryDrawableFace().getModel().compute(cs); this.secondaryPoints = cs.getPoints(); //Mirror profile - CrossSection mcs = new CrossSection(getScene().getDrawableMirrorPlane(1).getFacets().get(0)); + CrossSectionZigZag mcs = new CrossSectionZigZag(getScene().getDrawableMirrorPlane(1).getPlane()); getSecondaryDrawableFace().getModel().compute(mcs); this.secondaryMirrorPoints = mcs.getPoints(); } @@ -154,9 +157,18 @@ public class ProfilesAction extends ControlPanelAction { } private void setCuttingPlaneOffset(int index, double value) { - for (int i = 0; i < 4; ++i) { - getScene().getDrawableCuttingPlane(index).getFacets().get(0).getVertex(i).getPosition().x = value; - getScene().getDrawableMirrorPlane(index).getFacets().get(0).getVertex(i).getPosition().x = value * -1.0; + //Move cutting planes left and mirror planes right + //If normal is negative, need to negate value + if (getScene().getDrawableCuttingPlane(index).getPlane().getNormal().x < 0) { + getScene().getDrawableCuttingPlane(index).translatePlane(-value); + } else { + getScene().getDrawableCuttingPlane(index).translatePlane(value); + } + + if (getScene().getDrawableMirrorPlane(index).getPlane().getNormal().x < 0) { + getScene().getDrawableMirrorPlane(index).translatePlane(value); + } else { + getScene().getDrawableMirrorPlane(index).translatePlane(-value); } } @@ -176,20 +188,28 @@ public class ProfilesAction extends ControlPanelAction { break; case ProfilesPanel.ACTION_COMMAND_EXPORT: exportProfile(this.primaryPoints, "Export primary face profile to file"); + if (controlPanel.isMirrorCutsChecked()) { + exportProfile(this.primaryMirrorPoints, "Export primary face mirror profile to file"); + } + if (getSecondaryDrawableFace() != null) { exportProfile(this.secondaryPoints, "Export secondary face profile to file"); + if (controlPanel.isMirrorCutsChecked()) { + exportProfile(this.secondaryMirrorPoints, "Export secondary face mirror profile to file"); + } } break; case ProfilesPanel.ACTION_OFFSET_CUTTING_PLANE: - double value = controlPanel.getOffsetValue(); - double multiplier = 150; + double value = controlPanel.getOffsetValue() - lastSliderValue; + double multiplier = -150; - setCuttingPlaneOffset(0, multiplier * (value - 0.5)); + setCuttingPlaneOffset(0, multiplier * value); if (getSecondaryDrawableFace() != null) { - setCuttingPlaneOffset(1, multiplier * (value - 0.5)); + setCuttingPlaneOffset(1, multiplier * value); } recomputeProfiles(); + lastSliderValue = controlPanel.getOffsetValue(); break; case ProfilesPanel.ACTION_COMMAND_RECOMPUTE: recomputeProfiles(); diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java index d5adeaa0bea62d3573268e8e1669955c7e908ec6..69be05ea0053088a8405dd656327918c123c104e 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java @@ -117,26 +117,15 @@ public class ProfilesPanel extends ControlPanel { ); builder.addLine(); - builder.addButtons( - List.of("Recompute", - "Export Profile(s)"), - List.of( - (ActionEvent e) -> { - action.actionPerformed(new ActionEvent( - e.getSource(), - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_RECOMPUTE - )); - }, - (ActionEvent e) -> { - action.actionPerformed(new ActionEvent( - e.getSource(), - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_EXPORT - )); - } - ) - ); + builder.addButton( + "Export Profile(s)", + (ActionEvent e) -> { + action.actionPerformed(new ActionEvent( + e.getSource(), + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_EXPORT + )); + }); } @Override diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshFacet.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshFacet.java index e002bd80fdc3e78ddeaef3f3a3889ba0d38f9cce..c9f81ed95121b9b5467257d6c989d758646689b8 100644 --- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshFacet.java +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshFacet.java @@ -84,6 +84,33 @@ public interface MeshFacet extends Iterable<MeshTriangle>, Serializable { * @return Triangles sharing the mesh vertex */ List<MeshTriangle> getAdjacentTriangles(int vertexIndex); + + /** + * Returns triangles sharing the given edge. + * + * @param index1 Index of the first point of the edge + * @param index2 Index of the second point of the edge + * @return Triangles sharing the edge + */ + List<MeshTriangle> getAdjacentTriangles(int index1, int index2); + + /** + * Returns adjacent triangles, i.e., triangles sharing an edge or a vertex with the given triangle + * + * @param triangle The triangle + * @return Unique adjacent triangles + */ + List<MeshTriangle> getAdjacentTriangles(MeshTriangle triangle); + + /** + * Returns neighboring triangles, i.e., triangles sharing an edge with the given triangle. + * In contrast to the {@link #getAdjacentTriangles(cz.fidentis.analyst.mesh.core.MeshTriangle)}, + * triangles sharing only a vertex are omitted. + * + * @param triangle The triangle + * @return Unique neighboring triangles + */ + List<MeshTriangle> getNeighboringTriangles(MeshTriangle triangle); /** * Finds and returns a point lying at triangles around (sharing) the given mesh vertex diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshFacetImpl.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshFacetImpl.java index a12a2f78f338ff0430bdc1598e63f49180103bb8..f1b9f0c5154dc4088384558d95b782ad3fd19ba3 100644 --- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshFacetImpl.java +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshFacetImpl.java @@ -3,10 +3,12 @@ package cz.fidentis.analyst.mesh.core; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; import javax.vecmath.Vector3d; import cz.fidentis.analyst.mesh.MeshVisitor; import javax.vecmath.Point3d; @@ -15,6 +17,7 @@ import javax.vecmath.Point3d; * Mash facet is a compact triangular mesh without duplicated vertices. * * @author Matej Lukes + * @author Dominik Racek */ public class MeshFacetImpl implements MeshFacet { @@ -149,6 +152,52 @@ public class MeshFacetImpl implements MeshFacet { return ret; } + + @Override + public List<MeshTriangle> getAdjacentTriangles(int index1, int index2) { + List<MeshTriangle> output = getAdjacentTriangles(index1); + List<MeshTriangle> other = getAdjacentTriangles(index2); + output.retainAll(other); + return output; + } + + @Override + public List<MeshTriangle> getAdjacentTriangles(MeshTriangle tri) { + Set<MeshTriangle> output = new HashSet<>(); + output.addAll(getAdjacentTriangles(tri.index1)); + output.addAll(getAdjacentTriangles(tri.index2)); + output.addAll(getAdjacentTriangles(tri.index3)); + output.remove(tri); + + return new ArrayList<>(output); + } + + @Override + public List<MeshTriangle> getNeighboringTriangles(MeshTriangle tri) { + Set<MeshTriangle> output = new HashSet<>(); + + //Find only the three triangles sharing edges with tri + List<MeshTriangle> l1 = getAdjacentTriangles(tri.index1); + List<MeshTriangle> l2 = getAdjacentTriangles(tri.index2); + List<MeshTriangle> l3 = getAdjacentTriangles(tri.index3); + + //Calculate the three intersections + List<MeshTriangle> i1 = new ArrayList<>(l1); + i1.retainAll(l2); + + List<MeshTriangle> i2 = new ArrayList<>(l2); + i2.retainAll(l3); + + List<MeshTriangle> i3 = new ArrayList<>(l3); + i3.retainAll(l1); + + output.addAll(i1); + output.addAll(i2); + output.addAll(i3); + output.remove(tri); + + return new ArrayList<>(output); + } @Override public Point3d getClosestAdjacentPoint(Point3d point, int vertexIndex) { diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshTriangle.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshTriangle.java index 31ef4cde576946a4072c53a335c19bb397417697..86badda1f40bd9053aa38aeeab81dfab205acf30 100644 --- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshTriangle.java +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshTriangle.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; +import java.util.Objects; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; @@ -11,6 +12,7 @@ import javax.vecmath.Vector3d; * Adapter for the corner table representing a single triangle of {@code MeshFacet}. * * @author Natalia Bebjakova + * @author Dominik Racek */ public class MeshTriangle implements Iterable<MeshPoint> { @@ -69,6 +71,28 @@ public class MeshTriangle implements Iterable<MeshPoint> { public MeshPoint getPoint3() { return facet.getVertex(index3); } + + @Override + public boolean equals(Object obj) + { + if(this == obj) + return true; + + if(obj == null || obj.getClass()!= this.getClass()) + return false; + + MeshTriangle mt = (MeshTriangle) obj; + + return ((index1 == mt.index1 || index1 == mt.index2 || index1 == mt.index3) + && (index2 == mt.index1 || index2 == mt.index2 || index2 == mt.index3) + && (index3 == mt.index1 || index3 == mt.index2 || index3 == mt.index3)); + } + + @Override + public int hashCode() { + return Objects.hash(index1, index2, index3); + } + /** * Computes and returns normalized normal vector from the vertices. @@ -364,7 +388,7 @@ public class MeshTriangle implements Iterable<MeshPoint> { ab.sub(v1); Vector3d ap = new Vector3d(point); ap.sub(v1); - + double abLenSquared = ab.lengthSquared(); double t = ab.dot(ap) / abLenSquared; @@ -401,39 +425,43 @@ public class MeshTriangle implements Iterable<MeshPoint> { /** * Checks whether the triangle is intersected by a cutting plane * - * @param plane cutting plane + * @param normal normal defining the plane + * @param d distance defining the plane * @return true if the triangle is intersected by the plane */ - public boolean checkIntersectionWithPlane(MeshFacet plane) { - Point3d min = getVertex1(), max = getVertex1(); - - if (getVertex2().x < min.x) { - min = getVertex2(); - } + public boolean checkIntersectionWithPlane(Vector3d normal, double d) { + double p1 = (normal.x * getVertex1().x) + (normal.y * getVertex1().y) + (normal.z * getVertex1().z) + d; + double p2 = (normal.x * getVertex2().x) + (normal.y * getVertex2().y) + (normal.z * getVertex2().z) + d; + double p3 = (normal.x * getVertex3().x) + (normal.y * getVertex3().y) + (normal.z * getVertex3().z) + d; - if (getVertex3().x < min.x) { - min = getVertex3(); - } + //If one point is on one side of the plane and the other on the other side + return ((p1 <= 0 || p2 <= 0 || p3 <= 0) && (p1 >= 0 || p2 >= 0 || p3 >= 0)); + } - if (getVertex2().x > max.x) { - max = getVertex2(); - } + /** + * Selects the common points between two triangles + * + * @param other The other triangle + * @return the common points of two triangles + */ + public List<Point3d> getCommonPoints(MeshTriangle other) { + List<Point3d> output = new ArrayList<>(); - if (getVertex3().x > max.x) { - max = getVertex3(); + if (getVertex1().equals(other.getVertex1()) || getVertex1().equals(other.getVertex2()) + || getVertex1().equals(other.getVertex3())) { + output.add(getVertex1()); } - Point3d projMin = plane.getTriangles().get(0).getProjectionToTrianglePlane(min); - Point3d projMax = plane.getTriangles().get(0).getProjectionToTrianglePlane(max); - - if (projMin == null) { - projMin = plane.getTriangles().get(1).getProjectionToTrianglePlane(min); + if (getVertex2().equals(other.getVertex1()) || getVertex2().equals(other.getVertex2()) + || getVertex2().equals(other.getVertex3())) { + output.add(getVertex2()); } - if (projMax == null) { - projMax = plane.getTriangles().get(1).getProjectionToTrianglePlane(max); + if (getVertex3().equals(other.getVertex1()) || getVertex3().equals(other.getVertex2()) + || getVertex3().equals(other.getVertex3())) { + output.add(getVertex3()); } - return (min.x < projMin.x && max.x > projMax.x); + return output; } }