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 0507e945fc054d4d25c6fd66b35143618acadf20..2eb6eb4ecbcbb63424915ec6c7ad5fda50284b8c 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java @@ -3,10 +3,11 @@ package cz.fidentis.analyst.symmetry; import cz.fidentis.analyst.mesh.core.CornerTableRow; import cz.fidentis.analyst.mesh.core.MeshFacet; import cz.fidentis.analyst.mesh.core.MeshPoint; -import cz.fidentis.analyst.mesh.core.BoundingBox; +import cz.fidentis.analyst.mesh.visitors.BoundingBox; 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.mesh.visitors.BoundingBoxVisitor; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -15,11 +16,6 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.ProgressMonitor; import javax.swing.UIManager; -//import javax.swing.ImageIcon; -//import javax.swing.JOptionPane; -//import javax.swing.JPanel; -//import javax.swing.ProgressMonitor; -//import javax.swing.UIManager; import javax.vecmath.Vector3d; /** @@ -40,8 +36,6 @@ public class SymmetryEstimator { private List<MeshTriangle> triangles; // Helping array of triangles computed from corner table - //private JPanel panel; // panel for configuration of symmetry counting - private final Config config; /** @@ -60,8 +54,6 @@ public class SymmetryEstimator { for (int i = 0; i < areas.length; i++) { areas[i] = computeTriangleVertexAreas(triangles.get(i)); } - - boundingBox = facet.getBoundingBox(); } /** @@ -83,22 +75,6 @@ public class SymmetryEstimator { return config; } - /** - * - * @return panel for configuration of symmetry counting - */ - //public JPanel getPanel() { - // return panel; - //} - - /** - * - * @param panel new panel for configuration of symmetry counting - */ - //public void setPanel(JPanel panel) { - // this.panel = panel; - //} - /** * * @return Facet of the model on which symmetry is computed @@ -108,13 +84,15 @@ public class SymmetryEstimator { } /** - * If bounding box is not created yet, it creates new one. + * If bounding box is not created yet, it's created now. * * @return Represent min-max box of the boundries of the model. */ public BoundingBox getBoundingBox() { if (boundingBox == null) { - boundingBox = facet.getBoundingBox(); + BoundingBoxVisitor visitor = new BoundingBoxVisitor(); + facet.accept(visitor); + boundingBox = visitor.getBoundingBox(); } return boundingBox; } @@ -126,8 +104,6 @@ public class SymmetryEstimator { */ public Plane getApproxSymmetryPlane(JPanel panel) { - ///UIManager.put("ProgressMonitor.progressText", "Counting symmetry..."); - ArrayList<ApproxSymmetryPlane> planes = new ArrayList<>(); //List<Vector3d> normals = calculateNormals(); if (!facet.hasVertexNormals()) { @@ -215,7 +191,7 @@ public class SymmetryEstimator { config.getMinCurvRatio(), config.getMinAngleCos(), config.getMinNormAngleCos(), - boundingBox.getMaxDiag() * config.getMaxRelDistance()); + getBoundingBox().getMaxDiag() * config.getMaxRelDistance()); planes.add(new ApproxSymmetryPlane(newPlane, currentVotes)); @@ -241,7 +217,6 @@ public class SymmetryEstimator { finalPlanes.add(planes.get(i)); } } - //Plane finalPlane = new Plane(0, 0, 0, 0); double newA = 0, newB = 0, newC = 0, newD = 0; Vector3d refDir = finalPlanes.get(0).getNormal(); for (int i = 0; i < finalPlanes.size(); i++) { @@ -279,7 +254,7 @@ public class SymmetryEstimator { */ public SymmetryEstimator mergeWithPlane(Plane plane) { Vector3d normal = plane.getNormal(); - Vector3d midPoint = boundingBox.getMidPoint().getPosition(); + Vector3d midPoint = getBoundingBox().getMidPoint().getPosition(); double alpha = -((normal.x * midPoint.x) + (normal.y * midPoint.y) + (normal.z * midPoint.z) + @@ -307,7 +282,7 @@ public class SymmetryEstimator { b.normalize(); SymmetryEstimator planeMesh = new SymmetryEstimator(midPointOnPlane, a, b, - (boundingBox.getMaxPoint().subtractPosition(boundingBox.getMinPoint())).getPosition().x); + (getBoundingBox().getMaxPoint().subtractPosition(getBoundingBox().getMinPoint())).getPosition().x); return mergeMeshWith(planeMesh); } diff --git a/MeshModel/pom.xml b/MeshModel/pom.xml index d440dda45e4d3592806c68ae9d97b0a38f7be3f2..852d4a5ad2a12aa4a5ba11ea089af4248737b816 100644 --- a/MeshModel/pom.xml +++ b/MeshModel/pom.xml @@ -19,6 +19,7 @@ <publicPackages> <!-- expose API/packages to other modules --> <publicPackage>cz.fidentis.analyst.mesh.core.*</publicPackage> <publicPackage>cz.fidentis.analyst.mesh.io.*</publicPackage> + <publicPackage>cz.fidentis.analyst.mesh.visitors.*</publicPackage> <!--<publicPackage>cz.fidentis.analyst.mesh.core.MeshFacet</publicPackage>--> <!--<publicPackage>cz.fidentis.analyst.mesh.core.MeshPoint</publicPackage>--> </publicPackages> 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 3a70047917c3b62a2c4d31d76a81b2c5ddf36a8f..942f4a023385ce89ad98309159a01d3c24f6f5e1 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 @@ -1,5 +1,7 @@ package cz.fidentis.analyst.mesh.core; +import cz.fidentis.analyst.mesh.visitors.BoundingBox; +import cz.fidentis.analyst.mesh.visitors.Visitor; import java.util.List; /** @@ -51,12 +53,6 @@ public interface MeshFacet { */ List<MeshTriangle> asTriangles(); - /** - * Computes and returns bounding box of the mesh facet. - * @return bounding box of the mesh facet. - */ - BoundingBox getBoundingBox(); - /** * Returns true if normals of vertices are calculated. * @return true if normals of vertices are calculated. @@ -67,4 +63,11 @@ public interface MeshFacet { * Calculates normals of vertices from normals of triangles. */ void calculateVertexNormals(); + + /** + * Entry point for visitors. + * + * @param visitor Visitor + */ + public void accept(Visitor visitor); } 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 f0c12ab8bbd2aeb0b9b790d89a46a91b79acccaf..72873fc99e633629754b640b7343ac9b9e6348d8 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 @@ -1,5 +1,7 @@ package cz.fidentis.analyst.mesh.core; +import cz.fidentis.analyst.mesh.visitors.BoundingBox; +import cz.fidentis.analyst.mesh.visitors.Visitor; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -33,6 +35,11 @@ public class MeshFacetImpl implements MeshFacet { vertices.addAll(facet.getVertices()); // encapsulation preserved - vertices MeshPoints are immutable) cornerTable = new CornerTable(facet.getCornerTable()); } + + @Override + public void accept(Visitor visitor) { + visitor.visitMeshFacet(this); + } @Override public MeshPoint getVertex(int index) { @@ -71,11 +78,6 @@ public class MeshFacetImpl implements MeshFacet { return ret; } - @Override - public BoundingBox getBoundingBox() { - return new BoundingBox(this.vertices); - } - @Override public boolean hasVertexNormals() { return !this.vertices.isEmpty() && this.vertices.get(0).getNormal() != null; diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshModel.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshModel.java index 9ab6d00ce755367b76da0f4562f8d298021c0d6d..eaa796cf2f24cf15d03a742f246b8fa432f5d7df 100644 --- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshModel.java +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshModel.java @@ -1,5 +1,6 @@ package cz.fidentis.analyst.mesh.core; +import cz.fidentis.analyst.mesh.visitors.Visitor; import java.util.ArrayList; import java.util.List; @@ -9,7 +10,7 @@ import java.util.List; * @author Matej Lukes */ public class MeshModel { - private List<MeshFacet> facets = new ArrayList<>(); + private final List<MeshFacet> facets = new ArrayList<>(); /** * Constructor of MeshModel @@ -46,4 +47,13 @@ public class MeshModel { public void addFacet(MeshFacet facet) { facets.add(facet); } + + /** + * Entry point for visitors. + * + * @param visitor Visitor + */ + public void accept(Visitor visitor) { + visitor.visitMeshModel(this); + } } diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshPointImpl.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshPointImpl.java index 8284ac9a209f679198abdfdff8ce2a645b09c270..9d845de12bec745690266d34ee8e7b67e9f37da1 100644 --- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshPointImpl.java +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshPointImpl.java @@ -9,7 +9,7 @@ import javax.vecmath.Vector3d; */ public class MeshPointImpl implements MeshPoint { - private final Vector3d position, normal, texCoord; + private Vector3d position, normal, texCoord; /** * constructor of meshPoint @@ -20,12 +20,18 @@ public class MeshPointImpl implements MeshPoint { */ public MeshPointImpl(Vector3d position, Vector3d normal, Vector3d texCoord) { if (position == null) { - throw new NullPointerException("position cannot be null"); + throw new IllegalArgumentException("position cannot be null"); + } else { + this.position = new Vector3d(position); } - - this.position = new Vector3d(position); - this.normal = new Vector3d(normal); - this.texCoord = new Vector3d(texCoord); + + if (normal != null) { + this.normal = new Vector3d(normal); + } + + if (texCoord != null) { + this.texCoord = new Vector3d(texCoord); + } } /** @@ -197,4 +203,10 @@ public class MeshPointImpl implements MeshPoint { return position.hashCode() + normal.hashCode() + texCoord.hashCode(); } + @Override + public String toString() { + return "position " + position + ", " + + "normal " + normal + ", " + + "tex-coord " + texCoord; + } } diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/BoundingBox.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/visitors/BoundingBox.java similarity index 77% rename from MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/BoundingBox.java rename to MeshModel/src/main/java/cz/fidentis/analyst/mesh/visitors/BoundingBox.java index 733d5d844cdbf0b1a40279a28e5bfc4528d9a159..4f683737d34e1352ed16f72feeb80b162e3929e6 100644 --- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/BoundingBox.java +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/visitors/BoundingBox.java @@ -1,5 +1,7 @@ -package cz.fidentis.analyst.mesh.core; +package cz.fidentis.analyst.mesh.visitors; +import cz.fidentis.analyst.mesh.core.MeshPoint; +import cz.fidentis.analyst.mesh.core.MeshPointImpl; import java.util.List; import javax.vecmath.Vector3d; @@ -11,13 +13,12 @@ import javax.vecmath.Vector3d; public class BoundingBox { - private final MeshPoint maxPoint; - private final MeshPoint minPoint; - private final MeshPoint midPoint; - private final double maxDiag; + private MeshPoint maxPoint; + private MeshPoint minPoint; /** * Creates bounding box from given mesh points. + * * @param points List of mesh points, must not be null or empty * @throws IllegalArgumentException if the @code{points} param is null or empty */ @@ -26,9 +27,22 @@ public class BoundingBox { throw new IllegalArgumentException("points"); } - minPoint = new MeshPointImpl(new Vector3d(Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY,Double.POSITIVE_INFINITY), null, null); + minPoint = new MeshPointImpl(new Vector3d(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY), null, null); maxPoint = new MeshPointImpl(new Vector3d(Double.NEGATIVE_INFINITY,Double.NEGATIVE_INFINITY,Double.NEGATIVE_INFINITY), null, null); - + + compute(points); + } + + /** + * Creates bounding box from given mesh points, possibly extends the existing BB. + * + * @param points List of mesh points, must not be null or empty + */ + public void compute(List<MeshPoint> points) { + if (points == null || points.isEmpty()) { + return; + } + for (int i = 0; i < points.size(); i++) { MeshPoint point = points.get(i); @@ -40,10 +54,6 @@ public class BoundingBox { maxPoint.getPosition().y = Math.max(maxPoint.getPosition().y, point.getPosition().y); maxPoint.getPosition().z = Math.max(maxPoint.getPosition().z, point.getPosition().z); } - - midPoint = (minPoint.addPosition(maxPoint)).multiplyPosition(0.5); - MeshPoint diag = maxPoint.subtractPosition(minPoint); - this.maxDiag = diag.abs(); } /** @@ -59,7 +69,7 @@ public class BoundingBox { * @return middle point of the bounding box */ public MeshPoint getMidPoint() { - return midPoint; + return (minPoint.addPosition(maxPoint)).multiplyPosition(0.5); } /** @@ -75,7 +85,7 @@ public class BoundingBox { * @return maximal diagonal of bounding box */ public double getMaxDiag() { - return maxDiag; + return maxPoint.subtractPosition(minPoint).abs(); } /** @@ -89,8 +99,6 @@ public class BoundingBox { str += System.lineSeparator(); str += "\t" + "- min point : " + this.minPoint + System.lineSeparator(); str += "\t" + "- max point : " + this.maxPoint + System.lineSeparator(); - str += "\t" + "- mid point : " + this.midPoint + System.lineSeparator(); - str += "\t" + "- max diag : " + this.maxDiag + System.lineSeparator(); return str; } } \ No newline at end of file diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/visitors/BoundingBoxVisitor.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/visitors/BoundingBoxVisitor.java new file mode 100644 index 0000000000000000000000000000000000000000..848848151917c1db524c0b10d16b2e8cef6d014a --- /dev/null +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/visitors/BoundingBoxVisitor.java @@ -0,0 +1,31 @@ +package cz.fidentis.analyst.mesh.visitors; + +import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshModel; + +/** + * Computes a 3D bounding box (cube). + * + * @author oslejsek + */ +public class BoundingBoxVisitor implements Visitor { + + private BoundingBox bbox; + + @Override + public void visitMeshFacet(MeshFacet facet) { + if (bbox == null) { + bbox = new BoundingBox(facet.getVertices()); + } else { + bbox.compute(facet.getVertices()); + } + } + + /** + * Returns computed bounding box. + * @return Bounding box or null + */ + public BoundingBox getBoundingBox() { + return bbox; + } +} diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/visitors/Visitor.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/visitors/Visitor.java new file mode 100644 index 0000000000000000000000000000000000000000..cf8401b9329240af765cca3055e76e18ae81b0f4 --- /dev/null +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/visitors/Visitor.java @@ -0,0 +1,31 @@ +package cz.fidentis.analyst.mesh.visitors; + +import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshModel; + +/** + * Visitor for the traversal of @code{MeshModel} and @code{MeshFacet}. + * + * @author oslejsek + */ +public interface Visitor { + + + /** + * Visits a mesh model. + * + * @param model Mesh model to be visited. + */ + default void visitMeshModel(MeshModel model) { + for (MeshFacet facet: model.getFacets()) { + facet.accept(this); + } + } + + /** + * Visits a mesh facet. + * + * @param facet Mesh facet to be visited. + */ + void visitMeshFacet(MeshFacet facet); +} diff --git a/MeshModel/src/test/java/cz/fidentis/analyst/mesh/visitors/BoundingBoxVisitorTest.java b/MeshModel/src/test/java/cz/fidentis/analyst/mesh/visitors/BoundingBoxVisitorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4d693d11fc7e494e90c4e75dd40b57acc474e74f --- /dev/null +++ b/MeshModel/src/test/java/cz/fidentis/analyst/mesh/visitors/BoundingBoxVisitorTest.java @@ -0,0 +1,50 @@ +package cz.fidentis.analyst.mesh.visitors; + +import cz.fidentis.analyst.mesh.core.MeshModel; +import cz.fidentis.analyst.mesh.io.MeshObjLoader; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import javax.vecmath.Vector3d; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Radek Oslejsek + */ +public class BoundingBoxVisitorTest { + + Path testFileDirectory = Paths.get("src", "test", "resources", "cz", "fidentis", "analyst", "mesh", "io"); + + @Test + void moreObjectFileTest() { //TODO: Not testing in loader + File moreObjects = new File(testFileDirectory.toFile(), "MoreObjects.obj"); + Exception ex = assertThrows(IOException.class, () -> MeshObjLoader.read(moreObjects)); + } + + @Test + void icoSphereTest() throws IOException { + MeshModel m = MeshObjLoader.read(new File(testFileDirectory.toFile(), "IcoSphere-20.obj")); + assertNotNull(m); + BoundingBoxVisitor visitor = new BoundingBoxVisitor(); + m.accept(visitor); + assertEquals(new Vector3d(0.8944249749183655, 1.0, 0.8506399989128113), visitor.getBoundingBox().getMaxPoint().getPosition()); + assertEquals(new Vector3d(-0.8944249749183655, -1.0, -0.8506399989128113), visitor.getBoundingBox().getMinPoint().getPosition()); + } + + @Test + void combinedTest() throws IOException { + MeshModel m = MeshObjLoader.read(new File(testFileDirectory.toFile(), "IcoSphere-20.obj")); + assertNotNull(m); + BoundingBoxVisitor visitor = new BoundingBoxVisitor(); + m.accept(visitor); + + m = MeshObjLoader.read(new File(testFileDirectory.toFile(), "Tetrahedron.obj")); + assertNotNull(m); + m.accept(visitor); + + assertEquals(new Vector3d(1.0, 1.0, 1.0), visitor.getBoundingBox().getMaxPoint().getPosition()); + assertEquals(new Vector3d(-1.0, -1.0, -1.0), visitor.getBoundingBox().getMinPoint().getPosition()); + } +}