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 31359cc2fd5d659c0b120a5c0dc0d1bef7f6d37a..91e6062f40f48a0cedd76eb59080c4d4231f40a7 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,167 +1,162 @@ 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.CrossSectionCurve; 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; +import java.util.stream.Collectors; + /** - * A visitor that calculates the cross-section of a face and a cutting plane using the triangles. + * 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 + * @author Radek Oslejsek */ public class CrossSection extends MeshVisitor { - private List<Point3d> points; - private Set<Point3d> usedPoints; - private Plane plane; - + private final CrossSectionCurve curve; + private final Set<Point3d> usedPoints; private Set<MeshTriangle> visited; + private Set<MeshTriangle> toVisit; + + private final Plane plane; /** - * Constructor for CrossSection visitor + * Constructor for CrossSectionZigZag visitor * * @param plane cutting plane */ public CrossSection(Plane plane) { this.plane = plane; - this.points = new ArrayList<>(); + this.curve = new CrossSectionCurve(); 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; - } + private void addPoint(Point3d p, boolean direction, int segment) { + if (!usedPoints.contains(p)) { + usedPoints.add(p); - if (!usedPoints.contains(p)) { - usedPoints.add(p); - - if (direction) { - points.add(p); - } else { - points.add(0, p); - } + if (direction) { + curve.addPointToSegmentEnd(segment, p); + } else { + curve.addPointToSegmentStart(segment, 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); + private void visitMeshFacetRec(MeshFacet facet, int p1, int p2, MeshTriangle previous, boolean direction, int segment) { + List<MeshTriangle> triangles = facet.getAdjacentTriangles(p1, p2); + triangles.remove(previous); + if (triangles.isEmpty()) { + return; + } - // Recursively call to the end - // Get all neighbors that have not been visited and are intercepted - List<MeshTriangle> neighbors = getValidNeighboringTriangles(facet, tri); + //Find the next intersected edge + MeshTriangle current = triangles.get(0); + toVisit.remove(current); - // If no valid neighbours are found, try to look further - if (neighbors.size() == 0) { - neighbors = getValidAdjacentTriangles(facet, tri); + 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; } - 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)); + //Next intersected edge will be between p1-p3 or p2-p3 + Point3d intersection = plane.getIntersectionWithLine(facet.getVertex(p1).getPosition(), + facet.getVertex(p3).getPosition()); - if (valid.size() == 1) { - visitMeshFacetRec(facet, neighbors.get(0), tri, direction); - } else { - visitMeshFacetRec(facet, neighbors.get(1), tri, direction); - } + if (intersection == null) { + intersection = plane.getIntersectionWithLine(facet.getVertex(p2).getPosition(), + facet.getVertex(p3).getPosition()); + + addPoint(intersection, direction, segment); + visitMeshFacetRec(facet, p2, p3, current, direction, segment); + } else { + addPoint(intersection, direction, segment); + visitMeshFacetRec(facet, p1, p3, current, direction, segment); } + } @Override public void visitMeshFacet(MeshFacet facet) { - Logger out = Logger.measureTime(); - - this.visited = new HashSet<>(); - + //Logger out = Logger.measureTime(); + + // Find all candidates + visited = new HashSet<>(); + toVisit = facet.getTriangles().parallelStream() + .filter(t -> t.checkIntersectionWithPlane(plane.getNormal(), plane.getDistance())) + .collect(Collectors.toSet()); + 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; - } - - // 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); + while (!toVisit.isEmpty()) { + MeshTriangle tri = toVisit.iterator().next(); + toVisit.remove(tri); + int segment = curve.addNewSegment(); + + // Figure out which lines are intersected + Point3d intersection1 = plane.getIntersectionWithLine(tri.getVertex1(), tri.getVertex2()); + Point3d intersection2 = plane.getIntersectionWithLine(tri.getVertex2(), tri.getVertex3()); + Point3d intersection3 = plane.getIntersectionWithLine(tri.getVertex3(), tri.getVertex1()); + + if (intersection1 != null) { + //Intersection 1 + addPoint(intersection1, true, segment); + visitMeshFacetRec(facet, tri.index1, tri.index2, tri, true, segment); + if (intersection2 != null) { + //Intersection 1 and 2 + addPoint(intersection2, false, segment); + visitMeshFacetRec(facet, tri.index2, tri.index3, tri, false, segment); + } else if (intersection3 != null) { + //Intersection 1 and 3 + addPoint(intersection3, false, segment); + visitMeshFacetRec(facet, tri.index3, tri.index1, tri, false, segment); + } + } else if (intersection2 != null) { + //Intersection 2 + addPoint(intersection2, true, segment); + visitMeshFacetRec(facet, tri.index2, tri.index3, tri, true, segment); + if (intersection3 != null) { + //Intersection 2 and 3 + addPoint(intersection3, false, segment); + visitMeshFacetRec(facet, tri.index3, tri.index1, tri, false, segment); } - } 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); + + } else if (intersection3 != null) { + //Intersection 3 only + addPoint(intersection3, true, segment); + visitMeshFacetRec(facet, tri.index3, tri.index1, tri, true, segment); } + //No intersection } } - out.printDuration("Cross section with triangle method"); + //out.printDuration("Cross section with zigzag method"); } - public List<Point3d> getPoints() { - return points; + /** + * Returns computed cross section curve. + * @return cross section curve + */ + public CrossSectionCurve getCrossSectionCurve() { + return curve; } } 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 deleted file mode 100644 index befe27888118ed1a6b897713c5d1fc7c1214dc3e..0000000000000000000000000000000000000000 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSectionZigZag.java +++ /dev/null @@ -1,162 +0,0 @@ -package cz.fidentis.analyst.visitors.mesh; - -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.CrossSectionCurve; -import cz.fidentis.analyst.symmetry.Plane; - -import javax.vecmath.Point3d; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - - -/** - * 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 - * @author Radek Oslejsek - */ -public class CrossSectionZigZag extends MeshVisitor { - private final CrossSectionCurve curve; - private final Set<Point3d> usedPoints; - private Set<MeshTriangle> visited; - private Set<MeshTriangle> toVisit; - - private final Plane plane; - - /** - * Constructor for CrossSectionZigZag visitor - * - * @param plane cutting plane - */ - public CrossSectionZigZag(Plane plane) { - this.plane = plane; - this.curve = new CrossSectionCurve(); - this.usedPoints = new HashSet<>(); - } - - private void addPoint(Point3d p, boolean direction, int segment) { - if (!usedPoints.contains(p)) { - usedPoints.add(p); - - if (direction) { - curve.addPointToSegmentEnd(segment, p); - } else { - curve.addPointToSegmentStart(segment, p); - } - } - } - - private void visitMeshFacetRec(MeshFacet facet, int p1, int p2, MeshTriangle previous, boolean direction, int segment) { - List<MeshTriangle> triangles = facet.getAdjacentTriangles(p1, p2); - triangles.remove(previous); - if (triangles.isEmpty()) { - return; - } - - //Find the next intersected edge - MeshTriangle current = triangles.get(0); - toVisit.remove(current); - - 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, segment); - visitMeshFacetRec(facet, p2, p3, current, direction, segment); - } else { - addPoint(intersection, direction, segment); - visitMeshFacetRec(facet, p1, p3, current, direction, segment); - } - - } - - @Override - public void visitMeshFacet(MeshFacet facet) { - //Logger out = Logger.measureTime(); - - // Find all candidates - visited = new HashSet<>(); - toVisit = facet.getTriangles().parallelStream() - .filter(t -> t.checkIntersectionWithPlane(plane.getNormal(), plane.getDistance())) - .collect(Collectors.toSet()); - - synchronized (this) { - while (!toVisit.isEmpty()) { - MeshTriangle tri = toVisit.iterator().next(); - toVisit.remove(tri); - int segment = curve.addNewSegment(); - - // Figure out which lines are intersected - Point3d intersection1 = plane.getIntersectionWithLine(tri.getVertex1(), tri.getVertex2()); - Point3d intersection2 = plane.getIntersectionWithLine(tri.getVertex2(), tri.getVertex3()); - Point3d intersection3 = plane.getIntersectionWithLine(tri.getVertex3(), tri.getVertex1()); - - if (intersection1 != null) { - //Intersection 1 - addPoint(intersection1, true, segment); - visitMeshFacetRec(facet, tri.index1, tri.index2, tri, true, segment); - if (intersection2 != null) { - //Intersection 1 and 2 - addPoint(intersection2, false, segment); - visitMeshFacetRec(facet, tri.index2, tri.index3, tri, false, segment); - } else if (intersection3 != null) { - //Intersection 1 and 3 - addPoint(intersection3, false, segment); - visitMeshFacetRec(facet, tri.index3, tri.index1, tri, false, segment); - } - } else if (intersection2 != null) { - //Intersection 2 - addPoint(intersection2, true, segment); - visitMeshFacetRec(facet, tri.index2, tri.index3, tri, true, segment); - if (intersection3 != null) { - //Intersection 2 and 3 - addPoint(intersection3, false, segment); - visitMeshFacetRec(facet, tri.index3, tri.index1, tri, false, segment); - } - - } else if (intersection3 != null) { - //Intersection 3 only - addPoint(intersection3, true, segment); - visitMeshFacetRec(facet, tri.index3, tri.index1, tri, true, segment); - } - //No intersection - } - } - - //out.printDuration("Cross section with zigzag method"); - } - - /** - * Returns computed cross section curve. - * @return cross section curve - */ - public CrossSectionCurve getCrossSectionCurve() { - return curve; - } -} diff --git a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/CrossSectionTest.java b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/CrossSectionTest.java index 29d32ff33feaf376aa7bec416c95fdc14e2a2915..53851b2baeed3047e4e6b461283768d62bb1fad9 100644 --- a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/CrossSectionTest.java +++ b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/CrossSectionTest.java @@ -58,7 +58,7 @@ public class CrossSectionTest { Plane cuttingPlane = new Plane(new Vector3d(-1, 0, 0), -0.5); MeshModel model = createModel(); - CrossSectionZigZag cs = new CrossSectionZigZag(cuttingPlane); + CrossSection cs = new CrossSection(cuttingPlane); model.compute(cs); CrossSectionCurve curve = cs.getCrossSectionCurve(); @@ -83,35 +83,4 @@ public class CrossSectionTest { } } - /** - * Unit test for CrossSection visitor - */ - @Test - public void CrossSection() { - Plane cuttingPlane = new Plane(new Vector3d(-1, 0, 0), -0.5); - MeshModel model = createModel(); - - CrossSection cs = new CrossSection(cuttingPlane); - model.compute(cs); - List<Point3d> points = cs.getPoints(); - - //They can be ordered two ways, check both - //Flaw of the non-zigzag design: it can't detect first and last intersection, - //since it only controls edges between two triangles. - Assertions.assertEquals(4, points.size()); - if (points.get(0).equals(new Point3d(0.5, 0.5, 0))) { - Assertions.assertEquals(points.get(0), new Point3d(0.5, 0.5, 0)); - Assertions.assertEquals(points.get(1), new Point3d(0.5, 1, 0)); - Assertions.assertEquals(points.get(2), new Point3d(0.5, 1.5, 0)); - Assertions.assertEquals(points.get(3), new Point3d(0.5, 2, 0)); - } else if (points.get(0).equals(new Point3d(0.5, 2, 0))) { - Assertions.assertEquals(points.get(1), new Point3d(0.5, 2, 0)); - Assertions.assertEquals(points.get(2), new Point3d(0.5, 1.5, 0)); - Assertions.assertEquals(points.get(3), new Point3d(0.5, 1, 0)); - Assertions.assertEquals(points.get(4), new Point3d(0.5, 0.5, 0)); - } else { - Assertions.fail(); - } - } - } 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 50c730c0d054d15f4021583b5dffab7ebf1a8d68..e79f9fd1eebc048ff394f0252f1f6390c3a7268c 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java @@ -9,7 +9,7 @@ import cz.fidentis.analyst.face.events.HumanFaceListener; import cz.fidentis.analyst.face.events.HumanFaceTransformedEvent; import cz.fidentis.analyst.scene.DrawableCuttingPlane; import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox; -import cz.fidentis.analyst.visitors.mesh.CrossSectionZigZag; +import cz.fidentis.analyst.visitors.mesh.CrossSection; import org.openide.filesystems.FileChooserBuilder; import javax.swing.JTabbedPane; @@ -221,24 +221,24 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe private void recomputePrimaryProfile() { //Main profile - CrossSectionZigZag cs = new CrossSectionZigZag(getDrawableCuttingPlane(priCuttingPlaneSlot).getPlane()); + CrossSection cs = new CrossSection(getDrawableCuttingPlane(priCuttingPlaneSlot).getPlane()); getPrimaryDrawableFace().getModel().compute(cs); this.primaryCurve = cs.getCrossSectionCurve(); //Mirror profile - CrossSectionZigZag mcs = new CrossSectionZigZag(getDrawableCuttingPlane(priCuttingPlaneSlot).getMirrorPlane()); + CrossSection mcs = new CrossSection(getDrawableCuttingPlane(priCuttingPlaneSlot).getMirrorPlane()); getPrimaryDrawableFace().getModel().compute(mcs); this.primaryMirrorCurve = mcs.getCrossSectionCurve(); } private void recomputeSecondaryProfile() { //Main profile - CrossSectionZigZag cs = new CrossSectionZigZag(getDrawableCuttingPlane(secCuttingPlaneSlot).getPlane()); + CrossSection cs = new CrossSection(getDrawableCuttingPlane(secCuttingPlaneSlot).getPlane()); getSecondaryDrawableFace().getModel().compute(cs); this.secondaryCurve = cs.getCrossSectionCurve(); //Mirror profile - CrossSectionZigZag mcs = new CrossSectionZigZag(getDrawableCuttingPlane(secCuttingPlaneSlot).getMirrorPlane()); + CrossSection mcs = new CrossSection(getDrawableCuttingPlane(secCuttingPlaneSlot).getMirrorPlane()); getSecondaryDrawableFace().getModel().compute(mcs); this.secondaryMirrorCurve = mcs.getCrossSectionCurve(); }