diff --git a/Comparison/src/main/java/cz/fidentis/analyst/BatchProcessor.java b/Comparison/src/main/java/cz/fidentis/analyst/BatchProcessor.java index 1adce12f5c10f12798eddbb13e3809b29a6e5299..1d8f26378635695932a09f2c6631e91a57ec6885 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/BatchProcessor.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/BatchProcessor.java @@ -71,7 +71,6 @@ public class BatchProcessor { HumanFace face = HumanFaceFactory.instance().getFace(faceId); SymmetryEstimator se = new SymmetryEstimator(this.symmetryConfig); face.getMeshModel().compute(se); - face.setSymmetryPlane(se.getSymmetryPlane(), se.getSymmetryPlaneMesh()); } } diff --git a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java index 4ad68932a1073c8ac3c5490e7b35db98bb34e6f1..54f54923256c3b9c93ccbd356f56d38e70482b02 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java @@ -10,11 +10,13 @@ import cz.fidentis.analyst.face.events.KdTreeCreated; import cz.fidentis.analyst.face.events.KdTreeDestroyed; import cz.fidentis.analyst.face.events.MeshChangedEvent; import cz.fidentis.analyst.face.events.SymmetryPlaneChangedEvent; -import cz.fidentis.analyst.mesh.core.MeshFacet; import cz.fidentis.analyst.mesh.core.MeshModel; +import cz.fidentis.analyst.mesh.core.MeshRectangleFacet; import cz.fidentis.analyst.mesh.io.MeshObjLoader; import cz.fidentis.analyst.symmetry.Plane; import cz.fidentis.analyst.visitors.face.HumanFaceVisitor; +import cz.fidentis.analyst.visitors.mesh.BoundingBox; +import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -54,7 +56,8 @@ public class HumanFace implements Serializable { private Plane symmetryPlane; - private MeshFacet symmetryPlaneMesh; + //private MeshFacet symmetryPlaneMesh; + private BBox bbox; private List<FeaturePoint> featurePoints; @@ -78,6 +81,10 @@ public class HumanFace implements Serializable { meshModel = MeshObjLoader.read(new FileInputStream(file)); meshModel.simplifyModel(); this.id = file.getCanonicalPath(); + + BoundingBox visitor = new BoundingBox(); + meshModel.compute(visitor); + bbox = visitor.getBoundingBox(); } /** @@ -101,6 +108,11 @@ public class HumanFace implements Serializable { throw new IllegalArgumentException("meshModel"); } this.meshModel = meshModel; + + BoundingBox visitor = new BoundingBox(); + meshModel.compute(visitor); + bbox = visitor.getBoundingBox(); + announceEvent(new MeshChangedEvent(this, getShortName(), this)); } @@ -135,21 +147,13 @@ public class HumanFace implements Serializable { } /** - * Sets the symmetry plane. If both arguments are {@code null}, then removes the plane. + * Sets the symmetry plane. If the input argument is {@code null}, then removes the plane. * Triggers {@link cz.fidentis.analyst.face.events.SymmetryPlaneChangedEvent}. * * @param plane The new symmetry plane; Must not be {@code null} - * @param planeFacet The symmetry plane mesh; Must not be {@code null} */ - public void setSymmetryPlane(Plane plane, MeshFacet planeFacet) { - if (plane == null) { - throw new IllegalArgumentException("plane"); - } - if (planeFacet == null) { - throw new IllegalArgumentException("planeFacet"); - } + public void setSymmetryPlane(Plane plane) { this.symmetryPlane = plane; - this.symmetryPlaneMesh = planeFacet; this.announceEvent(new SymmetryPlaneChangedEvent(this, getShortName(), this)); } @@ -161,8 +165,20 @@ public class HumanFace implements Serializable { return symmetryPlane; } - public MeshFacet getSymmetryPlaneFacet() { - return this.symmetryPlaneMesh; + /** + * Returns rectangular mesh facet of the symmetry plane, if exists. + * @return a rectangular mesh facet of the symmetry plane or {@code null} + */ + public MeshRectangleFacet getSymmetryPlaneFacet() { + return (symmetryPlane == null) ? null : symmetryPlane.getMesh(bbox); + } + + /** + * Returns bounding box of this face. + * @return bounding box of this face. + */ + public BBox getBoundingBox() { + return bbox; } /** diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java index 6a46bc19962b9e0f6ca30397b137eea8390978e7..4516b41eec3f7160e1abc635bb502085860e4f51 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java @@ -1,5 +1,7 @@ package cz.fidentis.analyst.symmetry; +import cz.fidentis.analyst.mesh.core.MeshRectangleFacet; +import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox; import java.io.Serializable; import java.util.List; import javax.vecmath.Point3d; @@ -33,7 +35,7 @@ public class Plane implements Serializable { * Constructor. * @param normal Normal vector of the plane * @param dist distance - * @throws IllegalArgumentExpcption if the @code{plane} argument is null + * @throws IllegalArgumentException if the @code{plane} argument is null */ public Plane(Vector3d normal, double dist) { this(normal.x, normal.y, normal.z, dist); @@ -42,7 +44,7 @@ public class Plane implements Serializable { /** * Copy constructor. * @param plane original plane - * @throws IllegalArgumentExpcption if the @code{plane} argument is null + * @throws IllegalArgumentException if the @code{plane} argument is null */ public Plane(Plane plane) { this(plane.getNormal(), plane.getDistance()); @@ -52,7 +54,7 @@ public class Plane implements Serializable { * Creates average plane from existing planes. * * @param planes Source planes - * @throws {@code IllegalArgumentException} the {@code planes} list is {@code null} or empty + * @throws IllegalArgumentException if the {@code planes} list is {@code null} or empty */ public Plane(List<Plane> planes) { if (planes == null || planes.isEmpty()) { @@ -121,6 +123,64 @@ public class Plane implements Serializable { this.distance += value; } + /** + * Returns rectangular mesh facet of this symmetry plane. + * + * @param midPoint A 3D point, which is projected to the plane and used as a center of the rectangular facet + * @param width Width + * @param height Height + * @return a rectangular mesh facet of this symmetry plane + * @throws NullPointerException if the {@code midPoint} is {@code null} + * @throws IllegalArgumentException if {@code width} or {@code height} are <= 0 + */ + public MeshRectangleFacet getMesh(Point3d midPoint, double width, double height) { + if (width <= 0) { + throw new IllegalArgumentException("width"); + } + + if (height <= 0) { + throw new IllegalArgumentException("height"); + } + + double alpha = + -((normal.x * midPoint.x) + + (normal.y * midPoint.y) + + (normal.z * midPoint.z) + + distance) / (normal.dot(normal)); + + Point3d midPointOnPlane = new Point3d(midPoint); + Vector3d nn = new Vector3d(normal); + nn.scale(alpha); + midPointOnPlane.add(nn); + + return new MeshRectangleFacet(midPointOnPlane, normal, width, height); + } + + /** + * Returns rectangular mesh facet of this symmetry plane. + * + * @param midPoint A 3D point, which is projected to the plane and used as a center of the rectangular facet + * @param size Edge length + * @return a rectangular mesh facet of this symmetry plane + * @throws NullPointerException if the {@code midPoint} is {@code null} + * @throws IllegalArgumentException if {@code size} is <= 0 + */ + public MeshRectangleFacet getMesh(Point3d midPoint, double size) { + return Plane.this.getMesh(midPoint, size, size); + } + + /** + * Returns rectangular mesh facet of this symmetry plane. The centroid and size + * are estimated from bounding box. + * + * @param bbox Bounding box + * @return a rectangular mesh facet of this symmetry plane + * @throws NullPointerException if the {@code midPoint} or {@code bbox} are {@code null} + */ + public MeshRectangleFacet getMesh(BBox bbox) { + return Plane.this.getMesh(bbox.getMidPoint(), bbox.getDiagonalLength(), bbox.getDiagonalLength()); + } + protected void setNormal(Vector3d normal) { this.normal = normal; } 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 38b451c5436318b2a1710a227e31d5cd14bfccc5..993c2996ec49e4f4f7bfee9da9b63bd7a1dd05e4 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java @@ -2,7 +2,6 @@ package cz.fidentis.analyst.symmetry; import cz.fidentis.analyst.mesh.MeshVisitor; import cz.fidentis.analyst.mesh.core.MeshFacet; -import cz.fidentis.analyst.mesh.core.MeshRectangleFacet; import cz.fidentis.analyst.visitors.mesh.BoundingBox; import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox; import java.util.ArrayList; @@ -14,8 +13,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; -import javax.vecmath.Point3d; -import javax.vecmath.Vector3d; /** * Main class for computing approximate plane of symmetry of the 3D model. @@ -92,56 +89,6 @@ public class SymmetryEstimator extends MeshVisitor { return symmetryPlane; } - /** - * Computes and returns a triangular mesh for the symmetry plane (a rectangle). - * Its size is restricted by the bounding box of the original mesh facet. - * - * @return mesh facet of the symmetry plane or {@code null} - */ - public MeshFacet getSymmetryPlaneMesh() { - if (symmetryPlane == null) { - getSymmetryPlane(); // compute the symmetry plane - } - - BBox bbox = bbVisitor.getBoundingBox(); - - Vector3d normal = symmetryPlane.getNormal(); - Point3d midPoint = bbox.getMidPoint(); - - double alpha = - -((normal.x * midPoint.x) + - (normal.y * midPoint.y) + - (normal.z * midPoint.z) + - symmetryPlane.getDistance()) / (normal.dot(normal)); - - Point3d midPointOnPlane = new Point3d(midPoint); - Vector3d nn = new Vector3d(normal); - nn.scale(alpha); - midPointOnPlane.add(nn); - - double val = - normal.x * midPointOnPlane.x + - normal.y * midPointOnPlane.y + - normal.z * midPointOnPlane.z + - symmetryPlane.getDistance(); - - Vector3d frontDir = new Vector3d(); - if (Math.abs(normal.dot(new Vector3d(0.0, 1.0, 0.0))) > Math.abs(normal.dot(new Vector3d (1.0, 0.0, 0.0)))) { - frontDir.cross(normal, new Vector3d(1.0, 0.0, 0.0)); - } else { - frontDir.cross(normal, new Vector3d(0.0, 1.0, 0.0)); - } - frontDir.normalize(); - - Vector3d upDir = new Vector3d(); - upDir.cross(normal,frontDir); - upDir.normalize(); - - double scale = bbox.getMaxPoint().x - bbox.getMinPoint().x; - - return new MeshRectangleFacet(midPointOnPlane, frontDir, upDir, symmetryPlane.getNormal(), 2*scale, 2*scale); - } - /** * Returns the bounding box computed during the {@link SymmetryEstimator#calculateSymmetryPlane}. * @return the bounding box or {@code null} @@ -157,7 +104,7 @@ public class SymmetryEstimator extends MeshVisitor { */ protected void calculateSymmetryPlane(boolean concurrently) { final List<Plane> planes = new ArrayList<>(); - final double maxDistance = bbVisitor.getBoundingBox().getMaxDiag() * config.getMaxRelDistance(); + final double maxDistance = bbVisitor.getBoundingBox().getDiagonalLength() * config.getMaxRelDistance(); /* * Sequential computation diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/BoundingBox.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/BoundingBox.java index 52a56159c03091c9c7e20b01579a9d371a98712e..a1d5efe5d9e6d2e5bc3ac4f70f5cb8a4f271fcc8 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/BoundingBox.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/BoundingBox.java @@ -118,7 +118,7 @@ public class BoundingBox extends MeshVisitor { * Return volume diagonal of the bounding box. * @return maximal diagonal of bounding box */ - public double getMaxDiag() { + public double getDiagonalLength() { Vector3d v = new Vector3d(maxPoint); v.sub(minPoint); return v.length(); diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSectionZigZag.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSectionZigZag.java index 5b815eef88514684f645aa39d0456fec6b01ddee..befe27888118ed1a6b897713c5d1fc7c1214dc3e 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSectionZigZag.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSectionZigZag.java @@ -23,12 +23,12 @@ import java.util.stream.Collectors; * @author Radek Oslejsek */ public class CrossSectionZigZag extends MeshVisitor { - private CrossSectionCurve curve; - private Set<Point3d> usedPoints; + private final CrossSectionCurve curve; + private final Set<Point3d> usedPoints; private Set<MeshTriangle> visited; private Set<MeshTriangle> toVisit; - private Plane plane; + private final Plane plane; /** * Constructor for CrossSectionZigZag visitor @@ -37,7 +37,6 @@ public class CrossSectionZigZag extends MeshVisitor { */ public CrossSectionZigZag(Plane plane) { this.plane = plane; - //this.points = new ArrayList<>(); this.curve = new CrossSectionCurve(); this.usedPoints = new HashSet<>(); } diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableCuttingPlane.java b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableCuttingPlane.java new file mode 100644 index 0000000000000000000000000000000000000000..c4f334e1ace5c5dab60871f3719bf5019ee983b5 --- /dev/null +++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableCuttingPlane.java @@ -0,0 +1,121 @@ +package cz.fidentis.analyst.scene; + +import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshFacetImpl; +import cz.fidentis.analyst.symmetry.Plane; +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; + +/** + * Drawable plane with the possibility to shift it along the normal and, + * moreover, to show a "mirror" plane (a plane shifted in the opposite direction). + * + * @author Radek Oslejsek + * @author Dominik Racek + */ +public class DrawableCuttingPlane extends DrawablePlane { + + private double shift = 0.0; + + /** + * Copy constructor. + * @param drPlane Original plane + * @throws NullPointException if the input argument is {@code null} + */ + public DrawableCuttingPlane(DrawableCuttingPlane drPlane) { + super(drPlane); + this.shift = drPlane.shift; + } + + /** + * Constructor. + * @param facet Mesh facet of the plane + * @param plane The plane + */ + public DrawableCuttingPlane(MeshFacet facet, Plane plane) { + super(facet, plane); + } + + /** + * Constructor. + * + * @param plane The plane + * @param midPoint A 3D point, which is projected to the plane and used as a center of the rectangular facet + * @param width Width + * @param height Height + * @throws NullPointerException if the {@code plane} or {@code midPoint} are {@code null} + * @throws IllegalArgumentException if {@code width} or {@code height} are <= 0 + */ + public DrawableCuttingPlane(Plane plane, Point3d midPoint, double width, double height) { + super(plane, midPoint, width, height); + } + + /** + * Moves the plane in the plane's normal direction. + * + * @param dist Distance. If positive, then the movements is in the direction of the normal vector. + * Otherwise, the movement is against the normal direction. + */ + public void shift(double dist) { + shift += dist; + + Vector3d move = getPlane().getNormal(); + move.scale(dist); + for (int i = 0; i < getFacets().get(0).getNumberOfVertices(); ++i) { + getFacets().get(0).getVertex(i).getPosition().sub(move); + } + + if (isMirrorPlaneShown()) { + move.scale(-1.0); + for (int i = 0; i < getFacets().get(1).getNumberOfVertices(); ++i) { + getFacets().get(1).getVertex(i).getPosition().sub(move); + } + } + } + + /** + * Returns shifted cutting plane. + * @return shifted cutting plane. + */ + @Override + public Plane getPlane() { + return new Plane(super.getPlane().getNormal(), super.getPlane().getDistance() + shift); + } + + /** + * Returns the cutting plane shifted to opposite direction. + * @return the cutting plane shifted to opposite direction + */ + public Plane getMirrorPlane() { + return new Plane(super.getPlane().getNormal(), super.getPlane().getDistance() - shift); + } + + /** + * Shows/hides the mirror plane (i.e., the cutting plane shifted in the opposite direction + * @param show Shows if {@code true}, hides otherwise + */ + public void showMirrorPlane(boolean show) { + if (show) { + if (isMirrorPlaneShown()) { // already shown + return; + } + MeshFacetImpl mirror = new MeshFacetImpl(getModel().getFacets().get(0)); + Vector3d move = new Vector3d(super.getPlane().getNormal()); + move.scale(-2.0 * shift); + for (int i = 0; i < mirror.getNumberOfVertices(); ++i) { + mirror.getVertex(i).getPosition().sub(move); + } + getModel().addFacet(mirror); + } else if (isMirrorPlaneShown()) { + getModel().removeFacet(1); + } + } + + /** + * Returns {@code true} if the mirror planes are set to be shown. + * @return {@code true} if the mirror planes are set to be shown. + */ + public boolean isMirrorPlaneShown() { + return getModel().getFacets().size() > 1; + } +} diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePlane.java b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePlane.java index 2c7aa70d56275da41e0428d266e2b018b3047506..7999836ca817a0e3bd6b3ffab87a770066c3e1e3 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePlane.java +++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePlane.java @@ -3,13 +3,13 @@ package cz.fidentis.analyst.scene; import cz.fidentis.analyst.mesh.core.MeshFacet; import cz.fidentis.analyst.mesh.core.MeshModel; import cz.fidentis.analyst.symmetry.Plane; - -import javax.vecmath.Vector3d; +import javax.vecmath.Point3d; /** - * A cutting plane. + * A plane to be shown as a rectangular mesh facet. * * @author Radek Oslejsek + * @author Dominik Racek */ public class DrawablePlane extends DrawableMesh { @@ -50,25 +50,24 @@ public class DrawablePlane extends DrawableMesh { } this.plane = new Plane(plane); } + + /** + * Constructor. + * + * @param plane The plane + * @param midPoint A 3D point, which is projected to the plane and used as a center of the rectangular facet + * @param width Width + * @param height Height + * @throws NullPointerException if the {@code plane} or {@code midPoint} are {@code null} + * @throws IllegalArgumentException if {@code width} or {@code height} are <= 0 + */ + public DrawablePlane(Plane plane, Point3d midPoint, double width, double height) { + super(plane.getMesh(midPoint, width, height)); + this.plane = new Plane(plane); + } public Plane getPlane() { return plane; } - /** - * Translate the plane along its normal - * - * @param value - */ - public void translatePlane(double value) { - // Move real plane - this.plane.translate(value); - - // Move Drawable plane - Vector3d move = this.plane.getNormal(); - move.scale(value); - for (int i = 0; i < 4; ++i) { - getFacets().get(0).getVertex(i).getPosition().sub(move); - } - } } diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/Scene.java b/GUI/src/main/java/cz/fidentis/analyst/scene/Scene.java index 060500597ebdea48be367bd53b8fb931b1d85dae..62e3ed8d30817e2ff2d24cf44a391906fea48ed0 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/scene/Scene.java +++ b/GUI/src/main/java/cz/fidentis/analyst/scene/Scene.java @@ -14,11 +14,11 @@ import java.util.List; public class Scene { private final List<HumanFace> faces = new ArrayList<>(); + private final List<DrawableFace> drawableFaces = new ArrayList<>(); private final List<DrawableFeaturePoints> drawableFeaturePoints = new ArrayList<>(); private final List<DrawablePlane> drawableSymmetryPlanes = new ArrayList<>(); - private final List<DrawablePlane> drawableCuttingPlanes = new ArrayList<>(); - private final List<DrawablePlane> drawableMirrorPlanes = new ArrayList<>(); + private final List<DrawableCuttingPlane> drawableCuttingPlanes = new ArrayList<>(); private final List<Drawable> otherDrawables = new ArrayList<>(); /** @@ -41,7 +41,6 @@ public class Scene { } drawableSymmetryPlanes.add(null); drawableCuttingPlanes.add(null); - drawableMirrorPlanes.add(null); setDefaultColors(); } @@ -80,12 +79,10 @@ public class Scene { drawableSymmetryPlanes.add(null); drawableCuttingPlanes.add(null); - drawableMirrorPlanes.add(null); - + drawableSymmetryPlanes.add(null); drawableCuttingPlanes.add(null); - drawableMirrorPlanes.add(null); - + setDefaultColors(); } @@ -212,8 +209,8 @@ public class Scene { * @param index Index of the face * @return drawable plane or {@code null} */ - public DrawablePlane getDrawableCuttingPlane(int index) { - return (index >= 0 && index < getNumFaces()) ? drawableCuttingPlanes.get(index) : null; + public DrawableCuttingPlane getDrawableCuttingPlane(int index) { + return (index >= 0 && index < drawableCuttingPlanes.size()) ? drawableCuttingPlanes.get(index) : null; } /** @@ -222,18 +219,19 @@ public class Scene { * @param index Index of the face * @param cPlane New cutting plane */ - public void setDrawableCuttingPlane(int index, DrawablePlane cPlane) { - if (index >= 0 && index < getNumFaces() && cPlane != null) { + public void setDrawableCuttingPlane(int index, DrawableCuttingPlane cPlane) { + if (index >= 0 && index < drawableCuttingPlanes.size() && cPlane != null) { this.drawableCuttingPlanes.set(index, cPlane); } } /** - * Helper function for showing or hiding all cutting planes + * Show/hide cutting planes and their mirrors * * @param show determines whether to hide or show the planes + * @param show determines whether to hide or show the mirror planes */ - private void hideShowCuttingPlanes(boolean show) { + public void showCuttingPlanes(boolean show, boolean showMirrors) { for (int i = 0; i < drawableCuttingPlanes.size(); ++i) { if (drawableCuttingPlanes.get(i) != null) { if (show) { @@ -241,77 +239,11 @@ public class Scene { } else { drawableCuttingPlanes.get(i).hide(); } + drawableCuttingPlanes.get(i).showMirrorPlane(showMirrors); } } } - /** - * Show all cutting planes - */ - public void showCuttingPlanes() { - hideShowCuttingPlanes(true); - } - - /** - * Hide all cutting planes - */ - public void hideCuttingPlanes() { - hideShowCuttingPlanes(false); - } - - /** - * Returns drawable mirror plane. - * - * @param index Index of the face - * @return drawable face or {@code null} - */ - public DrawablePlane getDrawableMirrorPlane(int index) { - return (index >= 0 && index < getNumFaces()) ? drawableMirrorPlanes.get(index) : null; - } - - /** - * Sets the drawable mirror plane. - * - * @param index Index of the face - * @param mPlane New mirror plane - */ - public void setDrawableMirrorPlane(int index, DrawablePlane mPlane) { - if (index >= 0 && index < getNumFaces() && mPlane != null) { - this.drawableMirrorPlanes.set(index, mPlane); - } - } - - /** - * Helper function for showing or hiding all mirror planes - * - * @param show determines whether to hide or show the planes - */ - private void hideShowMirrorPlanes(boolean show) { - for (int i = 0; i < drawableMirrorPlanes.size(); ++i) { - if (drawableMirrorPlanes.get(i) != null) { - if (show) { - drawableMirrorPlanes.get(i).show(); - } else { - drawableMirrorPlanes.get(i).hide(); - } - } - } - } - - /** - * Show all mirror planes - */ - public void showMirrorPlanes() { - hideShowMirrorPlanes(true); - } - - /** - * Hide all mirror planes - */ - public void hideMirrorPlanes() { - hideShowMirrorPlanes(false); - } - /** * Adds new drawable object to the scene. * @@ -337,7 +269,6 @@ public class Scene { ret.addAll(this.drawableFeaturePoints); ret.addAll(this.drawableSymmetryPlanes); ret.addAll(this.drawableCuttingPlanes); - ret.addAll(this.drawableMirrorPlanes); ret.addAll(this.otherDrawables); while (ret.remove(null)) {} return ret; diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/CurveRenderingPanel.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/CurveRenderingPanel.java index 7c30646625bc75f9fd37e0ae5790657a09222799..73a34c4ac9cfe771db4b9338590a74cda92c8080 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/CurveRenderingPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/CurveRenderingPanel.java @@ -9,7 +9,6 @@ import java.awt.Graphics2D; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; -import java.util.ArrayList; import java.util.List; import javax.swing.JPanel; import javax.vecmath.Point3d; @@ -156,7 +155,6 @@ public class CurveRenderingPanel extends JPanel { protected void drawCurveSegment(Graphics2D g2, List<Point3d> curveSegment, Color faceColor, double offsetZ) { //Draw lines - List<Ellipse2D.Double> points = new ArrayList<>(); g2.setColor(faceColor); g2.setStroke(GRAPH_STROKE); for (int i = 0; i < curveSegment.size() - 1; i++) { diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java index a611171c9e3a7b6cfe292cbe8e4a64ed8214ff96..b9506962a17618bd868d959daf094fbcda544a8d 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java @@ -9,9 +9,8 @@ import cz.fidentis.analyst.face.events.HumanFaceListener; import cz.fidentis.analyst.face.events.SymmetryPlaneChangedEvent; import cz.fidentis.analyst.mesh.core.MeshFacet; import cz.fidentis.analyst.mesh.core.MeshFacetImpl; -import cz.fidentis.analyst.mesh.core.MeshRectangleFacet; -import cz.fidentis.analyst.scene.DrawablePlane; -import cz.fidentis.analyst.visitors.mesh.BoundingBox; +import cz.fidentis.analyst.scene.DrawableCuttingPlane; +import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox; import cz.fidentis.analyst.visitors.mesh.CrossSectionZigZag; import org.openide.filesystems.FileChooserBuilder; @@ -85,19 +84,14 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe // If the symmetry panel is focused... if (((JTabbedPane) e.getSource()).getSelectedComponent() instanceof ProfilesPanel) { getCanvas().getScene().setDefaultColors(); - getCanvas().getScene().showCuttingPlanes(); - if (controlPanel.isMirrorCutsChecked()) { - getCanvas().getScene().showMirrorPlanes(); - } + getCanvas().getScene().showCuttingPlanes(true, controlPanel.isMirrorCutsChecked()); recomputeProfiles(); } else { - getCanvas().getScene().hideCuttingPlanes(); - getCanvas().getScene().hideMirrorPlanes(); + getCanvas().getScene().showCuttingPlanes(false, false); } }); - getCanvas().getScene().hideCuttingPlanes(); - getCanvas().getScene().hideMirrorPlanes(); + getCanvas().getScene().showCuttingPlanes(false, false); // Be informed about changes in faces perfomed by other GUI elements getPrimaryDrawableFace().getHumanFace().registerListener(this); @@ -142,7 +136,12 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe if (option.equals(ProfilesPanel.OPTION_SYMMETRY_PLANE)) { if (getScene().getDrawableSymmetryPlane(0) == null) { - JOptionPane.showMessageDialog(new JFrame(), "Compute a symmetry plane at the Symmetry tab first.", "Dialog", JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog( + new JFrame(), + "Compute a symmetry plane at the Symmetry tab first.", + "Dialog", + JOptionPane.INFORMATION_MESSAGE + ); controlPanel.setDefaultPlaneSelection(); return; } @@ -153,21 +152,12 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe computeOrthogonalCuttingPlanes(false); } - if (controlPanel.isMirrorCutsChecked()) { - getCanvas().getScene().showMirrorPlanes(); - } else { - getCanvas().getScene().hideMirrorPlanes(); - } - + getCanvas().getScene().showCuttingPlanes(true, controlPanel.isMirrorCutsChecked()); recomputeProfiles(); break; case ProfilesPanel.ACTION_MIRROR_CUTS: controlPanel.getProfileRenderingPanel().setMirrorCuts(controlPanel.isMirrorCutsChecked()); - if (controlPanel.isMirrorCutsChecked()) { - getCanvas().getScene().showMirrorPlanes(); - } else { - getCanvas().getScene().hideMirrorPlanes(); - } + getCanvas().getScene().showCuttingPlanes(true, controlPanel.isMirrorCutsChecked()); break; default: // do nothing @@ -178,12 +168,9 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe @Override public void acceptEvent(HumanFaceEvent event) { if (event instanceof SymmetryPlaneChangedEvent && cuttingPlaneFromSymmetry) { + controlPanel.resetSliderSilently(); computeCuttingPlanesFromSymmetry(); // recompute cutting planes - if (controlPanel.isMirrorCutsChecked()) { - getCanvas().getScene().showMirrorPlanes(); - } else { - getCanvas().getScene().hideMirrorPlanes(); - } + //getCanvas().getScene().showCuttingPlanes(false, controlPanel.isMirrorCutsChecked()); } } @@ -232,7 +219,7 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe this.primaryCurve = cs.getCrossSectionCurve(); //Mirror profile - CrossSectionZigZag mcs = new CrossSectionZigZag(getScene().getDrawableMirrorPlane(0).getPlane()); + CrossSectionZigZag mcs = new CrossSectionZigZag(getScene().getDrawableCuttingPlane(0).getMirrorPlane()); getPrimaryDrawableFace().getModel().compute(mcs); this.primaryMirrorCurve = mcs.getCrossSectionCurve(); } @@ -244,7 +231,7 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe this.secondaryCurve = cs.getCrossSectionCurve(); //Mirror profile - CrossSectionZigZag mcs = new CrossSectionZigZag(getScene().getDrawableMirrorPlane(1).getPlane()); + CrossSectionZigZag mcs = new CrossSectionZigZag(getScene().getDrawableCuttingPlane(1).getMirrorPlane()); getSecondaryDrawableFace().getModel().compute(mcs); this.secondaryMirrorCurve = mcs.getCrossSectionCurve(); } @@ -281,15 +268,9 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe //Move cutting planes left and mirror planes right //If normal is negative, need to negate value if (getScene().getDrawableCuttingPlane(index).getPlane().getNormal().x < 0) { - getScene().getDrawableCuttingPlane(index).translatePlane(-value); + getScene().getDrawableCuttingPlane(index).shift(-value); } else { - getScene().getDrawableCuttingPlane(index).translatePlane(value); - } - - if (getScene().getDrawableMirrorPlane(index).getPlane().getNormal().x < 0) { - getScene().getDrawableMirrorPlane(index).translatePlane(value); - } else { - getScene().getDrawableMirrorPlane(index).translatePlane(-value); + getScene().getDrawableCuttingPlane(index).shift(value); } } @@ -299,58 +280,26 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe * @param horizontal */ protected void computeOrthogonalCuttingPlanes(boolean horizontal) { - DrawablePlane drPlane = getDrawableOrthogonalPlane(horizontal); + DrawableCuttingPlane drPlane = getDrawableOrthogonalPlane(horizontal); getCanvas().getScene().setDrawableCuttingPlane(0, drPlane); - getCanvas().getScene().setDrawableMirrorPlane(0, new DrawablePlane(drPlane)); if (getSecondaryDrawableFace() != null) { - getCanvas().getScene().setDrawableCuttingPlane(1, new DrawablePlane(drPlane)); - getCanvas().getScene().setDrawableMirrorPlane(1, new DrawablePlane(drPlane)); + getCanvas().getScene().setDrawableCuttingPlane(1, new DrawableCuttingPlane(drPlane)); } cuttingPlaneFromSymmetry = false; } - protected DrawablePlane getDrawableOrthogonalPlane(boolean horizontal) { - BoundingBox bbox = new BoundingBox(); - getPrimaryDrawableFace().getModel().compute(bbox); - if (getSecondaryDrawableFace() != null) { - getSecondaryDrawableFace().getModel().compute(bbox); - } - - Vector3d normal = (horizontal) ? new Vector3d(0,1,0) : new Vector3d(-1,0,0); - Point3d midPoint = bbox.getBoundingBox().getMidPoint(); - Point3d minPoint = bbox.getBoundingBox().getMinPoint(); - Point3d maxPoint = bbox.getBoundingBox().getMaxPoint(); - - // add border: - Vector3d diagonal = new Vector3d(maxPoint); - diagonal.sub(minPoint); - diagonal.scale(0.15); // border size - maxPoint.add(diagonal); - diagonal.scale(-1); - minPoint.add(diagonal); - - Plane plane = new Plane(normal, (horizontal) ? midPoint.y : midPoint.x); - - MeshRectangleFacet planeMesh; - if (horizontal) { - planeMesh = new MeshRectangleFacet( - midPoint, - new Vector3d(1, 0, 0), - new Vector3d(0, 0, 1), - maxPoint.x - minPoint.x, - maxPoint.z - minPoint.z - ); - } else { - planeMesh = new MeshRectangleFacet( - midPoint, - new Vector3d(0, 0, 1), - new Vector3d(0, 1, 0), - maxPoint.z - minPoint.z, - maxPoint.y - minPoint.y - ); - } - - DrawablePlane cuttingPlane = new DrawablePlane(planeMesh, plane); + protected DrawableCuttingPlane getDrawableOrthogonalPlane(boolean horizontal) { + BBox bbox = getScene().getHumanFace(0).getBoundingBox(); // use BBox of the first face for size and position estimation + Plane plane = new Plane( + (horizontal) ? new Vector3d(0,1,0) : new Vector3d(-1,0,0), + (horizontal) ? bbox.getMidPoint().y : bbox.getMidPoint().x + ); + DrawableCuttingPlane cuttingPlane = new DrawableCuttingPlane( + plane, + bbox.getMidPoint(), + bbox.getDiagonalLength(), + bbox.getDiagonalLength() + ); cuttingPlane.setTransparency(0.5f); return cuttingPlane; } @@ -361,34 +310,32 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe * @param faceIndex * @return */ - protected DrawablePlane getCuttingPlaneFromSymmetry(int faceIndex) { + protected DrawableCuttingPlane getCuttingPlaneFromSymmetry(int faceIndex) { MeshFacet symmetryFacet = getScene().getHumanFace(faceIndex).getSymmetryPlaneFacet(); Plane symmetryPlane = getScene().getHumanFace(faceIndex).getSymmetryPlane(); - return new DrawablePlane(new MeshFacetImpl(symmetryFacet), new Plane(symmetryPlane)); + return new DrawableCuttingPlane(new MeshFacetImpl(symmetryFacet), new Plane(symmetryPlane)); } /** * Creates cutting and mirror planes by copying the rectangle from the symmetry plane. */ protected void computeCuttingPlanesFromSymmetry() { - DrawablePlane cuttingPlane = getCuttingPlaneFromSymmetry(0); + DrawableCuttingPlane cuttingPlane = getCuttingPlaneFromSymmetry(0); cuttingPlane.setTransparency(0.5f); getCanvas().getScene().setDrawableCuttingPlane(0, cuttingPlane); - getCanvas().getScene().setDrawableMirrorPlane(0, new DrawablePlane(cuttingPlane)); recomputePrimaryProfile(); if (getSecondaryDrawableFace() != null) { cuttingPlane = getCuttingPlaneFromSymmetry(1); cuttingPlane.setTransparency(0.5f); getCanvas().getScene().setDrawableCuttingPlane(1, cuttingPlane); - getCanvas().getScene().setDrawableMirrorPlane(1, new DrawablePlane(cuttingPlane)); recomputeSecondaryProfile(); } cuttingPlaneFromSymmetry = true; } protected void projectCloseFeaturePoints(CrossSectionCurve curve, HumanFace face, Plane cuttingPlane) { - if (!face.hasFeaturePoints()) { + if (!face.hasFeaturePoints() || curve.getNumSegments() == 0) { return; } List<Point3d> close = face.getFeaturePoints() diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java index 793552f59eb682f3297e5e5be1916f3bd5bc931b..610426d58b88a422e20f37fb0920a0c3f08c33f2 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java @@ -3,14 +3,20 @@ package cz.fidentis.analyst.symmetry; import cz.fidentis.analyst.Logger; import cz.fidentis.analyst.canvas.Canvas; import cz.fidentis.analyst.core.ControlPanelAction; +import cz.fidentis.analyst.face.HumanFace; import cz.fidentis.analyst.face.events.HumanFaceEvent; import cz.fidentis.analyst.face.events.HumanFaceListener; import cz.fidentis.analyst.face.events.MeshChangedEvent; +import cz.fidentis.analyst.feature.FeaturePoint; import cz.fidentis.analyst.scene.DrawableFace; import cz.fidentis.analyst.scene.DrawablePlane; import java.awt.event.ActionEvent; +import java.util.HashMap; +import java.util.Map; import javax.swing.JTabbedPane; +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; /** * Action listener for the manipulation with the symmetry plane. @@ -33,6 +39,8 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe this.topControlPanel = topControlPanel; //this.controlPanel = new SymmetryPanel(this); this.controlPanel = new SymmetryPanel(this); + + controlPanel.setComputeFromFPs(getPrimaryFeaturePoints() != null || getSecondaryFeaturePoints() != null); // Place control panel to the topControlPanel this.topControlPanel.addTab(controlPanel.getName(), controlPanel.getIcon(), controlPanel); @@ -46,9 +54,6 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe } }); - //recomputeSymmetryPlane(0); - //recomputeSymmetryPlane(1); - // Be informed about changes in faces perfomed by other GUI elements getPrimaryDrawableFace().getHumanFace().registerListener(this); if (getSecondaryDrawableFace() != null) { @@ -64,7 +69,11 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe case SymmetryPanel.ACTION_COMMAND_RECOMPUTE: recomputeSymmetryPlane(0); recomputeSymmetryPlane(1); - break; + break; + case SymmetryPanel.ACTION_COMMAND_COMPUTE_FROM_FPS: + computeFromFeaturePoints(0); + computeFromFeaturePoints(1); + break; default: // do nothing } @@ -82,24 +91,88 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe } } - private void recomputeSymmetryPlane(int index) { - DrawableFace face = getCanvas().getScene().getDrawableFace(index); + protected void recomputeSymmetryPlane(int index) { + HumanFace face = getCanvas().getScene().getHumanFace(index); + if (face != null) { + Logger log = Logger.measureTime(); + SymmetryEstimator estimator = new SymmetryEstimator(controlPanel.getSymmetryConfig()); + face.getMeshModel().compute(estimator); + face.setSymmetryPlane(estimator.getSymmetryPlane()); + log.printDuration("Symmetry plane estimation for " + face.getShortName()); + setDrawablePlane(face, index); + } + } + + protected void computeFromFeaturePoints(int index) { + HumanFace face = getCanvas().getScene().getHumanFace(index); if (face == null) { return; } - Logger log = Logger.measureTime(); - SymmetryEstimator estimator = new SymmetryEstimator(controlPanel.getSymmetryConfig()); - face.getModel().compute(estimator); - face.getHumanFace().setSymmetryPlane(estimator.getSymmetryPlane(), estimator.getSymmetryPlaneMesh()); - log.printDuration("Symmetry plane estimation for " + face.getHumanFace().getShortName()); + //FeaturePointTypeProvider.getInstance(); + Map<String, Point3d> processFP = new HashMap<>(); + Vector3d dir = new Vector3d(); + int numDirs = 0; + Point3d centroid = new Point3d(); + + for (FeaturePoint fp: face.getFeaturePoints()) { + Point3d auxCentroid = new Point3d(fp.getPosition()); + auxCentroid.scale(1.0 / face.getFeaturePoints().size()); + if (numDirs == 0) { + centroid.set(auxCentroid); + } else { + + centroid.add(auxCentroid); + } + String code = fp.getFeaturePointType().getCode(); + if (code.endsWith("_R")) { + String pairCode = code.replaceAll("_R", "_L"); + if (processFP.containsKey(pairCode)) { + computeDir(processFP.remove(pairCode), fp.getPosition(), dir, numDirs++); + } else { + processFP.put(code, fp.getPosition()); + } + } else if (code.endsWith("_L")) { + String pairCode = code.replaceAll("_L", "_R"); + if (processFP.containsKey(pairCode)) { + computeDir(fp.getPosition(), processFP.remove(pairCode), dir, numDirs++); + } else { + processFP.put(code, fp.getPosition()); + } + } + } + + if (numDirs == 0) { // no pair found + return; + } + + dir.scale(1.0 / numDirs); + dir.normalize(); + + face.setSymmetryPlane(new Plane(dir, dir.dot(new Vector3d(centroid)))); + setDrawablePlane(face, index); + } + + protected void computeDir(Point3d left, Point3d right, Vector3d dir, int numDirs) { + Vector3d auxDir = new Vector3d(right); + auxDir.sub(left); + auxDir.normalize(); + if (numDirs == 0) { + dir.set(auxDir); + } else { + dir.add(auxDir); + } + } + + protected void setDrawablePlane(HumanFace face, int index) { DrawablePlane symmetryPlane = new DrawablePlane( - face.getHumanFace().getSymmetryPlaneFacet(), - face.getHumanFace().getSymmetryPlane() + face.getSymmetryPlaneFacet(), + face.getSymmetryPlane() ); symmetryPlane.setTransparency(0.5f); symmetryPlane.setColor((index == 0) ? DrawableFace.SKIN_COLOR_PRIMARY.darker() : DrawableFace.SKIN_COLOR_SECONDARY.darker()); getCanvas().getScene().setDrawableSymmetryPlane(index, symmetryPlane); } + } diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.form b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.form index 0ad51283523e82bebed4b0ea961cdd347add187f..7b7730230d14a3f4ec7a4500de0918caf3b9fefb 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.form +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.form @@ -16,15 +16,11 @@ <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> + <Group type="102" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Component id="jPanel1" min="-2" max="-2" attributes="0"/> - <Group type="102" attributes="0"> - <Component id="jButton1" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="jButton2" min="-2" max="-2" attributes="0"/> - </Group> + <Component id="jPanel1" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jButton3" alignment="0" min="-2" max="-2" attributes="0"/> </Group> <EmptySpace max="32767" attributes="0"/> </Group> @@ -35,12 +31,9 @@ <Group type="102" alignment="0" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Component id="jPanel1" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace max="32767" attributes="0"/> + <EmptySpace type="unrelated" max="-2" attributes="0"/> + <Component id="jButton3" min="-2" max="-2" attributes="0"/> + <EmptySpace pref="22" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> @@ -50,7 +43,7 @@ <Properties> <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> <Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo"> - <TitledBorder title="Configuration"> + <TitledBorder title="Symmetry from mesh"> <ResourceString PropertyName="titleX" bundle="cz/fidentis/analyst/symmetry/Bundle.properties" key="SymmetryPanel.jPanel1.border.title_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> <Font PropertyName="font" name="Dialog" size="14" style="1"/> </TitledBorder> @@ -81,7 +74,6 @@ </Group> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Component id="comboSliderDouble4" alignment="0" min="-2" max="-2" attributes="0"/> <Group type="102" alignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="103" alignment="0" groupAlignment="0" attributes="0"> @@ -101,6 +93,14 @@ <Component id="jButtonInfo3" min="-2" max="-2" attributes="0"/> </Group> </Group> + <Group type="103" alignment="0" groupAlignment="1" attributes="0"> + <Group type="102" attributes="0"> + <Component id="jButton1" min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="jButton2" min="-2" max="-2" attributes="0"/> + </Group> + <Component id="comboSliderDouble4" min="-2" max="-2" attributes="0"/> + </Group> </Group> </Group> </Group> @@ -168,12 +168,16 @@ <Component id="jButtonInfo4" min="-2" max="-2" attributes="0"/> </Group> </Group> - <EmptySpace min="-2" pref="29" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="24" max="-2" attributes="0"/> <Group type="103" groupAlignment="1" attributes="0"> <Component id="jLabel6" min="-2" max="-2" attributes="0"/> <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> </Group> - <EmptySpace pref="28" max="32767" attributes="0"/> + <EmptySpace max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> @@ -283,24 +287,31 @@ <Property name="borderPainted" type="boolean" value="false"/> </Properties> </Component> + <Component class="javax.swing.JButton" name="jButton1"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="cz/fidentis/analyst/symmetry/Bundle.properties" key="SymmetryPanel.jButton1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + </Component> + <Component class="javax.swing.JButton" name="jButton2"> + <Properties> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="cz/fidentis/analyst/symmetry/Bundle.properties" key="SymmetryPanel.jButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/> + </Events> + </Component> </SubComponents> </Container> - <Component class="javax.swing.JButton" name="jButton1"> - <Properties> - <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="cz/fidentis/analyst/symmetry/Bundle.properties" key="SymmetryPanel.jButton1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> - </Property> - </Properties> - </Component> - <Component class="javax.swing.JButton" name="jButton2"> + <Component class="javax.swing.JButton" name="jButton3"> <Properties> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="cz/fidentis/analyst/symmetry/Bundle.properties" key="SymmetryPanel.jButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + <ResourceString bundle="cz/fidentis/analyst/symmetry/Bundle.properties" key="SymmetryPanel.jButton3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> </Property> </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/> - </Events> </Component> </SubComponents> </Form> diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java index 0e2bd223afafba80a7180dd7934e2e06b7bdd01c..bd5a82602d5086ae26a589ad67b583f82a119039 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java @@ -18,6 +18,7 @@ public class SymmetryPanel extends ControlPanel { * Handled actions */ public static final String ACTION_COMMAND_RECOMPUTE = "recompute"; + public static final String ACTION_COMMAND_COMPUTE_FROM_FPS = "compute from FPs"; /* * Mandatory design elements @@ -132,6 +133,16 @@ public class SymmetryPanel extends ControlPanel { this.showNormalAngleHelp(); } ); + + jButton3.addActionListener( + (ActionEvent e) -> { + action.actionPerformed(new ActionEvent( // compute from FPs + e.getSource(), + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_COMPUTE_FROM_FPS) + ); + } + ); } @Override @@ -155,6 +166,14 @@ public class SymmetryPanel extends ControlPanel { public static ImageIcon getStaticIcon() { return new ImageIcon(SymmetryPanel.class.getClassLoader().getResource("/" + ICON)); } + + /** + * Enables/disables the button for the computation of the symmetry from feature points. + * @param on {@code true} = enable + */ + public void setComputeFromFPs(boolean on) { + jButton3.setEnabled(on); + } private void showSignPointsHelp() { JOptionPane.showMessageDialog(this, @@ -251,6 +270,7 @@ public class SymmetryPanel extends ControlPanel { jButtonInfo3 = new javax.swing.JButton(); jButton1 = new javax.swing.JButton(); jButton2 = new javax.swing.JButton(); + jButton3 = new javax.swing.JButton(); jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jPanel1.border.title_1"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 14))); // NOI18N @@ -285,6 +305,15 @@ public class SymmetryPanel extends ControlPanel { org.openide.awt.Mnemonics.setLocalizedText(jButtonInfo3, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jButtonInfo3.text")); // NOI18N jButtonInfo3.setBorderPainted(false); + org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jButton1.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jButton2, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jButton2.text")); // NOI18N + jButton2.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton2ActionPerformed(evt); + } + }); + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( @@ -306,7 +335,6 @@ public class SymmetryPanel extends ControlPanel { .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, 143, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(comboSliderDouble4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -320,7 +348,13 @@ public class SymmetryPanel extends ControlPanel { .addComponent(jButtonInfo1) .addComponent(jButtonInfo2) .addComponent(jButtonInfo4) - .addComponent(jButtonInfo3)))))) + .addComponent(jButtonInfo3))) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(jButton1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jButton2)) + .addComponent(comboSliderDouble4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); jPanel1Layout.setVerticalGroup( @@ -368,21 +402,17 @@ public class SymmetryPanel extends ControlPanel { .addGroup(jPanel1Layout.createSequentialGroup() .addGap(11, 11, 11) .addComponent(jButtonInfo4))) - .addGap(29, 29, 29) + .addGap(24, 24, 24) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(jLabel6) - .addComponent(jCheckBox1)) - .addContainerGap(28, Short.MAX_VALUE)) + .addComponent(jCheckBox1) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jButton1) + .addComponent(jButton2))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); - org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jButton1.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(jButton2, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jButton2.text")); // NOI18N - jButton2.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton2ActionPerformed(evt); - } - }); + org.openide.awt.Mnemonics.setLocalizedText(jButton3, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jButton3.text")); // NOI18N javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -392,10 +422,7 @@ public class SymmetryPanel extends ControlPanel { .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createSequentialGroup() - .addComponent(jButton1) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jButton2))) + .addComponent(jButton3)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( @@ -403,11 +430,9 @@ public class SymmetryPanel extends ControlPanel { .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton1) - .addComponent(jButton2)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jButton3) + .addContainerGap(22, Short.MAX_VALUE)) ); }// </editor-fold>//GEN-END:initComponents @@ -424,6 +449,7 @@ public class SymmetryPanel extends ControlPanel { private cz.fidentis.analyst.core.ComboSliderInteger comboSliderInteger1; private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; + private javax.swing.JButton jButton3; private javax.swing.JButton jButtonInfo1; private javax.swing.JButton jButtonInfo2; private javax.swing.JButton jButtonInfo3; diff --git a/GUI/src/main/resources/cz/fidentis/analyst/symmetry/Bundle.properties b/GUI/src/main/resources/cz/fidentis/analyst/symmetry/Bundle.properties index c6a2985a23be17b00739ff8af828d7c523c634a7..6a9163d800f6a7741f78386c4cd580f64b253c28 100644 --- a/GUI/src/main/resources/cz/fidentis/analyst/symmetry/Bundle.properties +++ b/GUI/src/main/resources/cz/fidentis/analyst/symmetry/Bundle.properties @@ -26,7 +26,7 @@ SymmetryPanel.jButtonInfo1.text= SymmetryPanel.jLabel3.text_1=Min. angle cosine SymmetryPanel.jLabel2.text_1=Min. curvature ratio SymmetryPanel.jLabel1.text_1=Undersampling -SymmetryPanel.jPanel1.border.title_1=Configuration +SymmetryPanel.jPanel1.border.title_1=Symmetry from mesh SymmetryPanel.jButton2.text=Compute SymmetryPanel.jButton1.text=Reset to defaults SymmetryPanel.jCheckBox1.text= @@ -36,3 +36,4 @@ SymmetryPanel.jLabel5.text_1=Relative distance SymmetryPanel.jButtonInfo4.text= ProfilesPanel.jCheckBox1.text=Mirror cuts ProfilesPanel.jButton1.text=Export +SymmetryPanel.jButton3.text=Compute from feature points 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 f046f5f948ffc74e693bd2f0b5de662298fdd059..ae5462997554ea1a954b513f1150529efe57e9dc 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 @@ -65,6 +65,17 @@ public class MeshModel implements Serializable { facets.add(facet); } + /** + * Removes facet from the model. + * + * @param index Index of the facet + * @return Removed facet or {@code null} + * @throws IndexOutOfBoundsException if the facet does not exist + */ + public MeshFacet removeFacet(int index) { + return facets.remove(index); + } + /** * Adds a new mesh facets to the model. * diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshRectangleFacet.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshRectangleFacet.java index 6c54facb7d3580a09106e84b14bb044706bfdc58..cd5fc6d7ea06642d1443891868c27f301eb1d210 100644 --- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshRectangleFacet.java +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshRectangleFacet.java @@ -10,6 +10,30 @@ import javax.vecmath.Vector3d; */ public class MeshRectangleFacet extends MeshFacetImpl { + /** + * Constructor. The vertical and horizontal direction are estimated automatically. + * + * @param center Central point of the rectangle + * @param normal Normalized normal vector + * @param w Width + * @param h Height + */ + public MeshRectangleFacet(Point3d center, Vector3d normal, double w, double h) { + Vector3d hDir = new Vector3d(); + if (Math.abs(normal.dot(new Vector3d(0.0, 1.0, 0.0))) > Math.abs(normal.dot(new Vector3d (1.0, 0.0, 0.0)))) { + hDir.cross(normal, new Vector3d(1.0, 0.0, 0.0)); + } else { + hDir.cross(normal, new Vector3d(0.0, 1.0, 0.0)); + } + hDir.normalize(); + + Vector3d vDir = new Vector3d(); + vDir.cross(normal, hDir); + vDir.normalize(); + + initRectangle(center, hDir, vDir, normal, w, h); + } + /** * Constructor. *