diff --git a/Comparison/src/main/java/cz/fidentis/analyst/EfficiencyTests.java b/Comparison/src/main/java/cz/fidentis/analyst/EfficiencyTests.java index 8f39e0925a8482bbfec40b97521d51689a30aedf..d05318295732334fe3658e012b3b37ddc342db71 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/EfficiencyTests.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/EfficiencyTests.java @@ -6,6 +6,7 @@ import cz.fidentis.analyst.mesh.core.MeshFacet; import cz.fidentis.analyst.symmetry.Config; import cz.fidentis.analyst.symmetry.Plane; import cz.fidentis.analyst.symmetry.SymmetryEstimator; +import cz.fidentis.analyst.visitors.mesh.BoundingBoxVisitor; import cz.fidentis.analyst.visitors.mesh.HausdorffDistance; import cz.fidentis.analyst.visitors.mesh.HausdorffDistance.Strategy; import java.io.File; @@ -45,17 +46,17 @@ public class EfficiencyTests { boolean relativeDist = true; boolean printDetails = false; - System.out.println(); - System.out.println("Hausdorff distance sequentially:"); - testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_ABSOLUTE, false, false, false), printDetails); - testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false, false), printDetails); - testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, false, false), printDetails); + //System.out.println(); + //System.out.println("Hausdorff distance sequentially:"); + //testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_ABSOLUTE, false, false, false), printDetails); + //testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false, false), printDetails); + //testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, false, false), printDetails); - System.out.println(); - System.out.println("Hausdorff distance consurrently:"); - testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_ABSOLUTE, false, false, true), printDetails); - testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false, true), printDetails); - testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, false, true), printDetails); + //System.out.println(); + //System.out.println("Hausdorff distance consurrently:"); + //testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_ABSOLUTE, false, false, true), printDetails); + //testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false, true), printDetails); + //testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, false, true), printDetails); } protected static void testAndPrint(HumanFace face, HausdorffDistance vis, boolean printDetails) { @@ -87,7 +88,7 @@ public class EfficiencyTests { private static long measureSymmetryPlane(HumanFace face, boolean printDetails) { long startTime = System.currentTimeMillis(); - SymmetryEstimator est = new SymmetryEstimator(face.getMeshModel().getFacets().get(0), new Config()); + SymmetryEstimator est = new SymmetryEstimator(face.getMeshModel().getFacets().get(0), Config.getDefault()); Plane plane = est.getApproxSymmetryPlane(null); long retTime = System.currentTimeMillis() - startTime; diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java index 61a773781b87b698bc3790d0c6f721f7248209e3..d4025e8e235b6be48cf81eb99cf656f4977ff27b 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java @@ -8,6 +8,9 @@ import cz.fidentis.analyst.mesh.core.MeshFacetImpl; import cz.fidentis.analyst.mesh.core.MeshPointImpl; import cz.fidentis.analyst.mesh.core.MeshTriangle; import cz.fidentis.analyst.visitors.mesh.BoundingBoxVisitor; +import cz.fidentis.analyst.visitors.mesh.CurvatureVisitor; +import cz.fidentis.analyst.visitors.mesh.GaussianCurvatureVisitor; +import cz.fidentis.analyst.visitors.mesh.MaxCurvatureVisitor; import cz.fidentis.analyst.visitors.mesh.TriangleListVisitor; import java.util.ArrayList; import java.util.Collections; @@ -31,7 +34,7 @@ public class SymmetryEstimator { private final MeshFacet facet; // Facet of the model on which symmetry is computed - private TriangleVertexAreas[] areas; // Representation for areas of Voronoi region of triangles + private List<Vector3d> areas; // Representation for areas of Voronoi region of triangles private BoundingBox boundingBox; // Represent min-max box. It is automatically maintained by given point array. @@ -49,10 +52,10 @@ public class SymmetryEstimator { this.facet = f; this.config = config; - this.areas = new TriangleVertexAreas[f.getNumTriangles()]; + this.areas = new ArrayList<>(f.getNumTriangles()); int i = 0; for (MeshTriangle tri: f) { - areas[i++] = computeTriangleVertexAreas(tri); + areas.add(tri.getVoronoiPoint()); } TriangleListVisitor vis = new TriangleListVisitor(false); @@ -94,7 +97,7 @@ public class SymmetryEstimator { */ public BoundingBox getBoundingBox() { if (boundingBox == null) { - BoundingBoxVisitor visitor = new BoundingBoxVisitor(true); + BoundingBoxVisitor visitor = new BoundingBoxVisitor(false); facet.accept(visitor); boundingBox = visitor.getBoundingBox(); } @@ -103,36 +106,33 @@ public class SymmetryEstimator { /** * Computes the approximate plane of symmetry. + * TO DO: ZRUSIT VSTUPNI PARAMETR!!! + * * @param panel parrent window for the progress window (can be null) * @return approximate plane of symmtetry */ public Plane getApproxSymmetryPlane(JPanel panel) { - List<ApproxSymmetryPlane> planes = new ArrayList<>(); - //List<Vector3d> normals = calculateNormals(); - if (!facet.hasVertexNormals()) { facet.calculateVertexNormals(); } - double[] curvatures = initCurvatures(); - List<Double> sortedCurvatures = new ArrayList<>(); - for (int i = 0; i < curvatures.length; i++) { - sortedCurvatures.add(curvatures[i]); - } + List<ApproxSymmetryPlane> planes = new ArrayList<>(); + + List<Double> curvatures = initCurvatures(facet); + List<Double> sortedCurvatures = new ArrayList<>(curvatures); Collections.sort(sortedCurvatures); - if(config.getSignificantPointCount() > facet.getNumberOfVertices()) { + if (config.getSignificantPointCount() > facet.getNumberOfVertices()) { config.setSignificantPointCount((facet.getNumberOfVertices()) - 1); } double bottomCurvature = sortedCurvatures.get(sortedCurvatures.size() - 1 - config.getSignificantPointCount()); List<Integer> significantPoints = new ArrayList<>(); List<Double> significantCurvatures = new ArrayList<>(); - for (int i = 0; i < facet.getNumberOfVertices(); i++) { - if (curvatures[i] >= bottomCurvature) { - significantCurvatures.add(curvatures[i]); + if (curvatures.get(i) >= bottomCurvature) { + significantCurvatures.add(curvatures.get(i)); significantPoints.add(i); } } @@ -140,74 +140,71 @@ public class SymmetryEstimator { Plane plane = new Plane(0, 0, 0, 0); int lastVotes = 0; - ProgressMonitor progressMonitor = null; - if (panel != null) { - UIManager.put("ProgressMonitor.progressText", "Counting symmetry..."); - progressMonitor = new ProgressMonitor(panel, "Counting...", "Steps", 0, significantPoints.size()); - } - - double onePercent = significantCurvatures.size() / 100.0; - double percentsPerStep = 1 / onePercent; for (int i = 0; i < significantPoints.size(); i++) { for (int j = 0; j < significantPoints.size(); j++) { - if (i != j) { - double minRatio = config.getMinCurvRatio(); - double maxRatio = 1.0 / minRatio; - if (significantCurvatures.get(i) / significantCurvatures.get(j) >= minRatio && - significantCurvatures.get(i) / significantCurvatures.get(j) <= maxRatio) { - - Vector3d p1 = new Vector3d(facet.getVertex(significantPoints.get(i)).getPosition()); - Vector3d p2 = new Vector3d(facet.getVertex(significantPoints.get(j)).getPosition()); + if (i == j) { + continue; + } + + double minRatio = config.getMinCurvRatio(); + double maxRatio = 1.0 / minRatio; + if (significantCurvatures.get(i) / significantCurvatures.get(j) < minRatio || + significantCurvatures.get(i) / significantCurvatures.get(j) > maxRatio) { + continue; + } + + MeshPoint meshPointI = facet.getVertex(significantPoints.get(i)); + MeshPoint meshPointJ = facet.getVertex(significantPoints.get(j)); + + Vector3d p1 = new Vector3d(meshPointI.getPosition()); + Vector3d p2 = new Vector3d(meshPointJ.getPosition()); - Vector3d avrg = new Vector3d(p1); - avrg.add(p2); - avrg.scale(0.5); + Vector3d avrg = new Vector3d(p1); + avrg.add(p2); + avrg.scale(0.5); - Vector3d normal = new Vector3d(p1); - normal.sub(p2); - normal.normalize(); + Vector3d normal = new Vector3d(p1); + normal.sub(p2); + normal.normalize(); - double d = -(normal.x * avrg.x) - (normal.y * avrg.y) - (normal.z * avrg.z); + double d = -(normal.x * avrg.x) - (normal.y * avrg.y) - (normal.z * avrg.z); - Vector3d ni = new Vector3d(facet.getVertex(significantPoints.get(i)).getNormal()); - Vector3d nj = new Vector3d(facet.getVertex(significantPoints.get(j)).getNormal()); - ni.normalize(); - nj.normalize(); + Vector3d ni = new Vector3d(meshPointI.getNormal()); + Vector3d nj = new Vector3d(meshPointI.getNormal()); + ni.normalize(); + nj.normalize(); - Vector3d normVec = ni; - normVec.sub(nj); - normVec.normalize(); - double normCos = normVec.dot(normal); + Vector3d normVec = new Vector3d(ni); + normVec.sub(nj); + normVec.normalize(); + double normCos = normVec.dot(normal); - if (Math.abs(normCos) >= config.getMinNormAngleCos()) { - Plane newPlane = new Plane(normal.x, normal.y, normal.z, d); - int currentVotes = getVotes(newPlane, - significantCurvatures, - significantPoints, - config.getMinCurvRatio(), - config.getMinAngleCos(), - config.getMinNormAngleCos(), - getBoundingBox().getMaxDiag() * config.getMaxRelDistance()); + if (Math.abs(normCos) < config.getMinNormAngleCos()) { + continue; + } + + Plane newPlane = new Plane(normal.x, normal.y, normal.z, d); + int currentVotes = getVotes( + newPlane, + significantCurvatures, + significantPoints, + config.getMinCurvRatio(), + config.getMinAngleCos(), + config.getMinNormAngleCos(), + getBoundingBox().getMaxDiag() * config.getMaxRelDistance() + ); - planes.add(new ApproxSymmetryPlane(newPlane, currentVotes)); - - if (currentVotes > lastVotes) { - lastVotes = currentVotes; - plane = newPlane; - } - } - - } + planes.add(new ApproxSymmetryPlane(newPlane, currentVotes)); + + if (currentVotes > lastVotes) { + lastVotes = currentVotes; + plane = newPlane; } } - if (panel != null) { - progressMonitor.setNote("Task step: " + (int) ((i + 1) * percentsPerStep)); - progressMonitor.setProgress(i); - } } Collections.sort(planes); - ArrayList<ApproxSymmetryPlane> finalPlanes = new ArrayList<>(); + List<ApproxSymmetryPlane> finalPlanes = new ArrayList<>(); for (int i = 0; i < planes.size(); i++) { if (planes.get(i).getVotes() == lastVotes){ finalPlanes.add(planes.get(i)); @@ -217,27 +214,47 @@ public class SymmetryEstimator { if (config.isAveraging()){ plane = finalPlane; } - if (panel != null) { - JOptionPane.showMessageDialog(panel, "Symmetry estimate done.", "Done", 0, - new ImageIcon(getClass().getResource("/cz/fidentis/analyst/gui/resources/exportedModel.png"))); - progressMonitor.close(); - } + return plane; } - private double[] initCurvatures() { - double[] curvatures = new double[facet.getNumberOfVertices()]; + private List<Double> initCurvatures(MeshFacet facet) { + CurvatureVisitor vis; + if (facet.getNumberOfVertices() < 2500) { + // searching for Maximum curvature in vertex using Gaussian curvature + // and Mean curvature leads to longer calculation but better results for real face models + //curvatures.add(getMaxCurvature(i)); + vis = new MaxCurvatureVisitor(false); + } else { + //curvatures.add(getGaussianCurvature(i)); + vis = new GaussianCurvatureVisitor(false); + } + facet.accept(vis); + List<Double> curvatures = new ArrayList<>(vis.getCurvatures().get(facet)); + for (int i = 0; i < curvatures.size(); i++) { + if (Double.isNaN(curvatures.get(i))) { + curvatures.set(i, Double.MIN_VALUE); // !!!! + } + } + return curvatures; + + /* for (int i = 0; i < facet.getNumberOfVertices(); i++) { - if (facet.getNumberOfVertices() == 2500) { - curvatures[i] = this.getMaxCurvature(i); + if (facet.getNumberOfVertices() < 2500) { + // searching for Maximum curvature in vertex using Gaussian curvature + // and Mean curvature leads to longer calculation but better results for real face models + //curvatures.add(getMaxCurvature(i)); + vis = new MaxCurvatureVisitor(false); } else { - curvatures[i] = this.getGaussianCurvature(i); + //curvatures.add(getGaussianCurvature(i)); + vis = new GaussianCurvatureVisitor(false); } - if (Double.isNaN(curvatures[i])){ - curvatures[i] = Double.MIN_VALUE; + if (Double.isNaN(curvatures.get(i))){ + curvatures.set(i, Double.MIN_VALUE); } } return curvatures; + */ } private Plane computeNewPlane(List<ApproxSymmetryPlane> finalPlanes) { @@ -364,302 +381,6 @@ public class SymmetryEstimator { return facet; } - /** - * Representation of areas of Voronoi region of triangles - * used for computing Gaussian curvature - * - * @author Natália Bebjaková - */ - private final class TriangleVertexAreas { - public final double v1Area; - public final double v2Area; - public final double v3Area; - - private TriangleVertexAreas(double v1, double v2, double v3) { - v1Area = v1; - v2Area = v2; - v3Area = v3; - } - } - - /** - * - * @param pointIndex index of point for which we are searching traingles in its neighborhood - * @return triangle neighbours of given index of vertex - */ - protected List<Integer> getNeighbours(int pointIndex) { - return facet.getCornerTable().getTriangleIndexesByVertexIndex(pointIndex); - } - - /** - * Return area of Voronoi region of triangle - * - * @param t triangle of which area is computed - * @return area of Voronoi region - */ - private TriangleVertexAreas computeTriangleVertexAreas(MeshTriangle t) { - double a = (t.vertex2.subtractPosition(t.vertex3)).abs(); - double b = (t.vertex3.subtractPosition(t.vertex1)).abs(); - double c = (t.vertex2.subtractPosition(t.vertex1)).abs(); - - double d1 = a * a * (b * b + c * c - a * a); - double d2 = b * b * (c * c + a * a - b * b); - double d3 = c * c * (a * a + b * b - c * c); - double dSum = d1 + d2 + d3; - - d1 /= dSum; - d2 /= dSum; - d3 /= dSum; - - MeshPoint v1Half = (t.vertex2.addPosition(t.vertex3)).dividePosition(2); - MeshPoint v2Half = (t.vertex1.addPosition(t.vertex3)).dividePosition(2); - MeshPoint v3Half = (t.vertex2.addPosition(t.vertex1)).dividePosition(2); - - - //TriangleVertexAreas area = new TriangleVertexAreas(); - - if (d1 < 0) { - double v3Area = ((v2Half.subtractPosition(t.vertex3)).crossProduct(v1Half.subtractPosition(t.vertex3))).abs() / 2.0; - double v2Area = ((v3Half.subtractPosition(t.vertex2)).crossProduct(v1Half.subtractPosition(t.vertex2))).abs() / 2.0; - double v1Area = (((v1Half.subtractPosition(t.vertex1)).crossProduct(v3Half.subtractPosition(t.vertex1))).abs() / 2.0) + - (((v1Half.subtractPosition(t.vertex1)).crossProduct(v2Half.subtractPosition(t.vertex1))).abs() / 2.0); - return new TriangleVertexAreas(v1Area, v2Area, v3Area); - } - if (d2 < 0) { - double v1Area = ((v3Half.subtractPosition(t.vertex1)).crossProduct(v2Half.subtractPosition(t.vertex1))).abs() / 2.0; - double v3Area = ((v1Half.subtractPosition(t.vertex3)).crossProduct(v2Half.subtractPosition(t.vertex3))).abs() / 2.0; - double v2Area = (((v2Half.subtractPosition(t.vertex2)).crossProduct(v1Half.subtractPosition(t.vertex2))).abs() / 2.0) + - (((v2Half.subtractPosition(t.vertex2)).crossProduct(v3Half.subtractPosition(t.vertex2))).abs() / 2.0); - return new TriangleVertexAreas(v1Area, v2Area, v3Area); - } - if (d3 < 0) { - double v2Area = ((v1Half.subtractPosition(t.vertex2)).crossProduct(v3Half.subtractPosition(t.vertex2))).abs() / 2.0; - double v1Area = ((v2Half.subtractPosition(t.vertex1)).crossProduct(v3Half.subtractPosition(t.vertex1))).abs() / 2.0; - double v3Area = (((v3Half.subtractPosition(t.vertex3)).crossProduct(v2Half.subtractPosition(t.vertex3))).abs() / 2.0) + - (((v3Half.subtractPosition(t.vertex3)).crossProduct(v1Half.subtractPosition(t.vertex3))).abs() / 2.0); - return new TriangleVertexAreas(v1Area, v2Area, v3Area); - } - - MeshPoint circumcenter = t.vertex1.multiplyPosition(d1).addPosition(t.vertex2.multiplyPosition(d2).addPosition(t.vertex3.multiplyPosition(d3))); - - double v1Area = (((v2Half.subtractPosition(t.vertex1)).crossProduct(circumcenter.subtractPosition(t.vertex1))).abs() / 2.0) + - (((v3Half.subtractPosition(t.vertex1)).crossProduct(circumcenter.subtractPosition(t.vertex1))).abs() / 2.0); - - double v2Area = (((v3Half.subtractPosition(t.vertex2)).crossProduct(circumcenter.subtractPosition(t.vertex2))).abs() / 2.0) + - (((v1Half.subtractPosition(t.vertex2)).crossProduct(circumcenter.subtractPosition(t.vertex2))).abs() / 2.0); - - double v3Area = (((v1Half.subtractPosition(t.vertex3)).crossProduct(circumcenter.subtractPosition(t.vertex3))).abs() / 2.0) + - (((v2Half.subtractPosition(t.vertex3)).crossProduct(circumcenter.subtractPosition(t.vertex3))).abs() / 2.0); - return new TriangleVertexAreas(v1Area, v2Area, v3Area); - } - - /** - * Calculates angle of the triangle according to center point - * - * @param centerPoint center point - * @param t triangle - * @return angle - */ - private double calculateTriangleAlfa(MeshPoint centerPoint, MeshTriangle t) { - double alfaCos = 0; - - if (t.vertex1 == centerPoint) { - MeshPoint e1 = t.vertex1.subtractPosition(t.vertex3); - e1 = e1.dividePosition(e1.abs()); - - MeshPoint e2 = t.vertex2.subtractPosition(t.vertex3); - e2 = e2.dividePosition(e2.abs()); - - alfaCos = e1.dotProduct(e2); - } else if (t.vertex2 == centerPoint) { - MeshPoint e1 = t.vertex2.subtractPosition(t.vertex1); - e1 = e1.dividePosition(e1.abs()); - - MeshPoint e2 = t.vertex3.subtractPosition(t.vertex1); - e2 = e2.dividePosition(e2.abs()); - - alfaCos = e1.dotProduct(e2); - } else if (t.vertex3 == centerPoint){ - MeshPoint e1 = t.vertex3.subtractPosition(t.vertex2); - e1 = e1.dividePosition(e1.abs()); - - MeshPoint e2 = t.vertex1.subtractPosition(t.vertex2); - e2 = e2.dividePosition(e2.abs()); - - alfaCos = e1.dotProduct(e2); - } - double alfa = Math.acos(alfaCos); - return 1 / Math.tan(alfa); - } - - /** - * Calculates angle of the triangle according to center point - * - * @param centerPoint center point - * @param t triangle - * @return angle - */ - private double calculateTriangleBeta(MeshPoint centerPoint, MeshTriangle t) { - double betaCos = 0; - - if (t.vertex1 == centerPoint) { - MeshPoint e1 = t.vertex1.subtractPosition(t.vertex2); - e1 = e1.dividePosition(e1.abs()); - - MeshPoint e2 = t.vertex3.subtractPosition(t.vertex2); - e2 = e2.dividePosition(e2.abs()); - - betaCos = e1.dotProduct(e2); - } else if (t.vertex2 == centerPoint) { - MeshPoint e1 = t.vertex2.subtractPosition(t.vertex3); - e1 = e1.dividePosition(e1.abs()); - - MeshPoint e2 = t.vertex1.subtractPosition(t.vertex3); - e2 = e2.dividePosition(e2.abs()); - - betaCos = e1.dotProduct(e2); - } else if (t.vertex3 == centerPoint){ - MeshPoint e1 = t.vertex3.subtractPosition(t.vertex1); - e1 = e1.dividePosition(e1.abs()); - - MeshPoint e2 = t.vertex2.subtractPosition(t.vertex1); - e2 = e2.dividePosition(e2.abs()); - - betaCos = e1.dotProduct(e2); - } - double beta = Math.acos(betaCos); - return 1 / Math.tan(beta); - } - - /** - * Calculates Laplacian - * - * @param centerIndex centerIndex - * @return laplacian - */ - private Vector3d calculateLaplacian(int centerIndex) { - List<Integer> trianglesNeighbours = getNeighbours(centerIndex); - - if (trianglesNeighbours.isEmpty()) { - return new Vector3d(); - } - - double areaSum = 0; - Vector3d pointSum = new Vector3d(); - for (int i = 0; i < trianglesNeighbours.size(); i++) { - MeshTriangle t = triangles.get(trianglesNeighbours.get(i)); - MeshTriangle tNext = triangles.get(trianglesNeighbours.get((i + 1) % trianglesNeighbours.size())); - double area = 0; - - //MeshPoint tVertex1 = facet.getVertices().get(t.vertex1); - //MeshPoint tVertex2 = facet.getVertices().get(t.vertex2); - //MeshPoint tVertex3 = facet.getVertices().get(t.vertex3); - MeshPoint centerPoint = facet.getVertices().get(centerIndex); - - Vector3d aux = new Vector3d(); - if (t.vertex1 == facet.getVertex(centerIndex)) { - area = areas[trianglesNeighbours.get(i)].v1Area; - aux = new Vector3d(t.vertex2.getPosition()); - } else if (t.vertex2 == facet.getVertex(centerIndex)) { - area = areas[trianglesNeighbours.get(i)].v2Area; - aux = new Vector3d(t.vertex3.getPosition()); - } else if (t.vertex3 == facet.getVertex(centerIndex)) { - area = areas[trianglesNeighbours.get(i)].v3Area; - aux = new Vector3d(t.vertex1.getPosition()); - } - aux.sub(centerPoint.getPosition()); - aux.scale((calculateTriangleAlfa(centerPoint, t) + calculateTriangleBeta(centerPoint, tNext)) / 2.0); - pointSum.add(aux); - areaSum += area; - } - pointSum.scale(1.0/areaSum); - return pointSum; - } - - /** - * Gaussian curvature in a vertex of a triangle mesh. - * It can only be estimated because triangle mesh is not a continuous - * but discrete representation of surface. - * - * @param centerIndex index of vertex in which gaussian curvature is computed - * @return Gaussian curvature in given vertex - */ - private double getGaussianCurvature(int centerIndex) { - - List<Integer> triangleNeighbours = this.getNeighbours(centerIndex); - - if (triangleNeighbours.isEmpty()) { - return Double.NaN; - } - double sum = 2 * Math.PI; - double areaSum = 0; - - for (int i = 0; i < triangleNeighbours.size(); i++) { - Vector3d v1 = new Vector3d(); - Vector3d v2 = new Vector3d(); - MeshTriangle t = triangles.get(triangleNeighbours.get(i)); - - double area = 0; - if (t.vertex1 == facet.getVertex(centerIndex)) { - v1 = new Vector3d(t.vertex2.getPosition()); - v2 = new Vector3d(t.vertex3.getPosition()); - v1.sub(t.vertex1.getPosition()); - v2.sub(t.vertex1.getPosition()); - - area = areas[triangleNeighbours.get(i)].v1Area; - } else if (t.vertex2 == facet.getVertex(centerIndex)) { - v1 = new Vector3d(t.vertex1.getPosition()); - v2 = new Vector3d(t.vertex3.getPosition()); - v1.sub(t.vertex2.getPosition()); - v2.sub(t.vertex2.getPosition()); - - area = areas[triangleNeighbours.get(i)].v2Area; - } else if (t.vertex3 == facet.getVertex(centerIndex)) { - v1 = new Vector3d(t.vertex2.getPosition()); - v2 = new Vector3d(t.vertex1.getPosition()); - v1.sub(t.vertex3.getPosition()); - v2.sub(t.vertex3.getPosition()); - - area = areas[triangleNeighbours.get(i)].v3Area; - } - - areaSum += area; - v1.normalize(); - v2.normalize(); - - sum -= Math.acos(v1.dot(v2)); - } - double value = sum * (1.0 / areaSum); - return value; - } - - /** - * - * @param centerIndex center index - * @return mean curvature - */ - private double getMeanCurvature(int centerIndex) { - return calculateLaplacian(centerIndex).length(); - } - - /** - * - * @param centerIndex center index - * @return max curvature - */ - private double getMaxCurvature(int centerIndex) { - double gaussianCurvature = getGaussianCurvature(centerIndex); - if (Double.isNaN(gaussianCurvature)) { - return Double.NaN; - } - double meanCurvature = getMeanCurvature(centerIndex); - double meanCurvatureSqr = meanCurvature * meanCurvature; - if (meanCurvatureSqr <= gaussianCurvature) { - return meanCurvature; - } - return meanCurvature + Math.sqrt(meanCurvatureSqr - gaussianCurvature); - } - /** * Computes votes for given plane * @@ -679,6 +400,7 @@ public class SymmetryEstimator { double minAngleCos, double minNormAngleCos, double maxDist) { + plane.normalize(); Vector3d normal = plane.getNormal(); @@ -687,9 +409,9 @@ public class SymmetryEstimator { int votes = 0; //List<Vector3d> normals = calculateNormals(); - if (!facet.hasVertexNormals()) { - facet.calculateVertexNormals(); - } + //if (!facet.hasVertexNormals()) { + // facet.calculateVertexNormals(); + //} for (int i = 0; i < curvatures.size(); i++) { for (int j = 0; j < curvatures.size(); j++) { diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CurvatureVisitor.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CurvatureVisitor.java new file mode 100644 index 0000000000000000000000000000000000000000..a6f2c06ddb4649a6e893561dc90bcad708a3e6c4 --- /dev/null +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CurvatureVisitor.java @@ -0,0 +1,57 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +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.MeshPoint; +import cz.fidentis.analyst.mesh.core.MeshTriangle; +import cz.fidentis.analyst.symmetry.SymmetryEstimator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.vecmath.Vector3d; + +/** + * + * @author oslejsek + */ +public abstract class CurvatureVisitor extends MeshVisitor { + + private Map<MeshFacet, List<Double>> curvatures = new HashMap<>(); + + /** + * Constructor. + * + * @param asynchronous If {@code true}, then asynchronous visitor is created. + */ + public CurvatureVisitor(boolean asynchronous) { + super(asynchronous); + } + + @Override + protected synchronized void visitMeshFacet(MeshFacet facet) { + if (curvatures.containsKey(facet)) { + return; // already visited facet + } + + curvatures.put(facet, new ArrayList<>()); + + List<MeshTriangle> triangles = facet.getTriangles(); + for (int i = 0; i < facet.getNumberOfVertices(); i++) { + curvatures.get(facet).add(calculateCurvature(facet, triangles, i)); + } + } + + public Map<MeshFacet, List<Double>> getCurvatures() { + return Collections.unmodifiableMap(curvatures); + } + + protected abstract double calculateCurvature(MeshFacet facet, List<MeshTriangle> triangles, int centerIndex); + +} diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/GaussianCurvatureVisitor.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/GaussianCurvatureVisitor.java new file mode 100644 index 0000000000000000000000000000000000000000..f6dd505146f0f9b029587301f55c0d0902a7f7b3 --- /dev/null +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/GaussianCurvatureVisitor.java @@ -0,0 +1,77 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package cz.fidentis.analyst.visitors.mesh; + +import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshTriangle; +import java.util.List; +import javax.vecmath.Vector3d; + +/** + * + * @author oslejsek + */ +public class GaussianCurvatureVisitor extends CurvatureVisitor { + + /** + * Constructor. + * + * @param asynchronous If {@code true}, then asynchronous visitor is created. + */ + public GaussianCurvatureVisitor(boolean asynchronous) { + super(asynchronous); + } + + protected double calculateCurvature(MeshFacet facet, List<MeshTriangle> triangles, int centerIndex) { + List<Integer> neighbouringTriangles = facet.getCornerTable().getTriangleIndexesByVertexIndex(centerIndex); + + if (neighbouringTriangles.isEmpty()) { + return Double.NaN; + } + + double sum = 2 * Math.PI; + double areaSum = 0; + + // fro all surrounding triangles: + List<Vector3d> voronoiPoints = facet.calculateVoronoiPoints(); + for (int i = 0; i < neighbouringTriangles.size(); i++) { + Vector3d v1 = new Vector3d(); + Vector3d v2 = new Vector3d(); + MeshTriangle tri = triangles.get(neighbouringTriangles.get(i)); + Vector3d voronoiPoint = voronoiPoints.get(neighbouringTriangles.get(i)); + + double area = 0; + if (tri.vertex1 == facet.getVertex(centerIndex)) { + v1 = new Vector3d(tri.vertex2.getPosition()); + v2 = new Vector3d(tri.vertex3.getPosition()); + v1.sub(tri.vertex1.getPosition()); + v2.sub(tri.vertex1.getPosition()); + area = voronoiPoint.x; + } else if (tri.vertex2 == facet.getVertex(centerIndex)) { + v1 = new Vector3d(tri.vertex1.getPosition()); + v2 = new Vector3d(tri.vertex3.getPosition()); + v1.sub(tri.vertex2.getPosition()); + v2.sub(tri.vertex2.getPosition()); + area = voronoiPoint.y; + } else if (tri.vertex3 == facet.getVertex(centerIndex)) { + v1 = new Vector3d(tri.vertex2.getPosition()); + v2 = new Vector3d(tri.vertex1.getPosition()); + v1.sub(tri.vertex3.getPosition()); + v2.sub(tri.vertex3.getPosition()); + area = voronoiPoint.z; + } + + areaSum += area; + v1.normalize(); + v2.normalize(); + + sum -= Math.acos(v1.dot(v2)); + } + double value = sum * (1.0 / areaSum); + + return value; + } +} diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MaxCurvatureVisitor.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MaxCurvatureVisitor.java new file mode 100644 index 0000000000000000000000000000000000000000..d5018e0837fa3e36c4b2a30747bdea7d87c735c7 --- /dev/null +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MaxCurvatureVisitor.java @@ -0,0 +1,168 @@ +package cz.fidentis.analyst.visitors.mesh; + +import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshPoint; +import cz.fidentis.analyst.mesh.core.MeshTriangle; +import java.util.List; +import javax.vecmath.Vector3d; + +/** + * + * @author oslejsek + */ +public class MaxCurvatureVisitor extends GaussianCurvatureVisitor{ + + /** + * Constructor. + * + * @param asynchronous If {@code true}, then asynchronous visitor is created. + */ + public MaxCurvatureVisitor(boolean asynchronous) { + super(asynchronous); + } + + protected double calculateCurvature(MeshFacet facet, List<MeshTriangle> triangles, int centerIndex) { + double gaussianCurvature = super.calculateCurvature(facet, triangles, centerIndex); + if (Double.isNaN(gaussianCurvature)) { + return Double.NaN; + } + double meanCurvature = getMeanCurvature(facet, triangles, centerIndex); + double meanCurvatureSqr = meanCurvature * meanCurvature; + if (meanCurvatureSqr <= gaussianCurvature) { + return meanCurvature; + } + return meanCurvature + Math.sqrt(meanCurvatureSqr - gaussianCurvature); + } + + /** + * + * @param centerIndex center index + * @return mean curvature + */ + private double getMeanCurvature(MeshFacet facet, List<MeshTriangle> triangles, int centerIndex) { + return calculateLaplacian(facet, triangles, centerIndex).length(); + } + + /** + * Calculates Laplacian + * + * @param centerIndex centerIndex + * @return laplacian + */ + private Vector3d calculateLaplacian(MeshFacet facet, List<MeshTriangle> triangles, int centerIndex) { + List<Integer> neighbouringTriangles = facet.getCornerTable().getTriangleIndexesByVertexIndex(centerIndex); + + if (neighbouringTriangles.isEmpty()) { + return new Vector3d(); + } + + double areaSum = 0; + Vector3d pointSum = new Vector3d(); + List<Vector3d> voronoiPoints = facet.calculateVoronoiPoints(); + + for (int i = 0; i < neighbouringTriangles.size(); i++) { + MeshTriangle t = triangles.get(neighbouringTriangles.get(i)); + MeshTriangle tNext = triangles.get(neighbouringTriangles.get((i + 1) % neighbouringTriangles.size())); + double area = 0; + + MeshPoint centerPoint = facet.getVertices().get(centerIndex); + Vector3d voronoiPoint = voronoiPoints.get(neighbouringTriangles.get(i)); + + Vector3d aux = new Vector3d(); + if (t.vertex1 == facet.getVertex(centerIndex)) { + area = voronoiPoint.x; + aux = new Vector3d(t.vertex2.getPosition()); + } else if (t.vertex2 == facet.getVertex(centerIndex)) { + area = voronoiPoint.y; + aux = new Vector3d(t.vertex3.getPosition()); + } else if (t.vertex3 == facet.getVertex(centerIndex)) { + area = voronoiPoint.z; + aux = new Vector3d(t.vertex1.getPosition()); + } + aux.sub(centerPoint.getPosition()); + aux.scale((calculateTriangleAlfa(centerPoint, t) + calculateTriangleBeta(centerPoint, tNext)) / 2.0); + pointSum.add(aux); + areaSum += area; + } + pointSum.scale(1.0/areaSum); + return pointSum; + } + + /** + * Calculates angle of the triangle according to center point + * + * @param centerPoint center point + * @param t triangle + * @return angle + */ + private double calculateTriangleAlfa(MeshPoint centerPoint, MeshTriangle t) { + double alfaCos = 0; + + if (t.vertex1 == centerPoint) { + MeshPoint e1 = t.vertex1.subtractPosition(t.vertex3); + e1 = e1.dividePosition(e1.abs()); + + MeshPoint e2 = t.vertex2.subtractPosition(t.vertex3); + e2 = e2.dividePosition(e2.abs()); + + alfaCos = e1.dotProduct(e2); + } else if (t.vertex2 == centerPoint) { + MeshPoint e1 = t.vertex2.subtractPosition(t.vertex1); + e1 = e1.dividePosition(e1.abs()); + + MeshPoint e2 = t.vertex3.subtractPosition(t.vertex1); + e2 = e2.dividePosition(e2.abs()); + + alfaCos = e1.dotProduct(e2); + } else if (t.vertex3 == centerPoint){ + MeshPoint e1 = t.vertex3.subtractPosition(t.vertex2); + e1 = e1.dividePosition(e1.abs()); + + MeshPoint e2 = t.vertex1.subtractPosition(t.vertex2); + e2 = e2.dividePosition(e2.abs()); + + alfaCos = e1.dotProduct(e2); + } + double alfa = Math.acos(alfaCos); + return 1 / Math.tan(alfa); + } + + /** + * Calculates angle of the triangle according to center point + * + * @param centerPoint center point + * @param t triangle + * @return angle + */ + private double calculateTriangleBeta(MeshPoint centerPoint, MeshTriangle t) { + double betaCos = 0; + + if (t.vertex1 == centerPoint) { + MeshPoint e1 = t.vertex1.subtractPosition(t.vertex2); + e1 = e1.dividePosition(e1.abs()); + + MeshPoint e2 = t.vertex3.subtractPosition(t.vertex2); + e2 = e2.dividePosition(e2.abs()); + + betaCos = e1.dotProduct(e2); + } else if (t.vertex2 == centerPoint) { + MeshPoint e1 = t.vertex2.subtractPosition(t.vertex3); + e1 = e1.dividePosition(e1.abs()); + + MeshPoint e2 = t.vertex1.subtractPosition(t.vertex3); + e2 = e2.dividePosition(e2.abs()); + + betaCos = e1.dotProduct(e2); + } else if (t.vertex3 == centerPoint){ + MeshPoint e1 = t.vertex3.subtractPosition(t.vertex1); + e1 = e1.dividePosition(e1.abs()); + + MeshPoint e2 = t.vertex2.subtractPosition(t.vertex1); + e2 = e2.dividePosition(e2.abs()); + + betaCos = e1.dotProduct(e2); + } + double beta = Math.acos(betaCos); + return 1 / Math.tan(beta); + } +} diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/TriangleListVisitor.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/TriangleListVisitor.java index bd136b7656ecff18eb8a7672ef1a923aa31d81e5..cc929f0cfc3a2e9d01408079ce1066df16f28591 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/TriangleListVisitor.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/TriangleListVisitor.java @@ -4,6 +4,7 @@ import cz.fidentis.analyst.mesh.MeshVisitor; import cz.fidentis.analyst.mesh.core.MeshFacet; import cz.fidentis.analyst.mesh.core.MeshTriangle; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -30,13 +31,13 @@ public class TriangleListVisitor extends MeshVisitor { } @Override - protected synchronized void visitMeshFacet(MeshFacet facet) { - for (MeshTriangle tri : facet) { - triangles.add(tri); + protected void visitMeshFacet(MeshFacet facet) { + synchronized (this) { + triangles.addAll(facet.getTriangles()); } } public List<MeshTriangle> getTriangles() { - return triangles; + return Collections.unmodifiableList(triangles); } } 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 5df388a191fcb8850905f26ae81fc0933fae9f2b..bcd476b777886e70c26514a84a515c20fa034439 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 @@ -27,6 +27,8 @@ public class MeshTriangle implements Iterable<MeshPoint> { public final int index2; public final int index3; + private Vector3d voronoiPoint; + /** * Creates new triangle * @@ -118,6 +120,10 @@ public class MeshTriangle implements Iterable<MeshPoint> { * @return the center of circumcircle */ public Vector3d getVoronoiPoint() { + if (voronoiPoint != null) { + return voronoiPoint; + } + double a = (vertex2.subtractPosition(vertex3)).abs(); double b = (vertex3.subtractPosition(vertex1)).abs(); double c = (vertex2.subtractPosition(vertex1)).abs(); @@ -141,21 +147,24 @@ public class MeshTriangle implements Iterable<MeshPoint> { double v2Area = ((v3Half.subtractPosition(vertex2)).crossProduct(v1Half.subtractPosition(vertex2))).abs() / 2.0; double v1Area = (((v1Half.subtractPosition(vertex1)).crossProduct(v3Half.subtractPosition(vertex1))).abs() / 2.0) + (((v1Half.subtractPosition(vertex1)).crossProduct(v2Half.subtractPosition(vertex1))).abs() / 2.0); - return new Vector3d(v1Area, v2Area, v3Area); + voronoiPoint = new Vector3d(v1Area, v2Area, v3Area); + return voronoiPoint; } if (d2 < 0) { double v1Area = ((v3Half.subtractPosition(vertex1)).crossProduct(v2Half.subtractPosition(vertex1))).abs() / 2.0; double v3Area = ((v1Half.subtractPosition(vertex3)).crossProduct(v2Half.subtractPosition(vertex3))).abs() / 2.0; double v2Area = (((v2Half.subtractPosition(vertex2)).crossProduct(v1Half.subtractPosition(vertex2))).abs() / 2.0) + (((v2Half.subtractPosition(vertex2)).crossProduct(v3Half.subtractPosition(vertex2))).abs() / 2.0); - return new Vector3d(v1Area, v2Area, v3Area); + voronoiPoint = new Vector3d(v1Area, v2Area, v3Area); + return voronoiPoint; } if (d3 < 0) { double v2Area = ((v1Half.subtractPosition(vertex2)).crossProduct(v3Half.subtractPosition(vertex2))).abs() / 2.0; double v1Area = ((v2Half.subtractPosition(vertex1)).crossProduct(v3Half.subtractPosition(vertex1))).abs() / 2.0; double v3Area = (((v3Half.subtractPosition(vertex3)).crossProduct(v2Half.subtractPosition(vertex3))).abs() / 2.0) + (((v3Half.subtractPosition(vertex3)).crossProduct(v1Half.subtractPosition(vertex3))).abs() / 2.0); - return new Vector3d(v1Area, v2Area, v3Area); + voronoiPoint = new Vector3d(v1Area, v2Area, v3Area); + return voronoiPoint; } MeshPoint circumcenter = vertex1.multiplyPosition(d1).addPosition(vertex2.multiplyPosition(d2).addPosition(vertex3.multiplyPosition(d3))); @@ -168,7 +177,9 @@ public class MeshTriangle implements Iterable<MeshPoint> { double v3Area = (((v1Half.subtractPosition(vertex3)).crossProduct(circumcenter.subtractPosition(vertex3))).abs() / 2.0) + (((v2Half.subtractPosition(vertex3)).crossProduct(circumcenter.subtractPosition(vertex3))).abs() / 2.0); - return new Vector3d(v1Area, v2Area, v3Area); + voronoiPoint = new Vector3d(v1Area, v2Area, v3Area); + + return voronoiPoint; } @Override