diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/Curvature.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/Curvature.java index d83e846fd16ac8dce56bf6df4a8b4f840f4b9165..536a78e0d06796c0e78f1b6c8c974cb2b8fb64d2 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/Curvature.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/Curvature.java @@ -1,14 +1,19 @@ package cz.fidentis.analyst.visitors.mesh; +import cz.fidentis.analyst.kdtree.KdTreeVisitor; import cz.fidentis.analyst.mesh.MeshVisitor; import cz.fidentis.analyst.mesh.core.MeshFacet; -import cz.fidentis.analyst.mesh.core.MeshPoint; import cz.fidentis.analyst.mesh.core.MeshTriangle; +import cz.fidentis.analyst.mesh.core.TriangleFan; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import javax.vecmath.Vector3d; /** @@ -29,6 +34,25 @@ public class Curvature extends MeshVisitor { private final Map<MeshFacet, List<Double>> minPrincipal = new HashMap<>(); private final Map<MeshFacet, List<Double>> maxPrincipal = new HashMap<>(); + private final boolean parallel; + + /** + * Constructor. + * + * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores + */ + public Curvature(boolean parallel) { + this.parallel = parallel; + } + + /** + * Constructor for parallel computation. + * + */ + public Curvature() { + this(true); + } + /** * Returns Gaussian curvatures for all inspected mesh facets. The order corresponds to * the order of vertices, i.e., i-th value represents the curvature of i-th mesh vertex. @@ -60,7 +84,7 @@ public class Curvature extends MeshVisitor { } /** - * Returns maximu principal curvatures for all inspected mesh facets. The order corresponds to + * Returns maximum principal curvatures for all inspected mesh facets. The order corresponds to * the order of vertices, i.e., i-th value represents the curvature of i-th mesh vertex. * * @return Maximum principal curvatures of inspected mesh facets. @@ -75,366 +99,274 @@ public class Curvature extends MeshVisitor { if (gaussian.containsKey(facet)) { return; // already visited facet } - gaussian.put(facet, new ArrayList<>()); - mean.put(facet, new ArrayList<>()); - minPrincipal.put(facet, new ArrayList<>()); - maxPrincipal.put(facet, new ArrayList<>()); + // prefill the arrays - required for concurrent computation. + int numVert = facet.getNumberOfVertices(); + gaussian.put(facet, new ArrayList<Double>(Collections.nCopies(numVert, Double.NaN))); + mean.put(facet, new ArrayList<Double>(Collections.nCopies(numVert, Double.NaN))); + minPrincipal.put(facet, new ArrayList<Double>(Collections.nCopies(numVert, Double.NaN))); + maxPrincipal.put(facet, new ArrayList<Double>(Collections.nCopies(numVert, Double.NaN))); } - //final List<MeshTriangle> triangles = facet.getTriangles(); - final List<CurvTriangle> triangles = precomputeTriangles(facet); - for (int vertA = 0; vertA < facet.getNumberOfVertices(); vertA++) { - List<Integer> neighbouringTriangles = facet.getCornerTable().getTriangleIndexesByVertexIndex(vertA); - - if (facet.vertexIsBoundary(vertA)) { - this.gaussian.get(facet).add(0.0); - this.mean.get(facet).add(0.0); - this.minPrincipal.get(facet).add(0.0); - this.maxPrincipal.get(facet).add(0.0); - continue; - } + final Cache cache = new Cache(facet); - if (neighbouringTriangles.isEmpty()) { - this.gaussian.get(facet).add(Double.NaN); - this.mean.get(facet).add(Double.NaN); - this.minPrincipal.get(facet).add(Double.NaN); - this.maxPrincipal.get(facet).add(Double.NaN); - continue; + if (parallel) { + ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + for (int vertIndexA = 0; vertIndexA < facet.getNumberOfVertices(); vertIndexA++) { + final int index = vertIndexA; + Runnable worker = new Runnable() { + @Override + public void run() { + computeCurvature(facet, cache, index); + } + }; + executor.execute(worker); } - - double sumArea = 0.0; - double sumAngles = 0.0; - Vector3d pointSum = new Vector3d(); - - // for all surrounding triangles: - for (int i = 0; i < neighbouringTriangles.size(); i++) { - CurvTriangle ctri = triangles.get(neighbouringTriangles.get(i)); - CurvTriangle tNext = triangles.get(neighbouringTriangles.get((i + 1) % neighbouringTriangles.size())); - - sumArea += computeArea(ctri, facet.getVertex(vertA)); - sumAngles += ctri.alpha(facet.getVertex(vertA)); - - Vector3d aux = new Vector3d(ctri.vertC(facet.getVertex(vertA))); - aux.sub(ctri.vertA(facet.getVertex(vertA))); - aux.scale(ctri.betaCotan(facet.getVertex(vertA)) + tNext.gammaCotan(facet.getVertex(vertA))); - pointSum.add(aux); + executor.shutdown(); + while (!executor.isTerminated()){} + } else { + for (int vertIndexA = 0; vertIndexA < facet.getNumberOfVertices(); vertIndexA++) { + computeCurvature(facet, cache, vertIndexA); } - - double gaussVal = (2.0 * Math.PI - sumAngles) / sumArea; - double meanVal = 0.25 * sumArea * pointSum.length(); - double delta = Math.max(0, Math.pow(meanVal, 2) - gaussVal); - - this.gaussian.get(facet).add(gaussVal); - this.mean.get(facet).add(meanVal); - this.minPrincipal.get(facet).add(meanVal - Math.sqrt(delta)); - this.maxPrincipal.get(facet).add(meanVal + Math.sqrt(delta)); } } - protected List<CurvTriangle> precomputeTriangles(MeshFacet facet) { - List<CurvTriangle> ret = new ArrayList<>(facet.getNumTriangles()); - for (MeshTriangle tri: facet) { - ret.add(new CurvTriangle(facet, tri)); + protected void computeCurvature(MeshFacet facet, Cache cache, int vertIndexA) { + TriangleFan oneRing = facet.getOneRingNeighborhood(vertIndexA); + + if (oneRing.isBoundary()) { + synchronized (this) { + this.gaussian.get(facet).set(vertIndexA, 0.0); + this.mean.get(facet).set(vertIndexA, 0.0); + this.minPrincipal.get(facet).set(vertIndexA, 0.0); + this.maxPrincipal.get(facet).set(vertIndexA, 0.0); + return; + } } - return ret; - } - - protected double computeArea(CurvTriangle ctri, MeshPoint vertA) { - double alpha = ctri.alpha(vertA); - double beta = ctri.beta(vertA); - double gamma = ctri.gamma(vertA); - - double piHalf = Math.PI / 2.0; // 90 degrees - if (alpha >= piHalf || beta >= piHalf || gamma >= piHalf) { // check for obtuse angle - return (alpha > piHalf) ? ctri.area() / 2.0 : ctri.area() / 4.0; - } else { - double cotBeta = ctri.betaCotan(vertA); - double cotGamma = ctri.gammaCotan(vertA); - double ab = ctri.lengthSquaredAB(vertA); - double ac = ctri.lengthSquaredAC(vertA); - return (ab * cotGamma + ac * cotBeta) / 8.0; + + double sumAngles = 0.0; + double sumArea = 0.0; + Vector3d pointSum = new Vector3d(); + + Vector3d vertA = facet.getVertex(vertIndexA).getPosition(); + for (int i = 0; i < oneRing.getVertices().size() - 1; i++) { + Vector3d vertB = facet.getVertex(oneRing.getVertices().get(i)).getPosition(); + Vector3d vertC = facet.getVertex(oneRing.getVertices().get(i + 1)).getPosition(); + + // Angles: + sumAngles += cache.getAngle(vertA, vertB, vertC); + + // Area: + double alpha = cache.getAngle(vertA, vertB, vertC); + double beta = cache.getAngle(vertB, vertA, vertC); + double gamma = cache.getAngle(vertC, vertA, vertB); + + Vector3d ab = new Vector3d(vertB); + ab.sub(vertA); + + Vector3d ac = new Vector3d(vertC); + ac.sub(vertA); + + double lenSqrtAB = ab.lengthSquared(); + double lenSqrtAC = ac.lengthSquared(); + + double piHalf = Math.PI / 2.0; // 90 degrees + if (alpha >= piHalf || beta >= piHalf || gamma >= piHalf) { // check for obtuse angle + double area = 0.5 * lenSqrtAB * lenSqrtAC * Math.sin(alpha); + sumArea += (alpha > piHalf) ? area / 2.0 : area / 4.0; + } else { + double cotGamma = cache.getCotan(vertC, vertA, vertB); + double cotBeta = cache.getCotan(vertB, vertA, vertC); + sumArea += (lenSqrtAB * cotGamma + lenSqrtAC * cotBeta) / 8.0; + } + + // Laplace: + Vector3d vertD; + if (i == (oneRing.getVertices().size() - 2)) { // last triangle + vertD = facet.getVertex(oneRing.getVertices().get(1)).getPosition(); + } else { + vertD = facet.getVertex(oneRing.getVertices().get(i + 2)).getPosition(); + } + //ac.scale(cache2.get(tri).getCotan(vertB) + cache2.get(triNext).getCotan(vertD)); + ac.scale(cache.getCotan(vertB, vertA, vertC) + cache.getCotan(vertD, vertA, vertC)); + pointSum.add(ac); + } + + double gaussVal = (2.0 * Math.PI - sumAngles) / sumArea; + double meanVal = 0.25 * sumArea * pointSum.length(); + double delta = Math.max(0, Math.pow(meanVal, 2) - gaussVal); + + synchronized (this) { + this.gaussian.get(facet).set(vertIndexA, gaussVal); + this.mean.get(facet).set(vertIndexA, meanVal); + this.minPrincipal.get(facet).set(vertIndexA, meanVal - Math.sqrt(delta)); + this.maxPrincipal.get(facet).set(vertIndexA, meanVal + Math.sqrt(delta)); } } - + /** - * Helper class that caches triangle characteristics used multiples times during the curvature computation. - * <ul> - * <li>A = central point of the 1-ring neighborhood</li> - * <li>B = point "on the left" (previous point in the clockwise orientation of the triangle</li> - * <li>C = point "on the right" (next point in the clockwise orientation of the triangle</li> - * </ul> - * + * Cache key. * @author Radek Oslejsek */ - protected class CurvTriangle { - - private final MeshFacet facet; - private final MeshTriangle tri; + private class CacheKey { - private final double v1Angle; - private final double v2Angle; - private final double v3Angle; - - private final double v1AngleCotang; - private final double v2AngleCotang; - private final double v3AngleCotang; - - public CurvTriangle(MeshFacet facet, MeshTriangle tri) { - this.facet = facet; - this.tri = tri; - - Vector3d a = new Vector3d(facet.getVertex(tri.index3).getPosition()); - a.sub(facet.getVertex(tri.index2).getPosition()); - - Vector3d b = new Vector3d(facet.getVertex(tri.index1).getPosition()); - b.sub(facet.getVertex(tri.index3).getPosition()); - - Vector3d c = new Vector3d(facet.getVertex(tri.index2).getPosition()); - c.sub(facet.getVertex(tri.index1).getPosition()); - - a.normalize(); - b.normalize(); - c.normalize(); - - b.scale(-1.0); - double cos1 = c.dot(b); - - c.scale(-1.0); - double cos2 = a.dot(c); - - a.scale(-1.0); - b.scale(-1.0); - double cos3 = a.dot(b); - - this.v1Angle = Math.acos(cos1); - this.v2Angle = Math.acos(cos2); - this.v3Angle = Math.acos(cos3); - - this.v1AngleCotang = 1.0 / Math.tan(v1Angle); - this.v2AngleCotang = 1.0 / Math.tan(v2Angle); - this.v3AngleCotang = 1.0 / Math.tan(v3Angle); - } - - public double area() { - return 0.5 * lengthSquaredAC(tri.vertex1) * lengthSquaredAB(tri.vertex1) * Math.sin(v1Angle); - } + private final Vector3d vCentral; + private final Vector3d v2; + private final Vector3d v3; /** - * Returns vertex A. - * - * @param facetPoint Central point of the 1-ring neighborhood - * @return vertex A. + * Constructor. + * @param vCentral The vertex for which the values are stored + * @param v2 Other vertex in the triangle + * @param v3 Other vertex in the triangle */ - public Vector3d vertA(MeshPoint facetPoint) { - if (facetPoint == tri.vertex1) { - return tri.vertex1.getPosition(); - } else if (facetPoint == tri.vertex2) { - return tri.vertex2.getPosition(); - } else if (facetPoint == tri.vertex3) { - return tri.vertex3.getPosition(); - } - return null; + CacheKey(Vector3d vCentral, Vector3d v2, Vector3d v3) { + this.vCentral = vCentral; + this.v2 = v2; + this.v3 = v3; } - /** - * Returns vertex B. - * - * @param facetPoint Central point of the 1-ring neighborhood - * @return vertex B. - */ - public Vector3d vertB(MeshPoint facetPoint) { - if (facetPoint == tri.vertex1) { - return tri.vertex3.getPosition(); - } else if (facetPoint == tri.vertex2) { - return tri.vertex1.getPosition(); - } else if (facetPoint == tri.vertex3) { - return tri.vertex2.getPosition(); - } - return null; + @Override + public int hashCode() { + int hash = 7; + hash = 11 * hash + Objects.hashCode(this.vCentral); + hash = 11 * hash + Objects.hashCode(this.v2); + hash = 11 * hash + Objects.hashCode(this.v3); + return hash; } - /** - * Returns vertex C. - * - * @param facetPoint Central point of the 1-ring neighborhood - * @return vertex C. - */ - public Vector3d vertC(MeshPoint facetPoint) { - if (facetPoint == tri.vertex1) { - return tri.vertex2.getPosition(); - } else if (facetPoint == tri.vertex2) { - return tri.vertex3.getPosition(); - } else if (facetPoint == tri.vertex3) { - return tri.vertex1.getPosition(); + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - return null; - } - - /** - * Returns cached angle in the vertex A. - * - * @param facetPoint Central point of the 1-ring neighborhood - * @return Alpha angle. - */ - public double alpha(MeshPoint facetPoint) { - if (facetPoint == tri.vertex1) { - return v1Angle; - } else if (facetPoint == tri.vertex2) { - return v2Angle; - } else if (facetPoint == tri.vertex3) { - return v3Angle; + if (obj == null) { + return false; } - return Double.NaN; - } - - /** - * Returns cached angle in the vertex B. - * - * @param facetPoint Central point of the 1-ring neighborhood - * @return Beta angle. - */ - public double beta(MeshPoint facetPoint) { - if (facetPoint == tri.vertex1) { - return v3Angle; - } else if (facetPoint == tri.vertex2) { - return v1Angle; - } else if (facetPoint == tri.vertex3) { - return v2Angle; + if (getClass() != obj.getClass()) { + return false; } - return Double.NaN; - } - - /** - * Returns cached angle in the vertex C. - * - * @param facetPoint Central point of the 1-ring neighborhood - * @return Gamma angle. - */ - public double gamma(MeshPoint facetPoint) { - if (facetPoint == tri.vertex1) { - return v2Angle; - } else if (facetPoint == tri.vertex2) { - return v3Angle; - } else if (facetPoint == tri.vertex3) { - return v1Angle; + final CacheKey other = (CacheKey) obj; + if (!Objects.equals(this.vCentral, other.vCentral)) { + return false; } - return Double.NaN; - } - - /** - * Returns cached cotangent of the angle in the vertex A. - * - * @param facetPoint Central point of the 1-ring neighborhood - * @return Cotangent of the alpha angle. - */ - public double alphaCotan(MeshPoint facetPoint) { - if (facetPoint == tri.vertex1) { - return v1AngleCotang; - } else if (facetPoint == tri.vertex2) { - return v2AngleCotang; - } else if (facetPoint == tri.vertex3) { - return v3AngleCotang; + if (!Objects.equals(this.v2, other.v2)) { + return false; } - return Double.NaN; - } - - /** - * Returns cached cotangent of the angle in the vertex B. - * - * @param facetPoint Central point of the 1-ring neighborhood - * @return Cotangent of the beta angle. - */ - public double betaCotan(MeshPoint facetPoint) { - if (facetPoint == tri.vertex1) { - return v3AngleCotang; - } else if (facetPoint == tri.vertex2) { - return v1AngleCotang; - } else if (facetPoint == tri.vertex3) { - return v2AngleCotang; + if (!Objects.equals(this.v3, other.v3)) { + return false; } - return Double.NaN; + return true; } - /** - * Returns cached cotangent of the angle in the vertex C. - * - * @param facetPoint Central point of the 1-ring neighborhood - * @return Cotangent of the gamma angle. - */ - public double gammaCotan(MeshPoint facetPoint) { - if (facetPoint == tri.vertex1) { - return v2AngleCotang; - } else if (facetPoint == tri.vertex2) { - return v3AngleCotang; - } else if (facetPoint == tri.vertex3) { - return v1AngleCotang; - } - return Double.NaN; + @Override + public String toString() { + return vCentral + " | " + v2 + " | " + v3; } + } + + /** + * Helper class that caches triangle characteristics used multiples times during the curvature computation. + * @author Radek Oslejsek + */ + private class Cache { + + private final Map<CacheKey, List<Double>> cache = new HashMap<>(); + /** - * Returns squared length of the edge AB (opposite to the vertex A). - * - * @param facetPoint Central point of the 1-ring neighborhood - * @return Squared length of the edge AB. + * Constructor. + * @param facet Mesh facet */ - public double lengthSquaredAB(MeshPoint facetPoint) { - Vector3d v = null; - if (facetPoint == tri.vertex1) { - v = new Vector3d(facet.getVertex(tri.index3).getPosition()); - v.sub(facet.getVertex(tri.index1).getPosition()); - } else if (facetPoint == tri.vertex2) { - v = new Vector3d(facet.getVertex(tri.index1).getPosition()); - v.sub(facet.getVertex(tri.index2).getPosition()); - } else if (facetPoint == tri.vertex3) { - v = new Vector3d(facet.getVertex(tri.index2).getPosition()); - v.sub(facet.getVertex(tri.index3).getPosition()); + Cache(MeshFacet facet) { + for (MeshTriangle tri: facet.getTriangles()) { + computeValues(facet, tri); } - return v.lengthSquared(); } /** - * Returns squared length of the edge AC (opposite to the vertex B). + * Returns cached angle in the vertex {@code vCentral} at the triangle + * containing vertices {@code v2} and {@code v3}. * - * @param facetPoint Central point of the 1-ring neighborhood - * @return Squared length of the edge AC. + * @param vCentral Central vertex + * @param v2 Another vertex in the triangle + * @param v3 Another vertex in the triangle + * @return Angle at vertex {@code vCentral} */ - public double lengthSquaredAC(MeshPoint facetPoint) { - Vector3d v = null; - if (facetPoint == tri.vertex1) { - v = new Vector3d(facet.getVertex(tri.index2).getPosition()); - v.sub(facet.getVertex(tri.index1).getPosition()); - } else if (facetPoint == tri.vertex2) { - v = new Vector3d(facet.getVertex(tri.index3).getPosition()); - v.sub(facet.getVertex(tri.index2).getPosition()); - } else if (facetPoint == tri.vertex3) { - v = new Vector3d(facet.getVertex(tri.index1).getPosition()); - v.sub(facet.getVertex(tri.index3).getPosition()); - } - return v.lengthSquared(); + public double getAngle(Vector3d vCentral, Vector3d v2, Vector3d v3) { + CacheKey key = new CacheKey(vCentral, v2, v3); + if (cache.containsKey(key)) { + return cache.get(key).get(0); + } + + key = new CacheKey(vCentral, v3, v2); + if (cache.containsKey(key)) { + return cache.get(key).get(0); + } + + throw new IllegalArgumentException("[" + vCentral + ", " + v2 + ", " + v3 + "]"); } /** - * Returns squared length of the edge BC (opposite to the vertex A). + * Returns cached cotangent of the angle in the vertex {@code vCentral} at the triangle + * containing vertices {@code v2} and {@code v3}. * - * @param facetPoint Central point of the 1-ring neighborhood - * @return Squared length of the edge BC. + * @param vCentral Central vertex + * @param v2 Another vertex in the triangle + * @param v3 Another vertex in the triangle + * @return Cotangent of the angle at vertex {@code vCentral} */ - public double lengthSquaredBC(MeshPoint facetPoint) { - Vector3d v = null; - if (facetPoint == tri.vertex1) { - v = new Vector3d(facet.getVertex(tri.index3).getPosition()); - v.sub(facet.getVertex(tri.index2).getPosition()); - v.sub(facetPoint.getPosition()); - } else if (facetPoint == tri.vertex2) { - v = new Vector3d(facet.getVertex(tri.index1).getPosition()); - v.sub(facet.getVertex(tri.index3).getPosition()); - } else if (facetPoint == tri.vertex3) { - v = new Vector3d(facet.getVertex(tri.index2).getPosition()); - v.sub(facet.getVertex(tri.index1).getPosition()); + public double getCotan(Vector3d vCentral, Vector3d v2, Vector3d v3) { + CacheKey key = new CacheKey(vCentral, v2, v3); + if (cache.containsKey(key)) { + return cache.get(key).get(1); } - return v.lengthSquared(); + + key = new CacheKey(vCentral, v3, v2); + if (cache.containsKey(key)) { + return cache.get(key).get(1); + } + + throw new IllegalArgumentException("[" + vCentral + ", " + v2 + ", " + v3 + "]"); } - } - + + private void computeValues(MeshFacet facet, MeshTriangle tri) { + Vector3d ab = new Vector3d(facet.getVertex(tri.index2).getPosition()); + ab.sub(facet.getVertex(tri.index1).getPosition()); + + Vector3d ac = new Vector3d(facet.getVertex(tri.index3).getPosition()); + ac.sub(facet.getVertex(tri.index1).getPosition()); + + Vector3d bc = new Vector3d(facet.getVertex(tri.index3).getPosition()); + bc.sub(facet.getVertex(tri.index2).getPosition()); + + ab.normalize(); + ac.normalize(); + bc.normalize(); + + double cosA = ab.dot(ac); + + ab.scale(-1.0); + double cosB = ab.dot(bc); + + ac.scale(-1.0); + bc.scale(-1.0); + double cosC = ac.dot(bc); + + List<Double> list1 = new ArrayList<>(); + List<Double> list2 = new ArrayList<>(); + List<Double> list3 = new ArrayList<>(); + + list1.add(Math.acos(cosA)); + list2.add(Math.acos(cosB)); + list3.add(Math.acos(cosC)); + + list1.add(1.0 / Math.tan(list1.get(0))); + list2.add(1.0 / Math.tan(list2.get(0))); + list3.add(1.0 / Math.tan(list3.get(0))); + + cache.put(new CacheKey(tri.getVertex1(), tri.getVertex2(), tri.getVertex3()), list1); + cache.put(new CacheKey(tri.getVertex2(), tri.getVertex1(), tri.getVertex3()), list2); + cache.put(new CacheKey(tri.getVertex3(), tri.getVertex2(), tri.getVertex1()), list3); + } + } } diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistance.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistance.java index 51b08f8db7b0d2dbde261cea0ef2047512108b19..a865cff8e36def29d4a7a2be630992073d217411 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistance.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistance.java @@ -183,19 +183,7 @@ public class HausdorffDistance extends MeshVisitor { * @param strategy Strategy of the computation of distance * @param relativeDistance If true, then the visitor calculates the relative distances with respect * to the normal vectors of source facets (normal vectors have to present), - * i.e.public void drawMaxPrincipal() { - if (this.getModel() == null) { - return; - } - Curvature vis = new Curvature(); - this.getModel().compute(vis); - values = vis.getMaxPrincipalCurvatures(); - minDistance = 0.0; - maxDistance = 2.0; - //List<Double> distanceList = values.get(getModel().getFacets().get(0)); - //minDistance = distanceList.stream().mapToDouble(Double::doubleValue).min().getAsDouble(); - //maxDistance = distanceList.stream().mapToDouble(Double::doubleValue).max().getAsDouble(); - }, we can get negative distances. + * i.e., we can get negative distances. * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores * @throws IllegalArgumentException if some parameter is wrong */ diff --git a/GUI/src/main/java/cz/fidentis/analyst/tests/EfficiencyTests.java b/GUI/src/main/java/cz/fidentis/analyst/tests/EfficiencyTests.java index 64a50054ba13e12cafa3ee9aa4235158aae75200..aa40e41d52b21581ffcc7924a498a391ef21d7dc 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/tests/EfficiencyTests.java +++ b/GUI/src/main/java/cz/fidentis/analyst/tests/EfficiencyTests.java @@ -40,32 +40,32 @@ public class EfficiencyTests { face2 = new HumanFace(boyFile); boolean relativeDist = false; - boolean printDetails = true; + boolean printDetails = false; //face1.getMeshModel().compute(new GaussianCurvature()); // initialize everything, then measure - //System.out.println("Symmetry plane calculation:"); - //printSymmetryPlane(face1, true, SignificantPoints.CurvatureAlg.MEAN); - //printSymmetryPlane(face1, true, SignificantPoints.CurvatureAlg.GAUSSIAN); - //printSymmetryPlane(face1, false, SignificantPoints.CurvatureAlg.MEAN); - //printSymmetryPlane(face1, false, SignificantPoints.CurvatureAlg.GAUSSIAN); + System.out.println("Symmetry plane calculation:"); + printSymmetryPlane(face1, true, SignificantPoints.CurvatureAlg.MEAN); + printSymmetryPlane(face1, true, SignificantPoints.CurvatureAlg.GAUSSIAN); + printSymmetryPlane(face1, false, SignificantPoints.CurvatureAlg.MEAN); + printSymmetryPlane(face1, false, SignificantPoints.CurvatureAlg.GAUSSIAN); - //System.out.println(); - //System.out.println(measureKdTreeCreation(face1) + "\tmsec:\tKd-tree creation of first face"); - //System.out.println(measureKdTreeCreation(face2) + "\tmsec:\tKd-tree creation of second face"); + System.out.println(); + System.out.println(measureKdTreeCreation(face1) + "\tmsec:\tKd-tree creation of first face"); + System.out.println(measureKdTreeCreation(face2) + "\tmsec:\tKd-tree creation of second face"); System.out.println(); System.out.println("Hausdorff distance sequentially:"); - testAndPrint(face2, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false), printDetails); - //testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, false), printDetails); - //testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false), printDetails); - //testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, false), printDetails); + testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false), printDetails); + testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, false), printDetails); + testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false), printDetails); + testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, false), printDetails); - //System.out.println(); - //System.out.println("Hausdorff distance concurrently:"); - //testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, true), printDetails); - //testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, true), printDetails); - //testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, true), printDetails); + System.out.println(); + System.out.println("Hausdorff distance concurrently:"); + testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, true), printDetails); + testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, true), printDetails); + testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, true), printDetails); } protected static void testAndPrint(HumanFace face, HausdorffDistance vis, boolean printDetails) { diff --git a/GUI/src/main/java/cz/fidentis/analyst/tests/HeatMapsTestApp.java b/GUI/src/main/java/cz/fidentis/analyst/tests/HeatMapsTestApp.java index 1a95ee80eaf3dc497581bce53d67240dbe60129c..22cbf35a4ce2ccc8c00c6b5f90e91d8c5838c29d 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/tests/HeatMapsTestApp.java +++ b/GUI/src/main/java/cz/fidentis/analyst/tests/HeatMapsTestApp.java @@ -127,15 +127,12 @@ public class HeatMapsTestApp extends GeneralGLEventListener { System.err.println(); System.err.println("MODEL: " + getModel()); - Set<MeshPoint> uniqueVerts = new HashSet<>(getModel().getFacets().get(0).getVertices()); - System.err.println("UNIQUE VERTICES: " + uniqueVerts.size()); - - /* - Set<Vector3d> unique2 = new HashSet<>(); + Set<Vector3d> unique = new HashSet<>(); for (MeshPoint p: getModel().getFacets().get(0).getVertices()) { - unique2.add(p.getPosition()); + unique.add(p.getPosition()); } - System.out.println("UNIQUE VERTICES: " + unique2.size()); + System.out.println("UNIQUE VERTICES: " + unique.size()); + /* for (Vector3d v: unique2) { for (MeshPoint p: uniqueVerts) { if (v.equals(p.getPosition())) { @@ -181,8 +178,8 @@ public class HeatMapsTestApp extends GeneralGLEventListener { long duration = System.currentTimeMillis() - startTime; System.err.println(duration + "\tmsec: Mean curvature"); - minDistance = 0.0; - maxDistance = 2.0; + minDistance = -0.001; + maxDistance = 0.1; } /** @@ -200,8 +197,8 @@ public class HeatMapsTestApp extends GeneralGLEventListener { long duration = System.currentTimeMillis() - startTime; System.err.println(duration + "\tmsec: Minimum principal curvature"); - minDistance = -0.03; - maxDistance = 0.05; + minDistance = -0.05; + maxDistance = 0.03; } /** @@ -219,8 +216,8 @@ public class HeatMapsTestApp extends GeneralGLEventListener { long duration = System.currentTimeMillis() - startTime; System.err.println(duration + "\tmsec: Maximum principal curvature"); - minDistance = 0.0; - maxDistance = 2.0; + minDistance = -0.02; + maxDistance = 0.25; } /** diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/CornerTable.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/CornerTable.java index 93fb252704d0bec963e057c1602da2b7e95bb780..045a8666dadb95061252a558543c3226d992c4ba 100644 --- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/CornerTable.java +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/CornerTable.java @@ -1,6 +1,7 @@ package cz.fidentis.analyst.mesh.core; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -215,6 +216,15 @@ public class CornerTable { .map(rowIndex -> rows.get(rowIndex)) .collect(Collectors.toList()); } + + /** + * Returns cache. + * + * @return cache + */ + public Map<Integer, List<Integer>> getVertToCornerCache() { + return Collections.unmodifiableMap(vertexToRow); + } /** * returns list of indexes of faces that have a corner at specific vertex 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 c9dcefae010db42188a770888edcda9765833419..27cfd7980e8736b25f15b34db106dfc7dea9089b 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 @@ -5,7 +5,7 @@ import cz.fidentis.analyst.mesh.MeshVisitor; import javax.vecmath.Vector3d; /** - * An ancapsulated mesh plate, i.e., multiple triangles sharing vertices. + * An encapsulated mesh plate, i.e., multiple triangles sharing vertices. * Mesh facet is iterable (triangle as returned) and visitable by * {@link cz.fidentis.analyst.mesh.MeshVisitor}s. * @@ -114,8 +114,8 @@ public interface MeshFacet extends Iterable<MeshTriangle> { void accept(MeshVisitor visitor); /** - * Computes centers of circumcircle of all triaqngles. - * These points represent the point of Voronoi area used for Delaunay triangulation, for instenace. + * Computes centers of circumcircle of all triangles. + * These points represent the point of Voronoi area used for Delaunay triangulation, for instance. * The list is computed only once (during the first call) and than cached. * The order corresponds to the order of triangles, i.e., the i-th point * is the Voronoi point of i-th triangle. @@ -125,9 +125,10 @@ public interface MeshFacet extends Iterable<MeshTriangle> { List<Vector3d> calculateVoronoiPoints(); /** - * Returns {@code true} if the given vertex lie at the facet boundary. + * Returns 1-ring neighborhood, i.e., triangles around the given mesh point. + * * @param vertexIndex Index of mesh vertex - * @return {@code true} if the given vertex lie at the facet boundary. + * @return Triangles around the vertex or {@code null} */ - boolean vertexIsBoundary(int vertexIndex); + TriangleFan getOneRingNeighborhood(int vertexIndex); } 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 b009ee71c6b5bfe7c45b2776d7d7567b2e9d0f93..af454d2e3ef578962638d7070e4cc4037c1cb2d4 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 @@ -7,10 +7,8 @@ import java.util.List; import java.util.Map; import javax.vecmath.Vector3d; import cz.fidentis.analyst.mesh.MeshVisitor; -import java.util.HashSet; import java.util.Iterator; import java.util.NoSuchElementException; -import java.util.Set; /** * Mash facet is a compact triangular mesh without duplicated vertices. @@ -100,10 +98,10 @@ public class MeshFacetImpl implements MeshFacet { // calculate normals from corresponding triangles for (MeshTriangle t : this) { Vector3d triangleNormal = - (t.vertex3.subtractPosition(t.vertex1)).crossProduct(t.vertex2.subtractPosition(t.vertex1)).getPosition(); - normalMap.get(t.vertex1.getPosition()).add(triangleNormal); - normalMap.get(t.vertex2.getPosition()).add(triangleNormal); - normalMap.get(t.vertex3.getPosition()).add(triangleNormal); + (t.getPoint3().subtractPosition(t.getPoint1())).crossProduct(t.getPoint2().subtractPosition(t.getPoint1())).getPosition(); + normalMap.get(t.getPoint1().getPosition()).add(triangleNormal); + normalMap.get(t.getPoint2().getPosition()).add(triangleNormal); + normalMap.get(t.getPoint3().getPosition()).add(triangleNormal); } // normalize normals: @@ -133,31 +131,17 @@ public class MeshFacetImpl implements MeshFacet { List<Integer> adjacentTrianglesI = cornerTable.getTriangleIndexesByVertexIndex(vertexIndex); for (Integer triI: adjacentTrianglesI) { List<Integer> triVerticesI = cornerTable.getIndexesOfVerticesByTriangleIndex(triI); - MeshTriangle tri = new MeshTriangle( - getVertex(triVerticesI.get(0)), - getVertex(triVerticesI.get(1)), - getVertex(triVerticesI.get(2)), + MeshTriangle tri = new MeshTriangle( + this, triVerticesI.get(0), triVerticesI.get(1), triVerticesI.get(2)); - ret.add(tri); - } + ret.add(tri); + } return ret; } - @Override - public boolean vertexIsBoundary(int vertexIndex) { - List<MeshTriangle> tris = getAdjacentTriangles(vertexIndex); - Set<Vector3d> vset = new HashSet<>(); - for (MeshTriangle tri: tris) { - for (MeshPoint p: tri) { - vset.add(p.getPosition()); - } - } - return (tris.size()+1) != vset.size(); - } - @Override public Vector3d getClosestAdjacentPoint(Vector3d point, int vertexIndex) { double dist = Double.POSITIVE_INFINITY; @@ -218,7 +202,7 @@ public class MeshFacetImpl implements MeshFacet { int i2 = cornerTable.getRow(index + 1).getVertexIndex(); int i3 = cornerTable.getRow(index + 2).getVertexIndex(); - MeshTriangle tri = new MeshTriangle(vertices.get(i1), vertices.get(i2), vertices.get(i3), i1, i2, i3); + MeshTriangle tri = new MeshTriangle(MeshFacetImpl.this, i1, i2, i3); index += 3; return tri; @@ -236,5 +220,13 @@ public class MeshFacetImpl implements MeshFacet { } return Collections.unmodifiableList(voronoiPoints); } + + @Override + public TriangleFan getOneRingNeighborhood(int vertexIndex) { + if (vertexIndex < 0 || vertexIndex >= this.getNumberOfVertices()) { + return null; + } + return new TriangleFan(this, 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 bcd476b777886e70c26514a84a515c20fa034439..75c24d041b515824c2d7085811973ec1523e2ecd 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 @@ -16,9 +16,11 @@ public class MeshTriangle implements Iterable<MeshPoint> { public static final double EPS = 0.001; // tollerance - public final MeshPoint vertex1; - public final MeshPoint vertex2; - public final MeshPoint vertex3; + //public final MeshPoint vertex1; + //public final MeshPoint vertex2; + //public final MeshPoint vertex3; + + private final MeshFacet facet; /** * Under which index is the corresponding vertex stored in the mesh facet @@ -39,13 +41,38 @@ public class MeshTriangle implements Iterable<MeshPoint> { * @param i2 which index is the second vertex stored in the mesh facet * @param i3 which index is the third vertex stored in the mesh facet */ - public MeshTriangle(MeshPoint v1, MeshPoint v2, MeshPoint v3, int i1, int i2, int i3) { - this.vertex1 = v1; - this.vertex2 = v2; - this.vertex3 = v3; + public MeshTriangle(MeshFacet facet, int i1, int i2, int i3) { + if (facet == null) { + throw new IllegalArgumentException("facet"); + } this.index1 = i1; this.index2 = i2; this.index3 = i3; + this.facet = facet; + } + + public Vector3d getVertex1() { + return facet.getVertex(index1).getPosition(); + } + + public Vector3d getVertex2() { + return facet.getVertex(index2).getPosition(); + } + + public Vector3d getVertex3() { + return facet.getVertex(index3).getPosition(); + } + + public MeshPoint getPoint1() { + return facet.getVertex(index1); + } + + public MeshPoint getPoint2() { + return facet.getVertex(index2); + } + + public MeshPoint getPoint3() { + return facet.getVertex(index3); } /** @@ -54,10 +81,10 @@ public class MeshTriangle implements Iterable<MeshPoint> { * @return normalized normal vector from the vertices. */ public Vector3d computeNormal() { - Vector3d ab = new Vector3d(vertex1.getPosition()); - ab.sub(vertex2.getPosition()); - Vector3d ac = new Vector3d(vertex1.getPosition()); - ac.sub(vertex3.getPosition()); + Vector3d ab = new Vector3d(getVertex1()); + ab.sub(getVertex2()); + Vector3d ac = new Vector3d(getVertex1()); + ac.sub(getVertex3()); Vector3d normal = new Vector3d(); normal.cross(ab, ac); normal.normalize(); @@ -101,11 +128,11 @@ public class MeshTriangle implements Iterable<MeshPoint> { } switch (counter++) { case 0: - return vertex1; + return facet.getVertex(index1); case 1: - return vertex2; + return facet.getVertex(index2); case 2: - return vertex3; + return facet.getVertex(index3); default: return null; } @@ -124,6 +151,10 @@ public class MeshTriangle implements Iterable<MeshPoint> { return voronoiPoint; } + MeshPoint vertex1 = facet.getVertex(index1); + MeshPoint vertex2 = facet.getVertex(index2); + MeshPoint vertex3 = facet.getVertex(index3); + double a = (vertex2.subtractPosition(vertex3)).abs(); double b = (vertex3.subtractPosition(vertex1)).abs(); double c = (vertex2.subtractPosition(vertex1)).abs(); @@ -202,7 +233,7 @@ public class MeshTriangle implements Iterable<MeshPoint> { protected Vector3d getProjectionToTrianglePlane(Vector3d point) { Vector3d normal = computeNormal(); Vector3d helperVector = new Vector3d(point); - helperVector.sub(vertex1.getPosition()); + helperVector.sub(getVertex1()); double dist = helperVector.dot(normal); Vector3d projection = new Vector3d(point); @@ -242,14 +273,10 @@ public class MeshTriangle implements Iterable<MeshPoint> { * @return perpendicular projection to the nearest edge */ protected Vector3d getProjectionToClosestEdge(Vector3d point) { - Vector3d v1 = vertex1.getPosition(); - Vector3d v2 = vertex2.getPosition(); - Vector3d v3 = vertex3.getPosition(); - double minDistance = Double.POSITIVE_INFINITY; Vector3d closestProjection = null; - Vector3d projection = getProjectionToEdge(point, v2, v1); + Vector3d projection = getProjectionToEdge(point, getVertex2(), getVertex1()); Vector3d aux = new Vector3d(point); aux.sub(projection); double dist = aux.length(); @@ -258,7 +285,7 @@ public class MeshTriangle implements Iterable<MeshPoint> { closestProjection = projection; } - projection = getProjectionToEdge(point, v3, v2); + projection = getProjectionToEdge(point, getVertex3(), getVertex2()); aux.sub(point, projection); dist = aux.length(); if (dist < minDistance) { @@ -266,7 +293,7 @@ public class MeshTriangle implements Iterable<MeshPoint> { closestProjection = projection; } - projection = getProjectionToEdge(point, v1, v3); + projection = getProjectionToEdge(point, getVertex1(), getVertex3()); aux.sub(point, projection); dist = aux.length(); if (dist < minDistance) { diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/TriangleFan.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/TriangleFan.java index c8df70d97defb056090826000efd3261631af83b..eec8bd25d17a278533061b3bf14731335732bdd5 100644 --- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/TriangleFan.java +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/TriangleFan.java @@ -1,8 +1,10 @@ package cz.fidentis.analyst.mesh.core; import java.util.Collections; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.NoSuchElementException; import java.util.Objects; /** @@ -11,62 +13,102 @@ import java.util.Objects; * * @author Radek Oslejsek */ -public class TriangleFan { +public class TriangleFan implements Iterable<MeshTriangle> { + private final int centralVertex; + private final MeshFacet facet; private final List<Integer> vertices = new LinkedList<>(); private final List<Integer> triangles = new LinkedList<>(); + /** + * Constructor. + * + * @param facet Mesh facet + * @param vert Central vertex for the triangle fan + * @throws IllegalArgumentException if some parameter is missing or wrong + */ public TriangleFan(MeshFacet facet, int vert) { if (facet == null) { throw new IllegalArgumentException("facet"); } - //System.out.println(facet.getVertices()); + centralVertex = vert; + this.facet = facet; - CornerTable ct = facet.getCornerTable(); + // get first appearance of the vertex in the corner table: + int vertRow = facet.getCornerTable().getVertToCornerCache().get(vert).get(0); - int vertRow = getVertexRow(ct, vert); - //System.out.println("DDD " + vert + " " + vertRow + " " + ct.getRow(vertRow).getVertexIndex()); + /* + int vertRow = -1; + for (int i = 0; i < facet.getCornerTable().getSize(); i++) { + if (facet.getCornerTable().getRow(i).getVertexIndex() == vert) { + vertRow = i; + break; + } + } if (vertRow == -1) { throw new IllegalArgumentException("vert"); } + */ - computeOneRingData(ct, vertRow, vertices, triangles); + computeOneRingData(vertRow); } + /** + * Returns {@code true} if the triangle fan is enclosed (i.e., is not boundary). + * + * @return {@code true} if the triangle fan is enclosed (i.e., is not boundary). + */ public boolean isEnclosed() { return Objects.equals(vertices.get(0), vertices.get(vertices.size()-1)); } - public boolean isBoundery() { + /** + * Returns {@code true} if the triangle fan is boundary (i.e., is not enclosed). + * + * @return {@code true} if the triangle fan is enclosed (i.e., is not boundary). + */ + public boolean isBoundary() { return !isEnclosed(); } + /** + * Vertex indices of the 1-ring neighborhood. If the triangle fan is enclosed + * then the first and last vertices are the same. + * + * @return Vertex indices of the 1-ring neighborhood. + */ public List<Integer> getVertices() { return Collections.unmodifiableList(vertices); } + /** + * Triangle indices of the 1-ring neighborhood. The first triangle is related to + * the edge delimited by first and second vertex, etc. The number of triangles + * corresponds to the number of vertices minus one. + * + * @return Triangle indices of the 1-ring neighborhood. + */ public List<Integer> getTriangles() { return Collections.unmodifiableList(triangles); } - private void computeOneRingData(CornerTable ct, int vertRow, List<Integer> vertices, List<Integer> triangles) { - //System.out.println(ct); - //System.out.println("AAA " + ct.getRow(vertRow).getVertexIndex()); + private void computeOneRingData(int vertRow) { + CornerTable ct = facet.getCornerTable(); int ringVertRow = ct.getIndexOfNextCornerInFace(vertRow); while (true) { - //System.out.println("BBB " + ct.getRow(ringVertRow).getVertexIndex()); - vertices.add(ct.getRow(ringVertRow).getVertexIndex()); + vertices.add(facet.getCornerTable().getRow(ringVertRow).getVertexIndex()); if (vertices.size() > 1 && isEnclosed()) { return; // the ring is closed; we are done } - //System.out.println("CCC " + ct.getRow(ct.getIndexOfNextCornerInFace(ringVertRow)).getVertexIndex()); ringVertRow = ct.getIndexOfOppositeCorner(ct.getIndexOfNextCornerInFace(ringVertRow)); if (ringVertRow == -1) { break; // we reached an open end } - //System.out.println("DDD " + ct.getRow(ringVertRow).getVertexIndex()); triangles.add(ct.getIndexOfFace(ringVertRow)); + if (vertices.size() > ct.getSize()) { //emergency break + throw new RuntimeException("Error in mesh topology"); + } } ringVertRow = ct.getIndexOfPreviousCornerInFace(vertRow); @@ -80,16 +122,42 @@ public class TriangleFan { break; // we reached an open end } triangles.add(ct.getIndexOfFace(ringVertRow)); + if (vertices.size() > ct.getSize()) { //emergency break + throw new RuntimeException("Error in mesh topology"); + } } } - private int getVertexRow(CornerTable cornerTable, int vert) { - for (int i = 0; i < cornerTable.getSize(); i++) { - if (cornerTable.getRow(i).getVertexIndex() == vert) { - return i; + /** + * Vertex 1 of the returned triangle always corresponds to the central vertex + * around which the triangle fan is constructed. + * @return + */ + @Override + public Iterator<MeshTriangle> iterator() { + return new Iterator<MeshTriangle>() { + private int index; + + /** + * + * @param facet Mesh facet to iterate + */ + @Override + public boolean hasNext() { + return index < triangles.size(); } - } - return -1; + + @Override + public MeshTriangle next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + MeshTriangle tri = new MeshTriangle(facet, centralVertex, index, index+1); + index++; + return tri; + } + }; } } diff --git a/MeshModel/src/test/java/cz/fidentis/analyst/mesh/core/MeshTriangleTest.java b/MeshModel/src/test/java/cz/fidentis/analyst/mesh/core/MeshTriangleTest.java index 9abe112df7c45fbe704a55ed3321029239ae51bf..1fd03eecc738a72aba00a0b6260cd24d5261c0a9 100644 --- a/MeshModel/src/test/java/cz/fidentis/analyst/mesh/core/MeshTriangleTest.java +++ b/MeshModel/src/test/java/cz/fidentis/analyst/mesh/core/MeshTriangleTest.java @@ -12,6 +12,7 @@ import org.junit.jupiter.api.Test; */ public class MeshTriangleTest { + /* protected MeshTriangle getTriangle() { MeshTriangle triangle = new MeshTriangle( new MeshPointImpl(new Vector3d(1, 0, 0), null, null), @@ -36,5 +37,6 @@ public class MeshTriangleTest { Assertions.assertEquals(new Vector3d(1, 0, 0), tri.getClosestPoint(new Vector3d(1, 0, -1))); Assertions.assertEquals(new Vector3d(1.2, 0.2, 0), tri.getClosestPoint(new Vector3d(1.2, 0.2, -1))); } + */ }