Skip to content
Snippets Groups Projects
Commit 0d5d4478 authored by Radek Ošlejšek's avatar Radek Ošlejšek
Browse files

Merge branch '131-remove-crosssection-remain-crosssectionzigzag-only' into 'master'

Resolve "Remove CrossSection, remain CrossSectionZigZag only"

Closes #131

See merge request grp-fidentis/analyst2!167
parents d9f2884a 44f75a2a
No related branches found
No related tags found
No related merge requests found
package cz.fidentis.analyst.visitors.mesh; package cz.fidentis.analyst.visitors.mesh;
import cz.fidentis.analyst.Logger;
import cz.fidentis.analyst.mesh.MeshVisitor; import cz.fidentis.analyst.mesh.MeshVisitor;
import cz.fidentis.analyst.mesh.core.MeshFacet; import cz.fidentis.analyst.mesh.core.MeshFacet;
import cz.fidentis.analyst.mesh.core.MeshTriangle; import cz.fidentis.analyst.mesh.core.MeshTriangle;
import cz.fidentis.analyst.symmetry.CrossSectionCurve;
import cz.fidentis.analyst.symmetry.Plane; import cz.fidentis.analyst.symmetry.Plane;
import javax.vecmath.Point3d; import javax.vecmath.Point3d;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; 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> * <p>
* This visitor is thread-safe. * This visitor is thread-safe.
* </p> * </p>
* *
* @author Dominik Racek * @author Dominik Racek
* @author Radek Oslejsek
*/ */
public class CrossSection extends MeshVisitor { public class CrossSection extends MeshVisitor {
private List<Point3d> points; private final CrossSectionCurve curve;
private Set<Point3d> usedPoints; private final Set<Point3d> usedPoints;
private Plane plane;
private Set<MeshTriangle> visited; private Set<MeshTriangle> visited;
private Set<MeshTriangle> toVisit;
private final Plane plane;
/** /**
* Constructor for CrossSection visitor * Constructor for CrossSectionZigZag visitor
* *
* @param plane cutting plane * @param plane cutting plane
*/ */
public CrossSection(Plane plane) { public CrossSection(Plane plane) {
this.plane = plane; this.plane = plane;
this.points = new ArrayList<>(); this.curve = new CrossSectionCurve();
this.usedPoints = new HashSet<>(); this.usedPoints = new HashSet<>();
} }
private List<MeshTriangle> getValidNeighboringTriangles(MeshFacet facet, MeshTriangle tri) { private void addPoint(Point3d p, boolean direction, int segment) {
List<MeshTriangle> output = facet.getNeighboringTriangles(tri); if (!usedPoints.contains(p)) {
output.removeIf(n -> ((visited.contains(n)) usedPoints.add(p);
|| (!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)) { if (direction) {
usedPoints.add(p); curve.addPointToSegmentEnd(segment, p);
} else {
if (direction) { curve.addPointToSegmentStart(segment, p);
points.add(p);
} else {
points.add(0, p);
}
} }
} }
} }
private void visitMeshFacetRec(MeshFacet facet, MeshTriangle tri, MeshTriangle previous, boolean direction) { private void visitMeshFacetRec(MeshFacet facet, int p1, int p2, MeshTriangle previous, boolean direction, int segment) {
visited.add(tri); List<MeshTriangle> triangles = facet.getAdjacentTriangles(p1, p2);
triangles.remove(previous);
// Depending on the direction, add the point to the start or to the end of points if (triangles.isEmpty()) {
addPoint(tri, previous, direction); return;
}
// Recursively call to the end //Find the next intersected edge
// Get all neighbors that have not been visited and are intercepted MeshTriangle current = triangles.get(0);
List<MeshTriangle> neighbors = getValidNeighboringTriangles(facet, tri); toVisit.remove(current);
// If no valid neighbours are found, try to look further if (visited.contains(current)) {
if (neighbors.size() == 0) { return;
neighbors = getValidAdjacentTriangles(facet, tri); }
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) { //Next intersected edge will be between p1-p3 or p2-p3
//Only one neighbor - go there Point3d intersection = plane.getIntersectionWithLine(facet.getVertex(p1).getPosition(),
visitMeshFacetRec(facet, neighbors.get(0), tri, direction); facet.getVertex(p3).getPosition());
} 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) { if (intersection == null) {
visitMeshFacetRec(facet, neighbors.get(0), tri, direction); intersection = plane.getIntersectionWithLine(facet.getVertex(p2).getPosition(),
} else { facet.getVertex(p3).getPosition());
visitMeshFacetRec(facet, neighbors.get(1), tri, direction);
} addPoint(intersection, direction, segment);
visitMeshFacetRec(facet, p2, p3, current, direction, segment);
} else {
addPoint(intersection, direction, segment);
visitMeshFacetRec(facet, p1, p3, current, direction, segment);
} }
} }
@Override @Override
public void visitMeshFacet(MeshFacet facet) { public void visitMeshFacet(MeshFacet facet) {
Logger out = Logger.measureTime(); //Logger out = Logger.measureTime();
this.visited = new HashSet<>(); // Find all candidates
visited = new HashSet<>();
toVisit = facet.getTriangles().parallelStream()
.filter(t -> t.checkIntersectionWithPlane(plane.getNormal(), plane.getDistance()))
.collect(Collectors.toSet());
synchronized (this) { synchronized (this) {
while (!toVisit.isEmpty()) {
// Find the first triangle MeshTriangle tri = toVisit.iterator().next();
MeshTriangle first = null; toVisit.remove(tri);
for (MeshTriangle tri : facet) { int segment = curve.addNewSegment();
if (tri.checkIntersectionWithPlane(plane.getNormal(), plane.getDistance())) {
first = tri; // Figure out which lines are intersected
break; 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 (first == null) { if (intersection1 != null) {
return; //Intersection 1
} addPoint(intersection1, true, segment);
visitMeshFacetRec(facet, tri.index1, tri.index2, tri, true, segment);
// Set found and visited and add to points if (intersection2 != null) {
visited.add(first); //Intersection 1 and 2
addPoint(intersection2, false, segment);
// Find the valid neighboring triangles and act based on the amount visitMeshFacetRec(facet, tri.index2, tri.index3, tri, false, segment);
List<MeshTriangle> neighbors = getValidNeighboringTriangles(facet, first); } else if (intersection3 != null) {
//Intersection 1 and 3
if (neighbors.size() == 1) { addPoint(intersection3, false, segment);
//Only one neighbor - go there visitMeshFacetRec(facet, tri.index3, tri.index1, tri, false, segment);
visitMeshFacetRec(facet, neighbors.get(0), first, true); }
} else if (neighbors.size() == 2) { } else if (intersection2 != null) {
//Two neighbors - one on each side //Intersection 2
visitMeshFacetRec(facet, neighbors.get(0), first, true); addPoint(intersection2, true, segment);
visitMeshFacetRec(facet, neighbors.get(1), first, false); visitMeshFacetRec(facet, tri.index2, tri.index3, tri, true, segment);
} else if (neighbors.size() == 3) { if (intersection3 != null) {
//Three neighbors - select the two with only one valid neighbor //Intersection 2 and 3
List<MeshTriangle> valid = getValidNeighboringTriangles(facet, neighbors.get(0)); addPoint(intersection3, false, segment);
if (valid.size() == 1) { visitMeshFacetRec(facet, tri.index3, tri.index1, tri, false, segment);
//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 } else if (intersection3 != null) {
visitMeshFacetRec(facet, neighbors.get(1), first, true); //Intersection 3 only
visitMeshFacetRec(facet, neighbors.get(2), first, false); 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;
} }
} }
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;
}
}
...@@ -58,7 +58,7 @@ public class CrossSectionTest { ...@@ -58,7 +58,7 @@ public class CrossSectionTest {
Plane cuttingPlane = new Plane(new Vector3d(-1, 0, 0), -0.5); Plane cuttingPlane = new Plane(new Vector3d(-1, 0, 0), -0.5);
MeshModel model = createModel(); MeshModel model = createModel();
CrossSectionZigZag cs = new CrossSectionZigZag(cuttingPlane); CrossSection cs = new CrossSection(cuttingPlane);
model.compute(cs); model.compute(cs);
CrossSectionCurve curve = cs.getCrossSectionCurve(); CrossSectionCurve curve = cs.getCrossSectionCurve();
...@@ -83,35 +83,4 @@ public class CrossSectionTest { ...@@ -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();
}
}
} }
...@@ -9,7 +9,7 @@ import cz.fidentis.analyst.face.events.HumanFaceListener; ...@@ -9,7 +9,7 @@ import cz.fidentis.analyst.face.events.HumanFaceListener;
import cz.fidentis.analyst.face.events.HumanFaceTransformedEvent; import cz.fidentis.analyst.face.events.HumanFaceTransformedEvent;
import cz.fidentis.analyst.scene.DrawableCuttingPlane; import cz.fidentis.analyst.scene.DrawableCuttingPlane;
import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox; 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 org.openide.filesystems.FileChooserBuilder;
import javax.swing.JTabbedPane; import javax.swing.JTabbedPane;
...@@ -221,24 +221,24 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe ...@@ -221,24 +221,24 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
private void recomputePrimaryProfile() { private void recomputePrimaryProfile() {
//Main profile //Main profile
CrossSectionZigZag cs = new CrossSectionZigZag(getDrawableCuttingPlane(priCuttingPlaneSlot).getPlane()); CrossSection cs = new CrossSection(getDrawableCuttingPlane(priCuttingPlaneSlot).getPlane());
getPrimaryDrawableFace().getModel().compute(cs); getPrimaryDrawableFace().getModel().compute(cs);
this.primaryCurve = cs.getCrossSectionCurve(); this.primaryCurve = cs.getCrossSectionCurve();
//Mirror profile //Mirror profile
CrossSectionZigZag mcs = new CrossSectionZigZag(getDrawableCuttingPlane(priCuttingPlaneSlot).getMirrorPlane()); CrossSection mcs = new CrossSection(getDrawableCuttingPlane(priCuttingPlaneSlot).getMirrorPlane());
getPrimaryDrawableFace().getModel().compute(mcs); getPrimaryDrawableFace().getModel().compute(mcs);
this.primaryMirrorCurve = mcs.getCrossSectionCurve(); this.primaryMirrorCurve = mcs.getCrossSectionCurve();
} }
private void recomputeSecondaryProfile() { private void recomputeSecondaryProfile() {
//Main profile //Main profile
CrossSectionZigZag cs = new CrossSectionZigZag(getDrawableCuttingPlane(secCuttingPlaneSlot).getPlane()); CrossSection cs = new CrossSection(getDrawableCuttingPlane(secCuttingPlaneSlot).getPlane());
getSecondaryDrawableFace().getModel().compute(cs); getSecondaryDrawableFace().getModel().compute(cs);
this.secondaryCurve = cs.getCrossSectionCurve(); this.secondaryCurve = cs.getCrossSectionCurve();
//Mirror profile //Mirror profile
CrossSectionZigZag mcs = new CrossSectionZigZag(getDrawableCuttingPlane(secCuttingPlaneSlot).getMirrorPlane()); CrossSection mcs = new CrossSection(getDrawableCuttingPlane(secCuttingPlaneSlot).getMirrorPlane());
getSecondaryDrawableFace().getModel().compute(mcs); getSecondaryDrawableFace().getModel().compute(mcs);
this.secondaryMirrorCurve = mcs.getCrossSectionCurve(); this.secondaryMirrorCurve = mcs.getCrossSectionCurve();
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment