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 8e089d9c2bcdb5f46a88ef66aea2b5f34a7d5f2e..12707870fbfbbae1cab90f30b426cd2ea586c138 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java @@ -23,6 +23,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.RandomAccessFile; import java.io.Serializable; import java.nio.file.Path; import java.util.Collections; @@ -87,6 +88,10 @@ public class HumanFace implements Serializable { meshModel.simplifyModel(); this.id = file.getCanonicalPath(); + meshModel.getFacets().stream() + .filter(f -> !f.hasVertexNormals()) + .forEach(f -> f.calculateVertexNormals()); + updateBoundingBox(); eventBus = new EventBus(); @@ -412,8 +417,9 @@ public class HumanFace implements Serializable { public File dumpToFile() throws IOException { File tempFile = File.createTempFile(this.getClass().getSimpleName(), ".ser"); tempFile.deleteOnExit(); + RandomAccessFile raf = new RandomAccessFile(tempFile, "rw"); - try (ObjectOutputStream fos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(tempFile)))) { + try (ObjectOutputStream fos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(raf.getFD())))) { fos.writeObject(this); fos.flush(); } diff --git a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceUtils.java b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceUtils.java index 3c91d5b5524ed0554cb6874e52b9a99e4fadcb29..700fff542062a5103b1e6dd93f22b2c15f1c95e8 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceUtils.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceUtils.java @@ -1,12 +1,14 @@ package cz.fidentis.analyst.face; +import cz.fidentis.analyst.Logger; import cz.fidentis.analyst.feature.FeaturePoint; -import cz.fidentis.analyst.icp.EigenvalueDecomposition; import cz.fidentis.analyst.icp.IcpTransformer; import cz.fidentis.analyst.icp.NoUndersampling; import cz.fidentis.analyst.icp.Quaternion; import cz.fidentis.analyst.icp.RandomStrategy; +import cz.fidentis.analyst.procrustes.ProcrustesAnalysis; import cz.fidentis.analyst.symmetry.Plane; +import java.util.zip.DataFormatException; import javax.vecmath.Matrix3d; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; @@ -22,7 +24,8 @@ import javax.vecmath.Vector3d; public class HumanFaceUtils { /** - * Superimpose two faces using ICP applied on their triangular meshes. + * Superimpose two faces by applying iterative closest points (ICP) algorithm + * on their triangular meshes. * * @param staticFace A face that remains unchanged. * @param transformedFace A face to be transformed. @@ -146,64 +149,36 @@ public class HumanFaceUtils { * @param transformedFace a human face that will be transformed * @param recomputeKdTree If {@code true} and the k-d tree of the {@code transformedFace} exists, * the it automatically re-computed. Otherwise, it is simply removed. + * @param preserveUpDir If {@code false}, then the object can be rotated around the target's normal arbitrarily. */ - public static void alignSymmetryPlanes(HumanFace staticFace, HumanFace transformedFace, boolean recomputeKdTree) { + public static void alignSymmetryPlanes( + HumanFace staticFace, HumanFace transformedFace, + boolean recomputeKdTree, boolean preserveUpDir) { + Plane statPlane = staticFace.getSymmetryPlane(); Plane tranPlane = transformedFace.getSymmetryPlane(); - // Get "centroids" of the planes - Point3d statCentroid = statPlane.projectToPlane(staticFace.getBoundingBox().getMidPoint()); - Point3d tranCentroid = tranPlane.projectToPlane(transformedFace.getBoundingBox().getMidPoint()); - - // Compute a 3D transformation of planes. - // However, this transforation rotates the face around the plane axis arbitrarily - Vector3d translation = new Vector3d( - statCentroid.x - tranCentroid.x, - statCentroid.y - tranCentroid.y, - statCentroid.z - tranCentroid.z - ); - Quaternion rotation = computeRotation(tranPlane.getNormal(), statPlane.getNormal()); - - // Compute rotation around plane's normal so that the face is oriented as before - - // Get a vector perpendicular to the transformed plane nornal (and then laying at the plane) - Point3d pp = getPerpendicularPoint(tranPlane); // point at the original plane - Vector3d pv = new Vector3d(pp); - pv.sub(tranCentroid); - pv.normalize(); - - // transform the point so that it lays at the target plane - transformPoint(pp, rotation, translation, 1.0); // point at the transformed plane - transformPoint(tranCentroid, rotation, translation, 1.0); // point at the transformed plane - Vector3d trPV = new Vector3d(pp); - trPV.sub(tranCentroid); - trPV.normalize(); - - // compute angle of rotation around the plane normal so that the face is oriented as before - double cosRot = pv.dot(trPV); - pv.cross(pv, trPV); - double sinRot = pv.length(); - Vector3d axis = (statPlane.getNormal().dot(tranPlane.getNormal()) < 0) - ? statPlane.getNormal() - : new Vector3d(-statPlane.getNormal().x, -statPlane.getNormal().y, -statPlane.getNormal().z); + if (statPlane.getNormal().dot(tranPlane.getNormal()) < 0) { + tranPlane = tranPlane.flip(); + } - // get rotation matrix around the plane's normal - Matrix3d rotMat = rotMatAroundAxis(axis, sinRot, cosRot); + Matrix4d trMat = statPlane.getAlignmentMatrix(tranPlane, preserveUpDir); // Transform mesh vertices: transformedFace.getMeshModel().getFacets().forEach(f -> { f.getVertices().stream().forEach(p -> { - transformPoint(p.getPosition(), rotation, translation, 1.0); - rotMat.transform(p.getPosition()); // rotate around the plane's normal + trMat.transform(p.getPosition()); + //transformPoint(p.getPosition(), rotation, translation, 1.0); + //rotMat.transform(p.getPosition()); // rotate around the plane's normal }); }); // update k-d of transformed face: if (transformedFace.hasKdTree()) { if (recomputeKdTree) { - transformedFace.computeKdTree(true); + transformedFace.computeKdTree(true); } else { - transformedFace.removeKdTree(); + transformedFace.removeKdTree(); } } @@ -213,16 +188,77 @@ public class HumanFaceUtils { // Transform feature points: if (transformedFace.hasFeaturePoints()) { transformedFace.getFeaturePoints().parallelStream().forEach(fp -> { - transformPoint(fp.getPosition(), rotation, translation, 1.0); - rotMat.transform(fp.getPosition()); // rotate around the plane's normal + trMat.transform(fp.getPosition()); + //transformPoint(fp.getPosition(), rotation, translation, 1.0); + //rotMat.transform(fp.getPosition()); // rotate around the plane's normal }); } - // Transform the symmetry plane: - if (transformedFace.hasSymmetryPlane()) { - transformedFace.setSymmetryPlane(transformPlane( - transformedFace.getSymmetryPlane(), rotation, translation, 1.0) - ); + // Transform the symmetry plane + //transformedFace.setSymmetryPlane(new Plane(staticFace.getSymmetryPlane())); + Point3d p = transformedFace.getSymmetryPlane().getPlanePoint(); + trMat.transform(p); + transformedFace.setSymmetryPlane(new Plane(p)); + } + + /** + * Superimpose two faces by applying Procrustes on their feature points. + * Be aware that both faces are transformed into the origin of the coordinate system! + * + * @param firstFace a human face that remains unchanged + * @param secondFace a human face that will be transformed + * @param scale Whether to scale faces as well + * @param recomputeKdTree If {@code true} and the k-d tree of the {@code transformedFace} exists, + * the it automatically re-computed. Otherwise, it is simply removed. + */ + public static void alignFeaturePoints( + HumanFace firstFace, HumanFace secondFace, + boolean scale, boolean recomputeKdTree) { + + ProcrustesAnalysis pa = null; + try { + pa = new ProcrustesAnalysis(firstFace, secondFace, scale); + pa.analyze(); + } catch (DataFormatException e) { + Logger.print("Procrustes Analysis experienced exception"); + return; + } + + // update k-d of transformed faces: + if (firstFace.hasKdTree()) { + if (recomputeKdTree) { + firstFace.computeKdTree(true); + } else { + firstFace.removeKdTree(); + } + } + // update k-d of transformed faces: + if (secondFace.hasKdTree()) { + if (recomputeKdTree) { + secondFace.computeKdTree(true); + } else { + secondFace.removeKdTree(); + } + } + + // update bounding box, which is always present: + firstFace.updateBoundingBox(); + secondFace.updateBoundingBox(); + + // transform feature points: + if (firstFace.hasFeaturePoints()) { + // TODO + } + if (secondFace.hasFeaturePoints()) { + // TODO + } + + // transform symmetry plane: + if (firstFace.hasSymmetryPlane()) { + // TODO + } + if (secondFace.hasSymmetryPlane()) { + // TODO } } @@ -255,53 +291,6 @@ public class HumanFaceUtils { return rotMat; } - /** - * Computes a "random" point at the plan, i.e. a point, which is - * perpendicular to the plane's normal and lays at the plane. - * - * @param plane the plane - * @return point at the plane - */ - protected static Point3d getPerpendicularPoint(Plane plane) { - Point3d p; - if (plane.getNormal().x >= Math.max(plane.getNormal().y, plane.getNormal().z)) { - p = new Point3d(0, 1000, 0); - } else if (plane.getNormal().y >= Math.max(plane.getNormal().x, plane.getNormal().y)) { - p = new Point3d(1000, 0, 0); - } else { - p = new Point3d(0, 1000, 0); - } - return plane.projectToPlane(p); - } - - /** - * Computed 3D rotation matrix that transforms one vector onto another. - * @param trDir vector to be transformed - * @param targetDir target vector - * @return rotation - */ - protected static Quaternion computeRotation(Vector3d trDir, Vector3d targetDir) { - Matrix4d multipleMatrix = new Matrix4d(); - Matrix4d sumMatrixComp = new Matrix4d( - 0, -trDir.x, -trDir.y, -trDir.z, - trDir.x, 0, trDir.z, -trDir.y, - trDir.y, -trDir.z, 0, trDir.x, - trDir.z, trDir.y, -trDir.x, 0 - ); - Matrix4d sumMatrixMain = new Matrix4d( - 0, -targetDir.x, -targetDir.y, -targetDir.z, - targetDir.x, 0, -targetDir.z, targetDir.y, - targetDir.y, targetDir.z, 0, -targetDir.x, - targetDir.z, -targetDir.y, targetDir.x, 0 - ); - - multipleMatrix.mulTransposeLeft(sumMatrixComp, sumMatrixMain); - Quaternion rotation = new Quaternion(new EigenvalueDecomposition(multipleMatrix)); - rotation.normalize(); - return rotation; - } - - /** * Transform a single 3d point. * diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java index 345c62e138d4bde06ccadbb97b1d1eaae3a4c860..bd9431a4b9eabbcbe79896e11feecbc51f5e9d82 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java @@ -17,7 +17,6 @@ import java.util.Objects; import java.util.Set; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; -import javax.vecmath.Tuple3d; import javax.vecmath.Tuple4d; import javax.vecmath.Vector3d; import javax.vecmath.Vector4d; 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 2b9489bf6f2f0eebf8424608e5084294c29d2b45..191c97674f11eb97af90994cf5396425cc1d33a0 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java @@ -4,6 +4,7 @@ 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.Matrix4d; import javax.vecmath.Point3d; import javax.vecmath.Tuple3d; import javax.vecmath.Vector3d; @@ -18,7 +19,6 @@ public class Plane implements Serializable { private Vector3d normal; private double distance; - private double normSquare; // normal.dot(normal) /** * Constructor. @@ -41,6 +41,18 @@ public class Plane implements Serializable { this(plane.getNormal(), plane.getDistance()); } + /** + * Constructor from a 3D point. + * + * @param point point in space + * @throws IllegalArgumentException if the @code{plane} argument is null + */ + public Plane(Tuple3d point) { + this.normal = new Vector3d(point); + setDistance(this.normal.length()); + this.normal.normalize(); + } + /** * Creates average plane from existing planes. * @@ -81,7 +93,6 @@ public class Plane implements Serializable { double normalLength = normal.length(); normal.normalize(); distance /= normalLength; // Do we really want this? --ro - this.normSquare = normal.dot(normal); } /** @@ -111,16 +122,79 @@ public class Plane implements Serializable { ret.scale(distance); return ret; } - + /** - * Translate the plane along its normal - * - * @param value + * Returns a normalized vector laying on the plane and directing up (toward the plus Y axis). + * + * @return a normalized vector laying on the plane and directing up + * (toward the plus Y axis) or {@code null} */ - //public void shift(double value) { - // this.distance += value; - //} + public Vector3d getUpPlanarVector() { + Vector3d v = new Vector3d(projectToPlane(new Point3d(0,1,0))); + v.sub(projectToPlane(new Point3d(0,0,0))); + if (v.length() == 0) { + return null; + } + v.normalize(); + return v; + } + /** + * Returns a normalized vector laying on the plane and directing right (toward the plus X axis). + * + * @return a normalized vector laying on the plane and directing up + * (toward the plus X axis) or {@code null} + */ + public Vector3d getRightPlanarVector() { + Vector3d v = new Vector3d(projectToPlane(new Point3d(1,0,0))); + v.sub(projectToPlane(new Point3d(0,0,0))); + if (v.length() == 0) { + return null; + } + v.normalize(); + return v; + } + + /** + * Returns a normalized vector laying on the plane and directing ahead (toward the plus Z axis). + * + * @return a normalized vector laying on the plane and directing ahead + * (toward the plus Z axis) or {@code null} + */ + public Vector3d getFrontPlanarVector() { + Vector3d v = new Vector3d(projectToPlane(new Point3d(0,0,1))); + v.sub(projectToPlane(new Point3d(0,0,0))); + if (v.length() == 0) { + return null; + } + v.normalize(); + return v; + } + + /** + * Returns a point laying on the plane that is different + * from the {@link #getPlanePoint()} + * + * @return another point laying on the plane + */ + public Point3d getAnotherPoint() { + Point3d p = getPlanePoint(); + double x = Math.abs(getNormal().x); + double y = Math.abs(getNormal().y); + double z = Math.abs(getNormal().z); + + if (x >= Math.max(y, z)) { + p.y += 1000; + } else if (y >= Math.max(x, y)) { + p.x += 1000; + } else { + p.z += 1000; + } + + return projectToPlane(p); + } + + /** * Translate the plane along its normal * @@ -144,6 +218,64 @@ public class Plane implements Serializable { return ret; } + /** + * Computes and returns transformation matrix that transforms given plane + * into this plane. + * + * @param other plane to be transformed + * @param forbidRotation If {@code false}, then the rotation around + * the normal is ignored. + * @return transformation matrix or {@code null} + */ + public Matrix4d getAlignmentMatrix(Plane other, boolean forbidRotation) { + if (other == null) { + return null; + } + + Matrix4d retMat = getRotationAroundAxis(this.normal, other.getNormal(), null); // normal's rotation + + Point3d p = other.getPlanePoint(); + retMat.transform(p); + Vector3d trDir = new Vector3d(this.getPlanePoint()); + + if (p.equals(trDir)) { // no translation + return retMat; + } + + trDir.sub(p); + + Matrix4d trMat = new Matrix4d( // translation + 0, 0, 0, trDir.x, + 0, 0, 0, trDir.y, + 0, 0, 0, trDir.z, + 0, 0, 0, 0 + ); + retMat.add(trMat); + + if (forbidRotation) { + Vector3d myVec = this.getUpPlanarVector(); + Vector3d otherVec = other.getUpPlanarVector(); + if (myVec == null || otherVec == null) { + myVec = this.getRightPlanarVector(); + otherVec = other.getRightPlanarVector(); + } + if (myVec == null || otherVec == null) { + myVec = this.getFrontPlanarVector(); + otherVec = other.getFrontPlanarVector(); + } + + if (myVec != null && otherVec != null) { + retMat.transform(otherVec); // now, it lays on me with random orientation + Vector3d rotAxis = new Vector3d(myVec); + rotAxis.cross(rotAxis, otherVec); + Matrix4d axisTrMat = getRotationAroundAxis(myVec, otherVec, rotAxis); + retMat.mul(axisTrMat, retMat); + } + } + + return retMat; + } + /** * Returns a point laying on the plane. * @@ -248,7 +380,6 @@ public class Plane implements Serializable { throw new IllegalArgumentException("noraml"); } this.normal = new Vector3d(normal); - this.normSquare = this.normal.dot(this.normal); } /** @@ -297,4 +428,46 @@ public class Plane implements Serializable { return output; } + + /** + * Computes transformation matrix that rotates the given normal vector + * so that it fits our normal vector. + * Based on https://math.stackexchange.com/questions/180418/calculate-rotation-matrix-to-align-vector-a-to-vector-b-in-3d + * + * @param targetV Vector towards which we want to rotate the second vector + * @param trV Vector to be rotated + * @param axis Rotation axis. If {@code null}, then it's automatically computed + * @return transformation matrix + */ + protected static Matrix4d getRotationAroundAxis(Vector3d targetV, Vector3d trV, Vector3d axis) { + Matrix4d rotMat = new Matrix4d(); + rotMat.setIdentity(); + + double cos = targetV.dot(trV); // cosine of the angle + if (cos == -1) { // exactly oposite direction + rotMat.mul(-1.0); + return rotMat; + } + + Vector3d k = axis; + if (k == null) { + k = new Vector3d(); + k.cross(trV, targetV); + } + + Matrix4d cpMat = new Matrix4d( // skew-symmetric cross-product matrix of v + 0.0, -k.z, k.y, 0.0, + k.z, 0.0, -k.x, 0.0, + -k.y, k.x, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0 + ); + rotMat.add(cpMat); + + cpMat.mul(cpMat); + cpMat.mul(1.0 / (1.0 + cos)); + rotMat.add(cpMat); + + return rotMat; + } + } diff --git a/Comparison/src/test/java/cz/fidentis/analyst/symmetry/PlaneTest.java b/Comparison/src/test/java/cz/fidentis/analyst/symmetry/PlaneTest.java index 1e053ae74c4002d23b66100de000a94c4c1a4256..3d24252bd963f0beca7866059b60f76ed5c623f6 100644 --- a/Comparison/src/test/java/cz/fidentis/analyst/symmetry/PlaneTest.java +++ b/Comparison/src/test/java/cz/fidentis/analyst/symmetry/PlaneTest.java @@ -126,4 +126,73 @@ public class PlaneTest { Assertions.assertEquals(-6, p.getPointDistance(new Point3d(-9, -9, -9))); Assertions.assertEquals(3, p.getPointDistance(new Point3d(0, 0, 0))); } + + @Test + public void getRotationAroundAxis() { + Plane p = new Plane(new Vector3d(1, 0, 0), 0); + + Vector3d n = new Vector3d(1, 0, 0); + Plane.getRotationAroundAxis(p.getNormal(), n, null).transform(n); + Assertions.assertEquals(p.getNormal(), n); + + n = new Vector3d(-1, 0, 0); + Plane.getRotationAroundAxis(p.getNormal(), n, null).transform(n); + Assertions.assertEquals(p.getNormal(), n); + + n = new Vector3d(0, 1, 0); + Plane.getRotationAroundAxis(p.getNormal(), n, null).transform(n); + Assertions.assertEquals(p.getNormal(), n); + + n = new Vector3d(0, -1, 0); + Plane.getRotationAroundAxis(p.getNormal(), n, null).transform(n); + Assertions.assertEquals(p.getNormal(), n); + + n = new Vector3d(1, 1, 1); + n.normalize(); + Plane.getRotationAroundAxis(p.getNormal(), n, null).transform(n); + Assertions.assertTrue(p.getNormal().epsilonEquals(n, 0.001)); + + n = new Vector3d(-1, -1, -1); + n.normalize(); + Plane.getRotationAroundAxis(p.getNormal(), n, null).transform(n); + Assertions.assertTrue(p.getNormal().epsilonEquals(n, 0.001)); + + } + + @Test + public void getAlignmentMatrix() { + Vector3d n1 = new Vector3d(1, 0, 0); + n1.normalize(); + Plane plane1 = new Plane(n1, 3); + + Vector3d n2 = new Vector3d(0, 1, 0); + n2.normalize(); + Plane plane2 = new Plane(n2, 3); + Point3d p = plane2.getPlanePoint(); + plane1.getAlignmentMatrix(plane2, true).transform(p); + Assertions.assertEquals(plane1.getPlanePoint(), p); + + n2 = new Vector3d(0, 1, 0); + n2.normalize(); + plane2 = new Plane(n2, -3); + p = plane2.getPlanePoint(); + plane1.getAlignmentMatrix(plane2, true).transform(p); + Assertions.assertEquals(plane1.getPlanePoint(), p); + + n2 = new Vector3d(-1, -1, -1); + n2.normalize(); + plane2 = new Plane(n2, -10); + p = plane2.getPlanePoint(); + plane1.getAlignmentMatrix(plane2, true).transform(p); + //Assertions.assertEquals(plane1.getPlanePoint(), p); + Assertions.assertTrue(plane1.getPlanePoint().epsilonEquals(p, 0.001)); + + n2 = new Vector3d(-1, -1, -1); + n2.normalize(); + plane2 = new Plane(n2, 10); + p = plane2.getPlanePoint(); + plane1.getAlignmentMatrix(plane2, true).transform(p); + //Assertions.assertEquals(plane1.getPlanePoint(), p); + Assertions.assertTrue(plane1.getPlanePoint().epsilonEquals(p, 0.001)); + } } diff --git a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java index 501773048cbc42e58d8ed81779cd12c641d5fd95..955780a042ee9f3c896b1dc44b8743a9115412a9 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java @@ -281,7 +281,7 @@ public class BatchPanel extends ControlPanel { ); } - private void exportAvgFace() { + private void exportAvgFace() { if (!haveAvgFace) { return; } diff --git a/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java b/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java index d82266ed172fbc583cc3917a52872c4ea0b79cad..d3146792114b593bba51035875b5882d911ae270 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java @@ -98,8 +98,21 @@ public class IcpTask extends SwingWorker<MeshModel, HumanFace> { String faceId = factory.loadFace(faces.get(i).toFile()); HumanFace face = factory.getFace(faceId); loadTime.stop(); - + if (computeICP) { // ICP registration - face is transformed! + + /* + ////////////////////// + SymmetryEstimator se = new SymmetryEstimator(new SymmetryConfig()); + face.getMeshModel().compute(se); + face.setSymmetryPlane(se.getSymmetryPlane()); + se = new SymmetryEstimator(new SymmetryConfig()); + initFace.getMeshModel().compute(se); + initFace.setSymmetryPlane(se.getSymmetryPlane()); + HumanFaceUtils.alignSymmetryPlanes(initFace, face, computeAvgFace, true); + ////////////////////// + */ + icpComputationTime.start(); HumanFaceUtils.alignMeshes( initFace, diff --git a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java index a18d4d5e6dad070bcb24673b9da4b8658193ddae..70cb46e723aa1bbc9167357791e3a602f755e638 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java @@ -3,17 +3,14 @@ package cz.fidentis.analyst.registration; 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.HumanFaceUtils; import cz.fidentis.analyst.face.events.HausdorffDistanceComputed; import cz.fidentis.analyst.face.events.HumanFaceEvent; import cz.fidentis.analyst.face.events.HumanFaceListener; import cz.fidentis.analyst.face.events.HumanFaceTransformedEvent; -import cz.fidentis.analyst.procrustes.ProcrustesAnalysis; +import cz.fidentis.analyst.face.events.SymmetryPlaneChangedEvent; import java.awt.Color; import java.awt.event.ActionEvent; -import java.util.zip.DataFormatException; -import javax.swing.JCheckBox; import javax.swing.JFormattedTextField; import javax.swing.JOptionPane; import javax.swing.JTabbedPane; @@ -48,14 +45,10 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL /* * Attributes handling the state */ - private boolean scale = true; - private int maxIterations = 10; - private double error = 0.3; private String strategy = RegistrationPanel.STRATEGY_POINT_TO_POINT; private boolean relativeDist = false; private boolean heatmapRender = false; - private boolean procrustesScalingEnabled = false; - + /* * Coloring threshold and statistical values of feature point distances: */ @@ -91,19 +84,26 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL // Be informed about changes in faces perfomed by other GUI elements getPrimaryDrawableFace().getHumanFace().registerListener(this); getSecondaryDrawableFace().getHumanFace().registerListener(this); + + controlPanel.setEnabledPlanesButton( + getCanvas().getPrimaryFace().hasSymmetryPlane() && + getCanvas().getSecondaryFace().hasSymmetryPlane() + ); + + controlPanel.setEnabledProcrustesButton( + getCanvas().getPrimaryFace().hasFeaturePoints() && + getCanvas().getSecondaryFace().hasFeaturePoints() + ); } @Override public void actionPerformed(ActionEvent ae) { - double value; String action = ae.getActionCommand(); -// OutputWindow.print(ae.getActionCommand()); switch (action) { case RegistrationPanel.ACTION_COMMAND_APPLY_ICP: applyICP(); highlightCloseFeaturePoints(); - //announceMeshChange(getSecondaryDrawableFace()); getCanvas().getSecondaryFace().announceEvent(new HumanFaceTransformedEvent( getCanvas().getSecondaryFace(), "", this) ); @@ -125,24 +125,11 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL getCanvas().getSecondaryFace().announceEvent(new HumanFaceTransformedEvent( getCanvas().getSecondaryFace(), "", this, true) // finished transformation ); - //announceMeshChange(getSecondaryDrawableFace()); break; case RegistrationPanel.ACTION_COMMAND_FP_CLOSENESS_THRESHOLD: fpThreshold = ((Number) (((JFormattedTextField) ae.getSource()).getValue())).doubleValue(); highlightCloseFeaturePoints(); break; - case RegistrationPanel.ACTION_COMMAND_ICP_SCALE: - this.scale = ((JCheckBox) ae.getSource()).isSelected(); - break; - case RegistrationPanel.ACTION_COMMAND_ICP_MAX_ITERATIONS: - maxIterations = ((Number) (((JFormattedTextField) ae.getSource()).getValue())).intValue(); - break; - case RegistrationPanel.ACTION_COMMAND_ICP_ERROR: - error = ((Number) (((JFormattedTextField) ae.getSource()).getValue())).doubleValue(); - break; - case RegistrationPanel.ACTION_COMMAND_PROCRUSTES_SCALE: - this.procrustesScalingEnabled = ((JCheckBox) ae.getSource()).isSelected(); - break; case RegistrationPanel.ACTION_COMMAND_PROCRUSTES_APPLY: applyProcrustes(); highlightCloseFeaturePoints(); @@ -152,8 +139,6 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL getCanvas().getSecondaryFace().announceEvent(new HumanFaceTransformedEvent( getCanvas().getSecondaryFace(), "", this) ); - //announceMeshChange(getPrimaryDrawableFace()); - //announceMeshChange(getSecondaryDrawableFace()); break; case RegistrationPanel.ACTION_COMMAND_ALIGN_SYMMETRY_PLANES: alignSymmetryPlanes(); @@ -161,7 +146,6 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL getCanvas().getSecondaryFace().announceEvent(new HumanFaceTransformedEvent( getCanvas().getSecondaryFace(), "", this) ); - //announceMeshChange(getSecondaryDrawableFace()); break; default: // do nothing @@ -183,6 +167,13 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL hdEvent.getWeightedHusdorffDistStats() ); } + + if (event instanceof SymmetryPlaneChangedEvent) { + controlPanel.setEnabledPlanesButton( + getCanvas().getPrimaryFace().hasSymmetryPlane() && + getCanvas().getSecondaryFace().hasSymmetryPlane() + ); + } } /** @@ -196,27 +187,27 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL * If the {@code procrustesScalingEnabled} attribute is set to true, then one of the faces is scaled as well. */ protected void applyProcrustes() { + JOptionPane.showMessageDialog( + controlPanel, + "This method is not finished yet", + "Neither feature points nor symmetry planes are transformed properly", + JOptionPane.INFORMATION_MESSAGE); + Logger out = Logger.measureTime(); - - HumanFace primaryFace = getCanvas().getPrimaryFace(); - HumanFace secondaryFace = getCanvas().getSecondaryFace(); - - try { - ProcrustesAnalysis procrustesAnalysisInit = new ProcrustesAnalysis(primaryFace, secondaryFace, this.procrustesScalingEnabled); - procrustesAnalysisInit.analyze(); - primaryFace.removeKdTree(); // invalidate k-d tree, if exists - secondaryFace.removeKdTree(); // invalidate k-d tree, if exists - highlightCloseFeaturePoints(); - } catch (DataFormatException e) { - Logger.print("Procrustes Analysis experienced exception"); - } + + HumanFaceUtils.alignFeaturePoints( + getCanvas().getPrimaryFace(), + getCanvas().getSecondaryFace(), + controlPanel.getScaleParam(), + false // drop k-d tree, if exists + ); out.printDuration("Procrustes for models with " - + getPrimaryDrawableFace().getHumanFace().getMeshModel().getNumVertices() + + getCanvas().getPrimaryFace().getMeshModel().getNumVertices() + "/" - + getSecondaryDrawableFace().getHumanFace().getMeshModel().getNumVertices() + + getCanvas().getSecondaryFace().getMeshModel().getNumVertices() + " vertices and " - + getPrimaryDrawableFace().getHumanFace().getFeaturePoints().size() + + getCanvas().getPrimaryFace().getFeaturePoints().size() + " feature points" ); } @@ -225,19 +216,19 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL Logger out = Logger.measureTime(); HumanFaceUtils.alignMeshes( - getPrimaryDrawableFace().getHumanFace(), - getSecondaryDrawableFace().getHumanFace(), // is transformed - maxIterations, - scale, - error, + getCanvas().getPrimaryFace(), + getCanvas().getSecondaryFace(), // is transformed + controlPanel.getMaxIcpIterParam(), + controlPanel.getScaleParam(), + controlPanel.getMinIcpErrorParam(), controlPanel.getIcpUndersampling(), false // drop k-d tree, if exists ); out.printDuration("ICP for models with " - + getPrimaryDrawableFace().getHumanFace().getMeshModel().getNumVertices() + + getCanvas().getPrimaryFace().getMeshModel().getNumVertices() + "/" - + getSecondaryDrawableFace().getHumanFace().getMeshModel().getNumVertices() + + getCanvas().getSecondaryFace().getMeshModel().getNumVertices() + " vertices" ); } @@ -279,27 +270,12 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL // to do: show ACF dist } - //protected void announceMeshChange(DrawableFace face) { - // if (face != null) { - // face.getHumanFace().announceEvent(new MeshChangedEvent(face.getHumanFace(), face.getHumanFace().getShortName(), this)); - // } - //} - private void alignSymmetryPlanes() { - if (!getPrimaryDrawableFace().getHumanFace().hasSymmetryPlane() - || !getSecondaryDrawableFace().getHumanFace().hasSymmetryPlane()) { - - JOptionPane.showMessageDialog(controlPanel, - "Compute symmetry planes first", - "No symmerty planes", - JOptionPane.INFORMATION_MESSAGE); - return; - } - HumanFaceUtils.alignSymmetryPlanes( getPrimaryDrawableFace().getHumanFace(), getSecondaryDrawableFace().getHumanFace(), // is transformed - false // drops k-d tree, if exists + false, // drops k-d tree, if exists + true // forbid rotation around normal ); } } diff --git a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.form b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.form index 754e92b26aadcb71ca82f2aaea191c0dc879981c..d7774fda01f026a5626269f363185cb29cd662c7 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.form +++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.form @@ -47,7 +47,6 @@ <Component id="jPanel1" alignment="0" max="32767" attributes="0"/> <Component id="jPanel4" alignment="0" max="32767" attributes="0"/> <Component id="transformationPanel" alignment="0" min="-2" pref="585" max="-2" attributes="0"/> - <Component id="jPanel7" max="32767" attributes="0"/> </Group> <EmptySpace max="32767" attributes="0"/> <Component id="jSeparator7" min="-2" pref="50" max="-2" attributes="0"/> @@ -69,18 +68,16 @@ </Group> <Group type="102" alignment="0" attributes="0"> <EmptySpace max="-2" attributes="0"/> - <Component id="jPanel7" min="-2" max="-2" attributes="0"/> + <Component id="jPanel1" min="-2" max="-2" attributes="0"/> </Group> </Group> <EmptySpace max="-2" attributes="0"/> - <Component id="jPanel1" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="transformationPanel" min="-2" pref="176" max="-2" attributes="0"/> + <Component id="transformationPanel" min="-2" pref="134" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="jPanel4" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="jPanel2" min="-2" max="-2" attributes="0"/> - <EmptySpace min="-2" pref="328" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="451" max="-2" attributes="0"/> <Component id="jSeparator11" min="-2" pref="10" max="-2" attributes="0"/> <EmptySpace min="-2" pref="221" max="-2" attributes="0"/> <Component id="jSeparator2" min="-2" pref="10" max="-2" attributes="0"/> @@ -120,10 +117,6 @@ <EmptySpace min="-2" pref="580" max="-2" attributes="0"/> <Component id="shiftPanel" min="-2" max="-2" attributes="0"/> </Group> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace max="-2" attributes="0"/> - <Component id="jButton3" min="-2" max="-2" attributes="0"/> - </Group> <Group type="102" alignment="0" attributes="0"> <Component id="translationPanel" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> @@ -146,8 +139,6 @@ <Component id="translationPanel" alignment="0" max="32767" attributes="0"/> </Group> <EmptySpace max="32767" attributes="0"/> - <Component id="jButton3" min="-2" max="-2" attributes="0"/> - <EmptySpace min="-2" pref="59" max="-2" attributes="0"/> <Component id="shiftPanel" min="-2" max="-2" attributes="0"/> <EmptySpace max="32767" attributes="0"/> <Component id="jSeparator5" min="-2" pref="10" max="-2" attributes="0"/> @@ -883,13 +874,6 @@ </Container> <Component class="javax.swing.JSeparator" name="jSeparator5"> </Component> - <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/registration/Bundle.properties" key="RegistrationPanel.jButton3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> - </Property> - </Properties> - </Component> </SubComponents> </Container> <Component class="javax.swing.JSeparator" name="jSeparator2"> @@ -898,7 +882,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="Mesh alignment (ICP):"> + <TitledBorder title="Auto-alignment"> <ResourceString PropertyName="titleX" bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jPanel1.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> <Font PropertyName="font" name="Dialog" size="14" style="1"/> </TitledBorder> @@ -915,74 +899,70 @@ <Group type="102" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <Component id="jLabel7" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/> - <EmptySpace type="separate" max="-2" attributes="0"/> - <Component id="jLabel5" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="jFormattedTextField1" min="-2" pref="49" max="-2" attributes="0"/> - </Group> - <Component id="comboSliderInteger1" alignment="0" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace min="-2" pref="12" max="-2" attributes="0"/> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> - <Component id="jLabel6" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="jFormattedTextField2" min="-2" pref="53" max="-2" attributes="0"/> - </Group> - <Component id="jLabel8" min="-2" max="-2" attributes="0"/> - </Group> + <Group type="102" alignment="0" 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"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="jButton3" min="-2" max="-2" attributes="0"/> + <EmptySpace type="unrelated" max="-2" attributes="0"/> + <Component id="jButtonInfo1" min="-2" max="-2" attributes="0"/> + </Group> + <Group type="102" alignment="0" attributes="0"> + <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Component id="jLabel5" min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="jFormattedTextField1" min="-2" pref="49" max="-2" attributes="0"/> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Component id="jLabel6" min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="jFormattedTextField2" min="-2" pref="53" max="-2" attributes="0"/> </Group> - <Component id="jButton1" alignment="0" min="-2" max="-2" attributes="0"/> </Group> <EmptySpace max="32767" attributes="0"/> </Group> + <Group type="102" alignment="0" attributes="0"> + <Component id="comboSliderInteger1" min="-2" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="12" max="-2" attributes="0"/> + <Component id="jLabel8" min="-2" max="-2" attributes="0"/> + <EmptySpace min="0" pref="0" max="32767" attributes="0"/> + </Group> </Group> </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0"> <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="0" attributes="0"> - <Component id="jLabel7" min="-2" max="-2" attributes="0"/> - <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" max="-2" attributes="0"> <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="jFormattedTextField1" alignment="3" min="-2" pref="22" max="-2" attributes="0"/> - <Component id="jLabel6" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="jFormattedTextField2" alignment="3" min="-2" pref="20" max="-2" attributes="0"/> + <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jButton3" alignment="3" min="-2" max="-2" attributes="0"/> </Group> + <Component id="jButtonInfo1" pref="0" max="32767" attributes="0"/> </Group> + <EmptySpace pref="16" max="32767" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jCheckBox1" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jFormattedTextField1" alignment="3" min="-2" pref="22" max="-2" attributes="0"/> + <Component id="jLabel6" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jFormattedTextField2" alignment="3" min="-2" pref="20" max="-2" attributes="0"/> + </Group> + <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> - <EmptySpace min="-2" pref="5" max="-2" attributes="0"/> - <Component id="comboSliderInteger1" min="-2" max="-2" attributes="0"/> - </Group> + <Component id="comboSliderInteger1" alignment="0" min="-2" max="-2" attributes="0"/> <Group type="102" alignment="0" attributes="0"> - <EmptySpace min="-2" pref="26" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="21" max="-2" attributes="0"/> <Component id="jLabel8" min="-2" max="-2" attributes="0"/> </Group> </Group> - <EmptySpace max="-2" attributes="0"/> - <Component id="jButton1" min="-2" max="-2" attributes="0"/> - <EmptySpace max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> </Layout> <SubComponents> - <Component class="javax.swing.JLabel" name="jLabel7"> - <Properties> - <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jLabel7.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> - </Property> - </Properties> - </Component> <Component class="javax.swing.JCheckBox" name="jCheckBox1"> <Properties> <Property name="selected" type="boolean" value="true"/> @@ -1035,6 +1015,8 @@ </Property> </Properties> </Component> + <Component class="cz.fidentis.analyst.core.ComboSliderInteger" name="comboSliderInteger1"> + </Component> <Component class="javax.swing.JButton" name="jButton1"> <Properties> <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> @@ -1051,7 +1033,51 @@ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/> </Events> </Component> - <Component class="cz.fidentis.analyst.core.ComboSliderInteger" name="comboSliderInteger1"> + <Component class="javax.swing.JButton" name="jButton2"> + <Properties> + <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> + <Font name="Ubuntu" size="15" style="1"/> + </Property> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jButton2.toolTipText" 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> + <Component class="javax.swing.JButton" name="jButton3"> + <Properties> + <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> + <Font name="Ubuntu" size="15" style="1"/> + </Property> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jButton3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + </Component> + <Component class="javax.swing.JButton" name="jButtonInfo1"> + <Properties> + <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> + <Image iconType="3" name="/info.png"/> + </Property> + <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> + <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jButtonInfo1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> + <Border info="org.netbeans.modules.form.compat2.border.EmptyBorderInfo"> + <EmptyBorder/> + </Border> + </Property> + <Property name="borderPainted" type="boolean" value="false"/> + <Property name="focusPainted" type="boolean" value="false"/> + <Property name="focusable" type="boolean" value="false"/> + <Property name="requestFocusEnabled" type="boolean" value="false"/> + <Property name="rolloverEnabled" type="boolean" value="false"/> + </Properties> </Component> </SubComponents> </Container> @@ -1186,88 +1212,6 @@ </Container> <Component class="javax.swing.JSeparator" name="jSeparator7"> </Component> - <Container class="javax.swing.JPanel" name="jPanel7"> - <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="Feature points alignment (Procrustes):"> - <ResourceString PropertyName="titleX" bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jPanel7.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> - <Font PropertyName="font" name="Dialog" size="14" style="1"/> - </TitledBorder> - </Border> - </Property> - </Properties> - - <Layout> - <DimensionLayout dim="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace max="-2" attributes="0"/> - <Component id="jButton2" min="-2" max="-2" attributes="0"/> - <EmptySpace type="separate" max="-2" attributes="0"/> - <Component id="jLabel4" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="jCheckBox2" min="-2" max="-2" attributes="0"/> - <EmptySpace max="32767" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - <DimensionLayout dim="1"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="0" max="-2" attributes="0"> - <Component id="jCheckBox2" alignment="1" max="32767" attributes="0"/> - <Group type="103" alignment="1" groupAlignment="3" attributes="0"> - <Component id="jLabel4" alignment="3" max="32767" attributes="0"/> - <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - </Group> - <EmptySpace max="32767" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - </Layout> - <SubComponents> - <Component class="javax.swing.JButton" name="jButton2"> - <Properties> - <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> - <Font name="Ubuntu" size="15" style="1"/> - </Property> - <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> - </Property> - <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jButton2.toolTipText" 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> - <Component class="javax.swing.JLabel" name="jLabel4"> - <Properties> - <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> - <Font name="Ubuntu" size="15" style="0"/> - </Property> - <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jLabel4.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> - </Property> - </Properties> - </Component> - <Component class="javax.swing.JCheckBox" name="jCheckBox2"> - <Properties> - <Property name="selected" type="boolean" value="true"/> - <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jCheckBox2.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="jCheckBox2ActionPerformed"/> - </Events> - </Component> - </SubComponents> - </Container> <Container class="javax.swing.JPanel" name="jPanel2"> <Properties> <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> diff --git a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.java b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.java index d3eaa22f72b69ddaeb602a843566a5527c75a4a4..82b45c3ce16654e289143d52f82cfc94b6135983 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.java @@ -6,6 +6,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.DoubleSummaryStatistics; import javax.swing.ImageIcon; +import javax.swing.JOptionPane; import javax.vecmath.Vector3d; /** @@ -32,12 +33,7 @@ public class RegistrationPanel extends ControlPanel { public static final String ACTION_COMMAND_FP_CLOSENESS_THRESHOLD = "fp closeness treshold"; - public static final String ACTION_COMMAND_ICP_SCALE = "ICP scale"; - public static final String ACTION_COMMAND_ICP_MAX_ITERATIONS = "ICP iterations"; - public static final String ACTION_COMMAND_ICP_ERROR = "ICP error"; - public static final String ACTION_COMMAND_PROCRUSTES_APPLY = "Procrustes apply"; - public static final String ACTION_COMMAND_PROCRUSTES_SCALE = "Procrustes scale"; public static final String ACTION_COMMAND_ALIGN_SYMMETRY_PLANES = "align symmetry planes"; @@ -82,16 +78,16 @@ public class RegistrationPanel extends ControlPanel { thersholdFTF.setValue(5.0); thersholdFTF.addActionListener(createListener(action, ACTION_COMMAND_FP_CLOSENESS_THRESHOLD)); - jCheckBox1.addActionListener(createListener(action, ACTION_COMMAND_ICP_SCALE)); - jFormattedTextField1.addActionListener(createListener(action, ACTION_COMMAND_ICP_ERROR)); - jFormattedTextField2.addActionListener(createListener(action, ACTION_COMMAND_ICP_MAX_ITERATIONS)); - //comboSliderInteger1.setSliderEast(); comboSliderInteger1.setRange(10, 100); comboSliderInteger1.setValue(this.undersampling); comboSliderInteger1.addInputFieldListener((ActionEvent ae) -> { // update slider when the input field changed this.undersampling = comboSliderInteger1.getValue(); }); + + jButtonInfo1.addActionListener((ActionEvent e) -> { + showAutoAlignmentInfo(); + }); } /** @@ -131,7 +127,24 @@ public class RegistrationPanel extends ControlPanel { public int getIcpUndersampling() { return undersampling; } - + + /** + * Turns ob or off the plane alignment button + * @param on on-off value + */ + public void setEnabledPlanesButton(boolean on) { + jButton3.setEnabled(on); + jButton3.setFont(new java.awt.Font("Ubuntu", (on) ? 1 : 0, 15)); // NOI18N + } + + /** + * Turns ob or off the plane alignment button + * @param on on-off value + */ + public void setEnabledProcrustesButton(boolean on) { + jButton2.setEnabled(on); + jButton2.setFont(new java.awt.Font("Ubuntu", (on) ? 1 : 0, 15)); // NOI18N + } /** * Updates GUI elements that display statistical data about the calculated Hausdorff distance. @@ -144,6 +157,18 @@ public class RegistrationPanel extends ControlPanel { jTextField2.setText(String.format("%.3f", whd.getAverage())); } + public boolean getScaleParam() { + return jCheckBox1.isSelected(); + } + + public int getMaxIcpIterParam() { + return ((Number) jFormattedTextField2.getValue()).intValue(); + } + + public double getMinIcpErrorParam() { + return ((Number) jFormattedTextField2.getValue()).doubleValue(); + } + /** * Alters transformation based on move amount specified in listener * by updating specific formatted field @@ -219,6 +244,29 @@ public class RegistrationPanel extends ControlPanel { public static ImageIcon getStaticIcon() { return new ImageIcon(RegistrationPanel.class.getClassLoader().getResource("/" + ICON)); } + + private void showAutoAlignmentInfo() { + JOptionPane.showMessageDialog( + this, + "<html>" + + "<strong>Mesh</strong>: <br/>" + + "Superimposition of two faces by applying iterative closest points (ICP) algorithm<br/>" + + "on their triangular meshes. This approach is slow, unsharp, but universal.<br/>" + + "<br/>" + + "<strong>Feature points</strong>: <br/>" + + "Superimposition of two faces by applying Procrustes algorithm<br/>" + + "on feature points. It is fast, precise, but feature points of the same types<br/>" + + "have to be presented for both faces.<br/>" + + "<br/>" + + "<strong>Symmetry planes</strong>: <br/>" + + "Superimposition of two faces by aligning their symmetry planes.<br/>" + + "Symmetry planes have to be computed on the \"<i>Symmetry</i>\" tab first<br/>" + + "to enable this feature.<br/>" + + "</html>", + "Distance computation strategies", + JOptionPane.INFORMATION_MESSAGE + ); + } /** * This method is called from within the constructor to initialize the form. @@ -259,28 +307,25 @@ public class RegistrationPanel extends ControlPanel { scaleMinusButton = new javax.swing.JButton(); shiftPanel = new javax.swing.JPanel(); jSeparator5 = new javax.swing.JSeparator(); - jButton3 = new javax.swing.JButton(); jSeparator2 = new javax.swing.JSeparator(); jPanel1 = new javax.swing.JPanel(); - jLabel7 = new javax.swing.JLabel(); jCheckBox1 = new javax.swing.JCheckBox(); jLabel5 = new javax.swing.JLabel(); jFormattedTextField1 = new javax.swing.JFormattedTextField(); jLabel6 = new javax.swing.JLabel(); jFormattedTextField2 = new javax.swing.JFormattedTextField(); jLabel8 = new javax.swing.JLabel(); - jButton1 = new javax.swing.JButton(); comboSliderInteger1 = new cz.fidentis.analyst.core.ComboSliderInteger(); + jButton1 = new javax.swing.JButton(); + jButton2 = new javax.swing.JButton(); + jButton3 = new javax.swing.JButton(); + jButtonInfo1 = new javax.swing.JButton(); jPanel4 = new javax.swing.JPanel(); featurePointsLabel = new javax.swing.JLabel(); thersholdFTF = new javax.swing.JFormattedTextField(); thersholdUpButton = new javax.swing.JButton(); thresholdDownButton = new javax.swing.JButton(); jSeparator7 = new javax.swing.JSeparator(); - jPanel7 = new javax.swing.JPanel(); - jButton2 = new javax.swing.JButton(); - jLabel4 = new javax.swing.JLabel(); - jCheckBox2 = new javax.swing.JCheckBox(); jPanel2 = new javax.swing.JPanel(); jLabel1 = new javax.swing.JLabel(); jTextField1 = new javax.swing.JTextField(); @@ -685,8 +730,6 @@ public class RegistrationPanel extends ControlPanel { .addGap(0, 84, Short.MAX_VALUE) ); - org.openide.awt.Mnemonics.setLocalizedText(jButton3, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jButton3.text")); // NOI18N - javax.swing.GroupLayout transformationPanelLayout = new javax.swing.GroupLayout(transformationPanel); transformationPanel.setLayout(transformationPanelLayout); transformationPanelLayout.setHorizontalGroup( @@ -699,9 +742,6 @@ public class RegistrationPanel extends ControlPanel { .addGroup(transformationPanelLayout.createSequentialGroup() .addGap(580, 580, 580) .addComponent(shiftPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(transformationPanelLayout.createSequentialGroup() - .addContainerGap() - .addComponent(jButton3)) .addGroup(transformationPanelLayout.createSequentialGroup() .addComponent(translationPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -719,8 +759,6 @@ public class RegistrationPanel extends ControlPanel { .addComponent(rotationPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(translationPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton3) - .addGap(59, 59, 59) .addComponent(shiftPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jSeparator5, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)) @@ -729,8 +767,6 @@ public class RegistrationPanel extends ControlPanel { jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jPanel1.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 14))); // NOI18N jPanel1.setMaximumSize(new java.awt.Dimension(600, 32767)); - org.openide.awt.Mnemonics.setLocalizedText(jLabel7, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jLabel7.text")); // NOI18N - jCheckBox1.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(jCheckBox1, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jCheckBox1.text")); // NOI18N jCheckBox1.addActionListener(new java.awt.event.ActionListener() { @@ -760,6 +796,27 @@ public class RegistrationPanel extends ControlPanel { } }); + jButton2.setFont(new java.awt.Font("Ubuntu", 1, 15)); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jButton2, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jButton2.text")); // NOI18N + jButton2.setToolTipText(org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jButton2.toolTipText")); // NOI18N + jButton2.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton2ActionPerformed(evt); + } + }); + + jButton3.setFont(new java.awt.Font("Ubuntu", 1, 15)); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jButton3, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jButton3.text")); // NOI18N + + jButtonInfo1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/info.png"))); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jButtonInfo1, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jButtonInfo1.text")); // NOI18N + jButtonInfo1.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); + jButtonInfo1.setBorderPainted(false); + jButtonInfo1.setFocusPainted(false); + jButtonInfo1.setFocusable(false); + jButtonInfo1.setRequestFocusEnabled(false); + jButtonInfo1.setRolloverEnabled(false); + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( @@ -768,48 +825,53 @@ public class RegistrationPanel extends ControlPanel { .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(jLabel7) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jCheckBox1) - .addGap(18, 18, 18) - .addComponent(jLabel5) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jFormattedTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(comboSliderInteger1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(12, 12, 12) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(jLabel6) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jFormattedTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(jLabel8))) - .addComponent(jButton1)) + .addComponent(jButton1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jButton2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jButton3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jButtonInfo1)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(jCheckBox1) + .addGap(18, 18, 18) + .addComponent(jLabel5) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jFormattedTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addComponent(jLabel6) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jFormattedTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(comboSliderInteger1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(12, 12, 12) + .addComponent(jLabel8) + .addGap(0, 0, Short.MAX_VALUE)) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel7) - .addComponent(jCheckBox1) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel5) - .addComponent(jFormattedTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel6) - .addComponent(jFormattedTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(jButton1) + .addComponent(jButton2) + .addComponent(jButton3)) + .addComponent(jButtonInfo1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 16, Short.MAX_VALUE) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jCheckBox1) + .addComponent(jLabel5) + .addComponent(jFormattedTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel6) + .addComponent(jFormattedTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(comboSliderInteger1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(comboSliderInteger1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(26, 26, 26) - .addComponent(jLabel8))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jButton1) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGap(21, 21, 21) + .addComponent(jLabel8)))) ); jPanel4.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jPanel4.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 14))); // NOI18N @@ -875,53 +937,6 @@ public class RegistrationPanel extends ControlPanel { .addGap(0, 8, Short.MAX_VALUE)) ); - jPanel7.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jPanel7.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 14))); // NOI18N - - jButton2.setFont(new java.awt.Font("Ubuntu", 1, 15)); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(jButton2, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jButton2.text")); // NOI18N - jButton2.setToolTipText(org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jButton2.toolTipText")); // NOI18N - jButton2.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton2ActionPerformed(evt); - } - }); - - jLabel4.setFont(new java.awt.Font("Ubuntu", 0, 15)); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(jLabel4, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jLabel4.text")); // NOI18N - - jCheckBox2.setSelected(true); - org.openide.awt.Mnemonics.setLocalizedText(jCheckBox2, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jCheckBox2.text")); // NOI18N - jCheckBox2.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jCheckBox2ActionPerformed(evt); - } - }); - - javax.swing.GroupLayout jPanel7Layout = new javax.swing.GroupLayout(jPanel7); - jPanel7.setLayout(jPanel7Layout); - jPanel7Layout.setHorizontalGroup( - jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel7Layout.createSequentialGroup() - .addContainerGap() - .addComponent(jButton2) - .addGap(18, 18, 18) - .addComponent(jLabel4) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jCheckBox2) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - jPanel7Layout.setVerticalGroup( - jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel7Layout.createSequentialGroup() - .addContainerGap() - .addGroup(jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(jCheckBox2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton2))) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jPanel2.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 14))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jLabel1.text")); // NOI18N @@ -978,8 +993,7 @@ public class RegistrationPanel extends ControlPanel { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jPanel4, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(transformationPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 585, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jPanel7, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(transformationPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 585, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jSeparator7, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() @@ -996,16 +1010,14 @@ public class RegistrationPanel extends ControlPanel { .addComponent(jSeparator7, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(jPanel7, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(transformationPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 176, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(transformationPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 134, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(328, 328, 328) + .addGap(451, 451, 451) .addComponent(jSeparator11, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(221, 221, 221) .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -1033,12 +1045,16 @@ public class RegistrationPanel extends ControlPanel { thersholdFTF.postActionEvent(); }//GEN-LAST:event_thersholdUpButtonActionPerformed + private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_jButton2ActionPerformed + private void scaleMinusButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_scaleMinusButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_scaleMinusButtonMouseReleased @@ -1049,9 +1065,9 @@ public class RegistrationPanel extends ControlPanel { private void scalePlusButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_scalePlusButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_scalePlusButtonMouseReleased @@ -1062,9 +1078,9 @@ public class RegistrationPanel extends ControlPanel { private void rightRotationXButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_rightRotationXButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_rightRotationXButtonMouseReleased @@ -1075,9 +1091,9 @@ public class RegistrationPanel extends ControlPanel { private void leftRotationZButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_leftRotationZButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_leftRotationZButtonMouseReleased @@ -1088,9 +1104,9 @@ public class RegistrationPanel extends ControlPanel { private void rightRotationYButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_rightRotationYButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_rightRotationYButtonMouseReleased @@ -1101,9 +1117,9 @@ public class RegistrationPanel extends ControlPanel { private void rightRotationZButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_rightRotationZButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_rightRotationZButtonMouseReleased @@ -1114,9 +1130,9 @@ public class RegistrationPanel extends ControlPanel { private void leftRotationXButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_leftRotationXButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_leftRotationXButtonMouseReleased @@ -1127,9 +1143,9 @@ public class RegistrationPanel extends ControlPanel { private void leftRotationYButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_leftRotationYButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_leftRotationYButtonMouseReleased @@ -1140,9 +1156,9 @@ public class RegistrationPanel extends ControlPanel { private void leftTranslationZButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_leftTranslationZButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_leftTranslationZButtonMouseReleased @@ -1153,9 +1169,9 @@ public class RegistrationPanel extends ControlPanel { private void leftTranslationXButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_leftTranslationXButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_leftTranslationXButtonMouseReleased @@ -1166,9 +1182,9 @@ public class RegistrationPanel extends ControlPanel { private void rightTranslationZButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_rightTranslationZButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_rightTranslationZButtonMouseReleased @@ -1179,9 +1195,9 @@ public class RegistrationPanel extends ControlPanel { private void rightTranslationYButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_rightTranslationYButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_rightTranslationYButtonMouseReleased @@ -1192,9 +1208,9 @@ public class RegistrationPanel extends ControlPanel { private void leftTranslationYButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_leftTranslationYButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_leftTranslationYButtonMouseReleased @@ -1205,9 +1221,9 @@ public class RegistrationPanel extends ControlPanel { private void rightTranslationXButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_rightTranslationXButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( - evt, - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) + evt, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED) ); }//GEN-LAST:event_rightTranslationXButtonMouseReleased @@ -1215,14 +1231,6 @@ public class RegistrationPanel extends ControlPanel { animator.startModelAnimation(Direction.TRANSLATE_RIGHT, this); }//GEN-LAST:event_rightTranslationXButtonMousePressed - private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_jButton2ActionPerformed - - private void jCheckBox2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox2ActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_jCheckBox2ActionPerformed - // Variables declaration - do not modify//GEN-BEGIN:variables private cz.fidentis.analyst.core.ComboSliderInteger comboSliderInteger1; @@ -1230,21 +1238,18 @@ public class RegistrationPanel extends ControlPanel { private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; private javax.swing.JButton jButton3; + private javax.swing.JButton jButtonInfo1; private javax.swing.JCheckBox jCheckBox1; - private javax.swing.JCheckBox jCheckBox2; private javax.swing.JFormattedTextField jFormattedTextField1; private javax.swing.JFormattedTextField jFormattedTextField2; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; - private javax.swing.JLabel jLabel4; private javax.swing.JLabel jLabel5; private javax.swing.JLabel jLabel6; - private javax.swing.JLabel jLabel7; private javax.swing.JLabel jLabel8; private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel2; private javax.swing.JPanel jPanel4; - private javax.swing.JPanel jPanel7; private javax.swing.JSeparator jSeparator11; private javax.swing.JSeparator jSeparator2; private javax.swing.JSeparator jSeparator5; diff --git a/GUI/src/main/resources/cz/fidentis/analyst/registration/Bundle.properties b/GUI/src/main/resources/cz/fidentis/analyst/registration/Bundle.properties index d966ae397d3f270a0cb9c713a30fb9e1f3bdbb2b..4d2dcd52e7983cb9e610f330eb44122e2075696f 100644 --- a/GUI/src/main/resources/cz/fidentis/analyst/registration/Bundle.properties +++ b/GUI/src/main/resources/cz/fidentis/analyst/registration/Bundle.properties @@ -1,16 +1,28 @@ -RegistrationPanel.jButton1.text=Apply -RegistrationPanel.jCheckBox1.text= +RegistrationPanel.jButton1.text=Mesh +RegistrationPanel.jCheckBox1.text=scale RegistrationPanel.jFormattedTextField1.text=0,3 -RegistrationPanel.jLabel5.text=min. error: +RegistrationPanel.jLabel5.text=min ICP error: RegistrationPanel.jFormattedTextField2.text=50 -RegistrationPanel.jLabel6.text=iterations: -RegistrationPanel.jLabel7.text=scale: -RegistrationPanel.jLabel8.text=undersampling (100% = none) +RegistrationPanel.jLabel6.text=ICP iterations: +RegistrationPanel.jLabel8.text=ICP undersampling (100% = none) RegistrationPanel.thersholdUpButton.text= RegistrationPanel.thresholdDownButton.text= RegistrationPanel.featurePointsLabel.text=Highlight feature point pairs closer than: 2 -RegistrationPanel.translationPanel.border.title=translation +RegistrationPanel.jButton2.text=Feature points +RegistrationPanel.jButton1.toolTipText=Apply ICP +RegistrationPanel.jButton2.toolTipText=Apply Procrustes +RegistrationPanel.jPanel1.border.title=Auto-alignment +RegistrationPanel.jPanel4.border.title=View: +RegistrationPanel.jLabel1.text=Hausdorff distance: +RegistrationPanel.jPanel2.border.title=Measurements: +RegistrationPanel.jTextField2.text= +RegistrationPanel.jLabel2.text=Weighted Hausdorff distance: +RegistrationPanel.jTextField1.text= +BatchRegistrationPanel.jPanel2.border.title=Dataset +BatchRegistrationPanel.jCheckBox1.text=compute average face from +BatchRegistrationPanel.jPanel3.border.title=Similarity +RegistrationPanel.jButton3.text=Symmetry planes RegistrationPanel.scaleMinusButton.text= RegistrationPanel.scalePlusButton.text= RegistrationPanel.scalePanel.border.title=scale @@ -36,21 +48,6 @@ RegistrationPanel.translXLabel.text=horizontal RegistrationPanel.rightTranslationYButton.text= RegistrationPanel.leftTranslationYButton.text= RegistrationPanel.rightTranslationXButton.text= -RegistrationPanel.jButton2.text=Apply -RegistrationPanel.jButton1.toolTipText=Apply ICP -RegistrationPanel.jButton2.toolTipText=Apply Procrustes -RegistrationPanel.jLabel4.text=scale: -RegistrationPanel.jCheckBox2.text= -RegistrationPanel.jPanel7.border.title=Feature points alignment (Procrustes): -RegistrationPanel.jPanel1.border.title=Mesh alignment (ICP): +RegistrationPanel.translationPanel.border.title=translation RegistrationPanel.transformationPanel.border.title=Manual alignment: -RegistrationPanel.jPanel4.border.title=View: -RegistrationPanel.jLabel1.text=Hausdorff distance: -RegistrationPanel.jPanel2.border.title=Measurements: -RegistrationPanel.jTextField2.text= -RegistrationPanel.jLabel2.text=Weighted Hausdorff distance: -RegistrationPanel.jTextField1.text= -BatchRegistrationPanel.jPanel2.border.title=Dataset -BatchRegistrationPanel.jCheckBox1.text=compute average face from -BatchRegistrationPanel.jPanel3.border.title=Similarity -RegistrationPanel.jButton3.text=Aling symmetry planes +RegistrationPanel.jButtonInfo1.text= 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 7d701dc0db0d8aaf8f9688261e3285077654d7e7..d55b3132ad38215f146d4262eb3c3fa170514d8f 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 @@ -106,9 +106,11 @@ public class MeshFacetImpl implements MeshFacet { for (MeshTriangle t : this) { Vector3d triangleNormal = new Vector3d(t.getPoint1().getPosition()); triangleNormal.sub(t.getPoint3().getPosition()); + triangleNormal.normalize(); Vector3d edge2 = new Vector3d(t.getPoint1().getPosition()); edge2.sub(t.getPoint2().getPosition()); + edge2.normalize(); triangleNormal.cross(triangleNormal, edge2); //Vector3d triangleNormal = (t.getPoint3().subtractPosition(t.getPoint1())).crossProduct(t.getPoint2().subtractPosition(t.getPoint1())).getPosition(); @@ -118,10 +120,13 @@ public class MeshFacetImpl implements MeshFacet { normalMap.get(t.getPoint3().getPosition()).add(triangleNormal); } - // normalize normals: normalMap.values().forEach(normal -> { normal.normalize(); }); + + for (MeshPoint p: getVertices()) { + p.setNormal(normalMap.get(p.getPosition())); + } } @Override 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 9d4ee4fe39f73445ec80a8b6a0b1137e4d1cd171..e7ec534f9a21d77c4fe63679c712dd6524d9de74 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 @@ -8,6 +8,7 @@ import java.io.Serializable; import java.util.Collection; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import javax.vecmath.Point3d; /** * The main object for triangular meshes. Each mesh model consists @@ -157,5 +158,26 @@ public class MeshModel implements Serializable { .stream() .mapToInt(f -> f.getNumberOfVertices()) .sum(); - } + } + + /** + * Returns central point computed from mesh vertices + * + * @return centroid + */ + public Point3d getCentroid() { + Point3d c = new Point3d(0, 0, 0); + final long size = getNumVertices(); + getFacets().stream() + .flatMap(f -> f.getVertices().parallelStream()) + .map(p -> p.getPosition()) + .forEach(p -> { + c.x += p.x / size; + c.y += p.y / size; + c.z += p.z / size; + }); + return c; + } + + } diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshPoint.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshPoint.java index ab67c9edef169daf9335fff21339c4f0315947f1..10b4419df15dc10c4f979f0108fda7a30f982dba 100644 --- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshPoint.java +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshPoint.java @@ -42,6 +42,12 @@ public interface MeshPoint extends IPosition, Serializable { * @param newPos New position, must not be {@code null} */ void setPosition(Point3d newPos); + + /** + * Sets the normal vector + * @param newNormal New normal vector or {@code null} + */ + void setNormal(Vector3d newNormal); /** * @return texture coordinates 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 61dbabf958a9eec1ada278ca2d450182fd9d907e..4034eb9c6eff242281c35f1a18e615c7d22bb878 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 @@ -63,6 +63,11 @@ public class MeshPointImpl implements MeshPoint { this.position = new Point3d(newPos); } } + + @Override + public void setNormal(Vector3d newNormal) { + this.normal = new Vector3d(newNormal); + } @Override public Vector3d getTexCoord() {