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 302d52a76af9d079aee0572b74fda3803c87ae4d..5ea502d615f0670151b61b810d9073f5c4ce6f47 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java @@ -13,6 +13,7 @@ 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 cz.fidentis.analyst.visitors.mesh.Curvature; import java.awt.image.BufferedImage; import java.io.File; @@ -63,6 +64,8 @@ public class HumanFace implements Serializable { private transient BufferedImage preview; + private transient Curvature curvature; + /** * Fast (de)serialization handler */ @@ -254,6 +257,19 @@ public class HumanFace implements Serializable { public BBox getBoundingBox() { return bbox; } + + /** + * Computes and returns curvature. + * + * @return curvature + */ + public Curvature getCurvature() { + if (this.curvature == null) { + this.curvature = new Curvature(); + getMeshModel().compute(curvature); + } + return this.curvature; + } /** * 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 700fff542062a5103b1e6dd93f22b2c15f1c95e8..bb51d592eb23831313ed582424d27de414a5df0d 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceUtils.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceUtils.java @@ -3,11 +3,10 @@ package cz.fidentis.analyst.face; import cz.fidentis.analyst.Logger; import cz.fidentis.analyst.feature.FeaturePoint; 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 cz.fidentis.analyst.visitors.mesh.sampling.PointSampling; import java.util.zip.DataFormatException; import javax.vecmath.Matrix3d; import javax.vecmath.Matrix4d; @@ -33,14 +32,15 @@ public class HumanFaceUtils { * Reasonable number seems to be 10. * @param scale Whether to scale face as well * @param error Acceptable error (a number bigger than or equal to zero). - * @param undersampling 100 = no undersampling, 10 = undersampling into 10% of vertices. + * @param samplingStrategy Downsampling strategy * @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. * @return ICP visitor that holds the transformations performed on the {@code transformedFace}. */ public static IcpTransformer alignMeshes( HumanFace staticFace, HumanFace transformedFace, - int maxIterations, boolean scale, double error, int undersampling, + int maxIterations, boolean scale, double error, + PointSampling samplingStrategy, boolean recomputeKdTree) { // transform mesh: @@ -49,7 +49,7 @@ public class HumanFaceUtils { maxIterations, scale, error, - (undersampling == 100) ? new NoUndersampling() : new RandomStrategy(undersampling) + samplingStrategy ); transformedFace.getMeshModel().compute(icp, true); // superimpose face towards the static face 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 bd9431a4b9eabbcbe79896e11feecbc51f5e9d82..f58f58107cec3f4e03588b0acd2989b06f3e4708 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java @@ -7,6 +7,8 @@ import cz.fidentis.analyst.mesh.core.MeshFacet; import cz.fidentis.analyst.mesh.core.MeshModel; import cz.fidentis.analyst.mesh.core.MeshPoint; import cz.fidentis.analyst.visitors.mesh.HausdorffDistance; +import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -77,7 +79,7 @@ public class IcpTransformer extends MeshVisitor { */ private final KdTree primaryKdTree; - private final UndersamplingStrategy reductionStrategy; + private final PointSampling samplingStrategy; /** * Constructor. @@ -93,7 +95,7 @@ public class IcpTransformer extends MeshVisitor { * @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used. * @throws IllegalArgumentException if some parameter is wrong */ - public IcpTransformer(MeshFacet mainFacet, int maxIteration, boolean scale, double error, UndersamplingStrategy strategy) { + public IcpTransformer(MeshFacet mainFacet, int maxIteration, boolean scale, double error, PointSampling strategy) { this(new HashSet<>(Collections.singleton(mainFacet)), maxIteration, scale, error, strategy); if (mainFacet == null) { throw new IllegalArgumentException("mainFacet"); @@ -114,7 +116,7 @@ public class IcpTransformer extends MeshVisitor { * @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used. * @throws IllegalArgumentException if some parameter is wrong */ - public IcpTransformer(Set<MeshFacet> mainFacets, int maxIteration, boolean scale, double error, UndersamplingStrategy strategy) { + public IcpTransformer(Set<MeshFacet> mainFacets, int maxIteration, boolean scale, double error, PointSampling strategy) { if (mainFacets == null) { throw new IllegalArgumentException("mainFacets"); } @@ -128,7 +130,7 @@ public class IcpTransformer extends MeshVisitor { this.error = error; this.maxIteration = maxIteration; this.scale = scale; - this.reductionStrategy = (strategy == null) ? new NoUndersampling() : strategy; + this.samplingStrategy = (strategy == null) ? new NoSampling() : strategy; } /** @@ -145,7 +147,7 @@ public class IcpTransformer extends MeshVisitor { * @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used. * @throws IllegalArgumentException if some parameter is wrong */ - public IcpTransformer(MeshModel mainModel, int maxIteration, boolean scale, double error, UndersamplingStrategy strategy) { + public IcpTransformer(MeshModel mainModel, int maxIteration, boolean scale, double error, PointSampling strategy) { this(new HashSet<>(mainModel.getFacets()), maxIteration, scale, error, strategy); if (mainModel.getFacets().isEmpty()) { throw new IllegalArgumentException("mainModel"); @@ -165,7 +167,7 @@ public class IcpTransformer extends MeshVisitor { * @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used. * @throws IllegalArgumentException if some parameter is wrong */ - public IcpTransformer(KdTree primaryKdTree, int maxIteration, boolean scale, double error, UndersamplingStrategy strategy) { + public IcpTransformer(KdTree primaryKdTree, int maxIteration, boolean scale, double error, PointSampling strategy) { if (primaryKdTree == null) { throw new IllegalArgumentException("primaryKdTree"); } @@ -179,7 +181,7 @@ public class IcpTransformer extends MeshVisitor { this.error = error; this.maxIteration = maxIteration; this.scale = scale; - this.reductionStrategy = (strategy == null) ? new NoUndersampling() : strategy; + this.samplingStrategy = (strategy == null) ? new NoSampling() : strategy; } /** @@ -257,7 +259,7 @@ public class IcpTransformer extends MeshVisitor { false // auto cut ); - MeshFacet reducedFacet = new UndersampledMeshFacet(transformedFacet, reductionStrategy); + MeshFacet reducedFacet = new UndersampledMeshFacet(transformedFacet, samplingStrategy); int currentIteration = 0; IcpTransformation transformation = null; diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/NoUndersampling.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/NoUndersampling.java deleted file mode 100644 index c32c1109bc3c5e427ba912f61b40105ab1c89556..0000000000000000000000000000000000000000 --- a/Comparison/src/main/java/cz/fidentis/analyst/icp/NoUndersampling.java +++ /dev/null @@ -1,22 +0,0 @@ -package cz.fidentis.analyst.icp; - -import cz.fidentis.analyst.mesh.core.MeshPoint; -import java.util.List; - -/** - * No undersampling. The triangular mesh keeps unchanged. - * - * @author Radek Oslejsek - */ -public class NoUndersampling extends UndersamplingStrategy { - - @Override - public List<MeshPoint> reduceMeshVertices(List<MeshPoint> meshPoints) { - return meshPoints; - } - - @Override - public String toString() { - return "no undersampling"; - } -} diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/RandomStrategy.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/RandomStrategy.java deleted file mode 100644 index 8e7899ad7fa03967c97a68cad331bc96fec8b802..0000000000000000000000000000000000000000 --- a/Comparison/src/main/java/cz/fidentis/analyst/icp/RandomStrategy.java +++ /dev/null @@ -1,77 +0,0 @@ -package cz.fidentis.analyst.icp; - -import cz.fidentis.analyst.mesh.core.MeshPoint; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -/** - * Random undersampling when the mesh vertices to be removed are selected randomly. - * - * @author Radek Oslejsek - */ -public class RandomStrategy extends UndersamplingStrategy { - - /** - * Constructor for PERCENTAGE undersampling type. - * - * @param perc Percentage - a value in (0.0, 1.0> - * @throws IllegalArgumentException if the input parameter is wrong - */ - public RandomStrategy(double perc) { - super(perc); - } - - /** - * Constructor for PERCENTAGE undersampling type. - * - * @param max Maximal number of vertices. Must be bigger than zero - * @throws IllegalArgumentException if the input parameter is wrong - */ - public RandomStrategy(int max) { - super(max); - } - - @Override - public List<MeshPoint> reduceMeshVertices(List<MeshPoint> meshPoints) { - if (meshPoints == null) { - return null; - } - - int numVertices = getNumUndersampledVertices(meshPoints.size()); - - if (meshPoints.size() == numVertices) { - return meshPoints; - } - - // generate randomly ordered indexes: - List<Integer> range = IntStream.range(0, meshPoints.size()).boxed().collect(Collectors.toCollection(ArrayList::new)); - Collections.shuffle(range); - - if (numVertices < meshPoints.size() / 2) { // copy indices - MeshPoint[] array = new MeshPoint[meshPoints.size()]; - range.stream().limit(numVertices).forEach( - i -> array[i] = meshPoints.get(i) - ); - return Arrays.stream(array).filter( - p -> p != null - ).collect(Collectors.<MeshPoint>toList()); - } else { // remove indices - List<MeshPoint> copy = new ArrayList<>(meshPoints); - range.stream().limit(meshPoints.size() - numVertices).forEach( - i -> copy.set(i, null) - ); - return copy.parallelStream().filter( - p -> p != null - ).collect(Collectors.<MeshPoint>toList()); - } - } - - @Override - public String toString() { - return "random " + super.toString(); - } -} diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersampledMeshFacet.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersampledMeshFacet.java index c7b947ec8e9f404582c80094a096ccf09d88348f..588c5ef6b0d7a84d602a7d58ee2ea802c8fd246a 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersampledMeshFacet.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersampledMeshFacet.java @@ -5,6 +5,7 @@ import cz.fidentis.analyst.mesh.core.MeshFacetImpl; import cz.fidentis.analyst.mesh.core.MeshPoint; import cz.fidentis.analyst.mesh.core.MeshTriangle; import cz.fidentis.analyst.mesh.core.TriangleFan; +import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -27,7 +28,7 @@ public class UndersampledMeshFacet extends MeshFacetImpl { * @param origFacet Original facet * @param strategy Undersampling strategy */ - public UndersampledMeshFacet(MeshFacet origFacet, UndersamplingStrategy strategy) { + public UndersampledMeshFacet(MeshFacet origFacet, PointSampling strategy) { if (origFacet == null) { throw new IllegalArgumentException("origFacet"); } @@ -36,7 +37,8 @@ public class UndersampledMeshFacet extends MeshFacetImpl { throw new IllegalArgumentException("strategy"); } - this.reducedVertices = strategy.reduceMeshVertices(origFacet.getVertices()); + strategy.visitMeshFacet(origFacet); + this.reducedVertices = strategy.getSamples(); } @Override diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersamplingStrategy.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersamplingStrategy.java deleted file mode 100644 index ed812252691a242071e65aefbaf28fa7bdd66b07..0000000000000000000000000000000000000000 --- a/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersamplingStrategy.java +++ /dev/null @@ -1,102 +0,0 @@ -package cz.fidentis.analyst.icp; - -import cz.fidentis.analyst.mesh.core.MeshPoint; -import java.util.List; - -/** - * Undersampling strategies used to reduce the size of triangle meshes and then - * accelerate ICP computation. - * - * @author Radek Oslejsek - */ -public abstract class UndersamplingStrategy { - - /** - * If undersamoling is used, then this configuration parameter - * encodes whether the reduction of mesh size is expressed as the percentage - * of the original size (number of vertices) or as the maximal number of vertices - * to be used. - * - * @author Radek Oslejsek - */ - public enum UndersamplingType { - PERCENTAGE, - MAX_VERTICES - }; - - private final UndersamplingType undersamplingType; - private final double undersamplingLimit; - - /** - * Constructor for no undersampling. - */ - public UndersamplingStrategy() { - this.undersamplingType = null; - this.undersamplingLimit = 0.0; - } - - /** - * Constructor for PERCENTAGE undersampling type. - * - * @param perc Percentage - a value in (0.0, 1.0> - * @throws IllegalArgumentException if the input parameter is wrong - */ - public UndersamplingStrategy(double perc) { - if (perc <= 0.0 || perc > 1) { - throw new IllegalArgumentException("perc"); - } - this.undersamplingType = UndersamplingType.PERCENTAGE; - this.undersamplingLimit = perc; - } - - /** - * Constructor for PERCENTAGE undersampling type. - * - * @param max Maximal number of vertices. Must be bigger than zero - * @throws IllegalArgumentException if the input parameter is wrong - */ - public UndersamplingStrategy(int max) { - if (max <= 0) { - throw new IllegalArgumentException("max"); - } - this.undersamplingType = UndersamplingType.MAX_VERTICES; - this.undersamplingLimit = max; - } - - /** - * Reduces mesh points. Returned {@code MeshPoints} are backed by - * original mesh points. - * - * @param meshPoints Original list of mesh points - * @return Reduced list of mesh points or {@code null}. - */ - public abstract List<MeshPoint> reduceMeshVertices(List<MeshPoint> meshPoints); - - @Override - public String toString() { - if (this.undersamplingType == UndersamplingType.PERCENTAGE) { - return "undersampling to " + (undersamplingLimit * 100) + "%"; - } else { - return "undersampling to " + undersamplingLimit + " vertices"; - } - } - - /** - * Returns number of vertices to be returned after undersampling. - * - * @param origVertices Original number of vertices - * @return number of vertices to be returned after undersampling. - */ - protected int getNumUndersampledVertices(int origVertices) { - switch (this.undersamplingType) { - case PERCENTAGE: - return (int) (origVertices * this.undersamplingLimit); - case MAX_VERTICES: - //nt limit = (int) this.undersamplingLimit; - //return (limit <= origVertices) ? limit : origVertices; - return Math.min((int) this.undersamplingLimit, origVertices); - default: - return 0; - } - } -} diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/ApproxSymmetryPlane.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/ApproxSymmetryPlane.java index 48228ce7c7f06b0495946cb1ce94882685034250..9a14fccc528c8de416eb087abce6646ef6e00bc0 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/ApproxSymmetryPlane.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/ApproxSymmetryPlane.java @@ -1,7 +1,7 @@ package cz.fidentis.analyst.symmetry; -import cz.fidentis.analyst.mesh.core.MeshFacet; import cz.fidentis.analyst.mesh.core.MeshPoint; +import java.util.List; import javax.vecmath.Vector3d; /** @@ -17,19 +17,20 @@ public class ApproxSymmetryPlane extends Plane implements Comparable<ApproxSymme /** * Constructor. * - * @param sigPoints Mesh vertices with the most significant curvature + * @param vertices Mesh vertices + * @param cache Precomputed values form mesh vertices * @param config Symmetry plane configuration * @param i index of the first significant point used for the plane construction * @param j index of the second significant point used for the plane construction * @param maxDist Distance limit * @throws UnsupportedOperationException if the symmetry plane cannot be created */ - public ApproxSymmetryPlane(SignificantPoints sigPoints, SymmetryConfig config, int i, int j, double maxDist) throws UnsupportedOperationException { + public ApproxSymmetryPlane(List<MeshPoint> vertices, SymmetryCache cache, SymmetryConfig config, int i, int j, double maxDist) throws UnsupportedOperationException { if (i == j) { throw new UnsupportedOperationException(); } - setNormAndDist(sigPoints, config, i, j); - computeVotes(sigPoints, config, maxDist); + setNormAndDist(vertices, cache, config, i, j); + computeVotes(vertices, cache, config, maxDist); } /** @@ -41,19 +42,15 @@ public class ApproxSymmetryPlane extends Plane implements Comparable<ApproxSymme return votes; } - private void setNormAndDist(SignificantPoints sigPoints, SymmetryConfig config, int i, int j) { - MeshFacet facetI = sigPoints.getMeshFacet(i); - MeshFacet facetJ = sigPoints.getMeshFacet(j); - int sigPointI = sigPoints.getVertexIndex(i); - int sigPointJ = sigPoints.getVertexIndex(j); - MeshPoint meshPointI = facetI.getVertex(sigPointI); - MeshPoint meshPointJ = facetJ.getVertex(sigPointJ); + private void setNormAndDist(List<MeshPoint> vertices, SymmetryCache cache, SymmetryConfig config, int i, int j) { + MeshPoint meshPointI = vertices.get(i); + MeshPoint meshPointJ = vertices.get(j); // accpet only point pairs with significantly different curvatures (WTF?) double minRatio = config.getMinCurvRatio(); double maxRatio = 1.0 / minRatio; - double ratioIJ = sigPoints.getCachedCurRatio(i, j); - if (ratioIJ < minRatio || ratioIJ > maxRatio) { + double ratioIJ = cache.getCurRatio(i, j); + if (Double.isFinite(ratioIJ) && (ratioIJ < minRatio || ratioIJ > maxRatio)) { throw new UnsupportedOperationException(); } @@ -64,12 +61,12 @@ public class ApproxSymmetryPlane extends Plane implements Comparable<ApproxSymme normal.normalize(); // accpect only point pair with oposite normals along with the plane normal: - double normCos = sigPoints.getCachedNormCosVec(i, j).dot(normal); + double normCos = cache.getNormCosVec(i, j).dot(normal); if (Math.abs(normCos) < config.getMinNormAngleCos()) { throw new UnsupportedOperationException(); } - setDistance(-normal.dot(sigPoints.getCachedAvgPos(i, j))); + setDistance(-normal.dot(cache.getAvgPos(i, j))); setNormal(normal); } @@ -80,41 +77,38 @@ public class ApproxSymmetryPlane extends Plane implements Comparable<ApproxSymme * @param config Symmetry plane configuration * @param maxDist Distance limit */ - private void computeVotes(SignificantPoints sigPoints, SymmetryConfig config, double maxDist) { + private void computeVotes(List<MeshPoint> vertices, SymmetryCache cache, SymmetryConfig config, double maxDist) { normalize(); Vector3d normal = getNormal(); double d = getDistance(); double maxCurvRatio = 1.0 / config.getMinCurvRatio(); - for (int i = 0; i < sigPoints.size(); i++) { - for (int j = 0; j < sigPoints.size(); j++) { + for (int i = 0; i < vertices.size(); i++) { + for (int j = 0; j < vertices.size(); j++) { if (i == j) { continue; } - MeshFacet facetI = sigPoints.getMeshFacet(i); - MeshFacet facetJ = sigPoints.getMeshFacet(j); - - double ratioIJ = sigPoints.getCachedCurRatio(i, j); - if (ratioIJ < config.getMinCurvRatio() || ratioIJ > maxCurvRatio) { + double ratioIJ = cache.getCurRatio(i, j); + if (Double.isFinite(ratioIJ) && (ratioIJ < config.getMinCurvRatio() || ratioIJ > maxCurvRatio)) { continue; } - double normCos = sigPoints.getCachedNormCosVec(i, j).dot(normal); + double normCos = cache.getNormCosVec(i, j).dot(normal); if (Math.abs(normCos) < config.getMinNormAngleCos()) { continue; } // Caching this part doesn't improve efficiency anymore: - Vector3d vec = new Vector3d(facetI.getVertex(sigPoints.getVertexIndex(i)).getPosition()); - vec.sub(facetJ.getVertex(sigPoints.getVertexIndex(j)).getPosition()); + Vector3d vec = new Vector3d(vertices.get(i).getPosition()); + vec.sub(vertices.get(j).getPosition()); vec.normalize(); double cos = vec.dot(normal); if (Math.abs(cos) < config.getMinAngleCos()) { continue; } - Vector3d avg = sigPoints.getCachedAvgPos(i, j); + Vector3d avg = cache.getAvgPos(i, j); double dist = Math.abs(normal.dot(avg) + d); if (dist <= maxDist) { votes++; diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/CurvatureAlg.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/CurvatureAlg.java deleted file mode 100644 index 64eabced06338550c08014ce6b4ca0dae8b862a3..0000000000000000000000000000000000000000 --- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/CurvatureAlg.java +++ /dev/null @@ -1,13 +0,0 @@ -package cz.fidentis.analyst.symmetry; - -/** - * Curvature algorithm used for the selection of the top X significant points. - * - * @author Radek Oslejsek - */ -public enum CurvatureAlg { - MEAN, - GAUSSIAN, - MAX, - MIN -} diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SignificantPoints.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SignificantPoints.java deleted file mode 100644 index 1aa8bceb697b75e943b69606835622c254c809b5..0000000000000000000000000000000000000000 --- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SignificantPoints.java +++ /dev/null @@ -1,309 +0,0 @@ -package cz.fidentis.analyst.symmetry; - -import cz.fidentis.analyst.mesh.MeshVisitor; -import cz.fidentis.analyst.mesh.core.MeshFacet; -import cz.fidentis.analyst.mesh.core.MeshPoint; -import cz.fidentis.analyst.visitors.mesh.Curvature; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.PriorityQueue; -import javax.vecmath.Vector3d; - -/** - * Visitor selecting X most significant vertices of inspected mesh facets. - * The most significant vertices are those having the highest curvature. - * <p> - * This visitor <b>is not thread-safe</b> for the efficiency reasons, i.e., - * a single instance of the visitor cannot be used to inspect multiple meshes - * simultaneously (sequential inspection is okay). - * </p> - * - * @author Radek Oslejsek - * @author Natalia Bebjakova - */ -public class SignificantPoints extends MeshVisitor { - - private final int maxPoints; - - private final Curvature curvatureVisitor; - - private final CurvatureAlg curvatureAlg; - - private List<VertCurvature> significantPoints; - - private List<List<Vector3d>> normCosVecCache; - - private List<List<Vector3d>> avgPosCache; - - private List<List<Double>> curRatioCache; - - /** - * Constructor. - * - * @param cAlg Curvature strategy to be used for the selection of significant points. - * @param max Maximal number of significant points - * @throws IllegalArgumentException if the {@code curbatureVisitor} is missing - */ - public SignificantPoints(CurvatureAlg cAlg, int max) { - this(new Curvature(), cAlg, max); - } - - /** - * Constructor. - * - * @param curvatureVisitor Curvature. If the object has - * pre-filled curvatures of meshes, then they are used also used for the computation - * of significant points. In this way, it is possible to reuse already computed - * curvatures and skip calling the {@link #visitMeshFacet} inspection method. - * @param cAlg Curvature strategy to be used for the selection of significant points. - * @param max Maximal number of significant points - * @throws IllegalArgumentException if the {@code curbatureVisitor} is missing - */ - public SignificantPoints(Curvature curvatureVisitor, CurvatureAlg cAlg, int max) { - if (curvatureVisitor == null) { - throw new IllegalArgumentException("curvatureVisitor"); - } - this.maxPoints = max; - this.curvatureVisitor = curvatureVisitor; - this.curvatureAlg = cAlg; - } - - @Override - public boolean isThreadSafe() { - return false; - } - - @Override - public void visitMeshFacet(MeshFacet facet) { - significantPoints = null; // clear previous results - curvatureVisitor.visitMeshFacet(facet); // compute curvature for new inspected facet - } - - /** - * Returns cached normal vector for cos. - * @param i index of the i-th significant curvature point - * @param j index of the j-th significant curvature point - * @return cached value or null - */ - public Vector3d getCachedNormCosVec(int i, int j) { - checkAndComputeSignificantPoints(); - if (normCosVecCache == null || i >= normCosVecCache.size() || i >= normCosVecCache.size() || i < 0 || j < 0) { - return null; - } - return normCosVecCache.get(i).get(j); - } - - /** - * Returns cached vector for the computation average normal. - * @param i index of the i-th significant curvature point - * @param j index of the j-th significant curvature point - * @return cached value or null - */ - public Vector3d getCachedAvgPos(int i, int j) { - checkAndComputeSignificantPoints(); - if (avgPosCache == null || i >= avgPosCache.size() || i >= avgPosCache.size() || i < 0 || j < 0) { - return null; - } - return avgPosCache.get(i).get(j); - } - - /** - * Returns cached curvature ratio. - * @param i index of the i-th significant curvature point - * @param j index of the j-th significant curvature point - * @return cached value or {@code Double.NaN} - */ - public double getCachedCurRatio(int i, int j) { - checkAndComputeSignificantPoints(); - if (curRatioCache == null || i >= curRatioCache.size() || i >= curRatioCache.size() || i < 0 || j < 0) { - return Double.NaN; - } - return curRatioCache.get(i).get(j); - } - - /** - * Returns index of the i-th most significant mesh vertex. - * - * @param i The "i" (index of the ordering) - * @return index of the i-th most significant mesh vertex. - * @throws IllegalArgumentException if the {@code i} parameter is out of rage - */ - public int getVertexIndex(int i) { - checkAndComputeSignificantPoints(); - if (i < 0 || i >= significantPoints.size()) { - throw new IllegalArgumentException("i"); - } - return significantPoints.get(i).vertIndex; - } - - /** - * Returns curvature of the i-th most significant mesh vertex. - * - * @param i The "i" (index of the ordering) - * @return curvature of the i-th most significant mesh vertex. - * @throws IllegalArgumentException if the {@code i} parameter is out of rage - */ - public double getCurvature(int i) { - checkAndComputeSignificantPoints(); - if (i < 0 || i >= significantPoints.size()) { - throw new IllegalArgumentException("i"); - } - return significantPoints.get(i).curvature; - } - - /** - * Returns mesh facet of the i-th most significant mesh vertex. - * - * @param i The "i" (index of the ordering) - * @return mesh facet of the i-th most significant mesh vertex. - * @throws IllegalArgumentException if the {@code i} parameter is out of rage - */ - public MeshFacet getMeshFacet(int i) { - checkAndComputeSignificantPoints(); - if (i < 0 || i >= significantPoints.size()) { - throw new IllegalArgumentException("i"); - } - return significantPoints.get(i).facet; - } - - /** - * Returns number of the most significant points. - * @return number of the most significant points. - */ - public int size() { - checkAndComputeSignificantPoints(); - return significantPoints.size(); - } - - protected void checkAndComputeSignificantPoints() { - if (significantPoints != null) { - return; // already computed - } - - // fill the priority queue - final PriorityQueue<VertCurvature> priorityQueue = new PriorityQueue<>(); - - Map<MeshFacet, List<Double>> curvMap = null; - switch (curvatureAlg) { - case MEAN: - curvMap = curvatureVisitor.getMeanCurvatures(); - break; - case GAUSSIAN: - curvMap = curvatureVisitor.getGaussianCurvatures(); - break; - case MAX: - curvMap = curvatureVisitor.getMaxPrincipalCurvatures(); - break; - case MIN: - curvMap = curvatureVisitor.getMinPrincipalCurvatures(); - break; - default: - throw new IllegalArgumentException("curvatureAlg"); - } - - for (MeshFacet facet: curvMap.keySet()) { - List<Double> curvs = curvMap.get(facet); - for (int i = 0; i < curvs.size(); i++) { - // store helper objects, replace NaN with 0.0: - double c = curvs.get(i); - priorityQueue.add(new VertCurvature(facet, i, (Double.isNaN(c) ? 0.0 : c))); - } - } - - // select top significant points - significantPoints = new ArrayList<>(maxPoints); - for (int i = 0; i < maxPoints; i++) { - VertCurvature vc = priorityQueue.poll(); - if (vc == null) { - break; // no more points available - } else { - significantPoints.add(vc); - } - } - - // finally, precompute and cache data of the significant points - cacheData(); - } - - /** - * Pre-computes and caches some values related to the computation of - * asymmetric plane from curvature. - */ - protected void cacheData() { - normCosVecCache = new ArrayList<>(significantPoints.size()); - avgPosCache = new ArrayList<>(significantPoints.size()); - curRatioCache = new ArrayList<>(significantPoints.size()); - - for (int i = 0; i < significantPoints.size(); i++) { - List<Vector3d> cosArray = new ArrayList<>(significantPoints.size()); - normCosVecCache.add(cosArray); - - List<Vector3d> posArray = new ArrayList<>(significantPoints.size()); - avgPosCache.add(posArray); - - List<Double> curArray = new ArrayList<>(significantPoints.size()); - curRatioCache.add(curArray); - - for (int j = 0; j < significantPoints.size(); j++) { - VertCurvature vcI = significantPoints.get(i); - VertCurvature vcJ = significantPoints.get(j); - MeshPoint meshPointI = vcI.facet.getVertex(vcI.vertIndex); - MeshPoint meshPointJ = vcJ.facet.getVertex(vcJ.vertIndex); - - Vector3d ni = new Vector3d(meshPointI.getNormal()); - Vector3d nj = new Vector3d(meshPointJ.getNormal()); - ni.normalize(); - nj.normalize(); - ni.sub(nj); - ni.normalize(); - cosArray.add(ni); - - Vector3d avrg = new Vector3d(meshPointI.getPosition()); - Vector3d aux = new Vector3d(meshPointJ.getPosition()); - avrg.add(aux); - avrg.scale(0.5); - posArray.add(avrg); - curArray.add(vcI.curvature / vcJ.curvature); - } - } - } - - /** - * Helper class for sorting points with respect to their curvature. - * @author Radek Oslejsek - */ - private class VertCurvature implements Comparable<VertCurvature> { - - private final int vertIndex; - private final double curvature; - private final MeshFacet facet; - - VertCurvature(MeshFacet facet, int vertIndex, double curvature) { - this.vertIndex = vertIndex; - this.curvature = curvature; - this.facet = facet; - } - - /** - * Compares this object with the specified object for order. - * Curvature is taken into consideration as the primary value (descendant ordering). - * If the curvature equals, then the objects are sorted randomly (1 is always returned). - * This approach preserves all points in sorted collections. - * - * @param arg the object to be compared. - * @return a negative integer, zero, or a positive integer as this object - * is less than, equal to, or greater than the specified object. - */ - @Override - public int compareTo(VertCurvature arg) { - int comp = Double.compare(arg.curvature, this.curvature); - return (comp == 0) ? 1 : comp; - } - - public String toString() { - return ""+curvature; - } - } - -} diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryCache.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryCache.java new file mode 100644 index 0000000000000000000000000000000000000000..54ba355815ea4cd7fc6371ce9a8c06fca9bc406b --- /dev/null +++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryCache.java @@ -0,0 +1,110 @@ +package cz.fidentis.analyst.symmetry; + +import cz.fidentis.analyst.mesh.core.MeshPoint; +import java.util.ArrayList; +import java.util.List; +import javax.vecmath.Vector3d; + +/** + * Precomputed values for the symmetry plane estimation. + * + * @author Radek Oslejsek + */ +public class SymmetryCache { + + private List<List<Vector3d>> normCosVecCache; + private List<List<Vector3d>> avgPosCache; + private List<List<Double>> curRatioCache; + + /** + * Constructor. + * + * @param vertices Mesh vertices + * @param curvatures Curvatures in the vertices + */ + public SymmetryCache(List<MeshPoint> vertices, List<Double> curvatures) { + if (vertices == null || vertices.isEmpty()) { + throw new IllegalArgumentException("points"); + } + + normCosVecCache = new ArrayList<>(vertices.size()); + avgPosCache = new ArrayList<>(vertices.size()); + curRatioCache = (curvatures == null) ? null : new ArrayList<>(vertices.size()); + + for (int i = 0; i < vertices.size(); i++) { + List<Vector3d> cosArray = new ArrayList<>(vertices.size()); + normCosVecCache.add(cosArray); + + List<Vector3d> posArray = new ArrayList<>(vertices.size()); + avgPosCache.add(posArray); + + List<Double> curArray = null; + if (curvatures != null) { + curArray = new ArrayList<>(vertices.size()); + curRatioCache.add(curArray); + } + + for (int j = 0; j < vertices.size(); j++) { + MeshPoint meshPointI = vertices.get(i); + MeshPoint meshPointJ = vertices.get(j); + + Vector3d ni = new Vector3d(meshPointI.getNormal()); + Vector3d nj = new Vector3d(meshPointJ.getNormal()); + ni.normalize(); + nj.normalize(); + ni.sub(nj); + ni.normalize(); + cosArray.add(ni); + + Vector3d avrg = new Vector3d(meshPointI.getPosition()); + Vector3d aux = new Vector3d(meshPointJ.getPosition()); + avrg.add(aux); + avrg.scale(0.5); + posArray.add(avrg); + if (curvatures != null) { + curArray.add(curvatures.get(i) / curvatures.get(j)); + } + } + } + } + + /** + * Returns cached normal vector for cos. + * @param i index of the i-th significant curvature point + * @param j index of the j-th significant curvature point + * @return cached value or null + */ + public Vector3d getNormCosVec(int i, int j) { + if (normCosVecCache == null || i >= normCosVecCache.size() || i >= normCosVecCache.size() || i < 0 || j < 0) { + return null; + } + return normCosVecCache.get(i).get(j); + } + + /** + * Returns cached vector for the computation average normal. + * @param i index of the i-th significant curvature point + * @param j index of the j-th significant curvature point + * @return cached value or null + */ + public Vector3d getAvgPos(int i, int j) { + if (avgPosCache == null || i >= avgPosCache.size() || i >= avgPosCache.size() || i < 0 || j < 0) { + return null; + } + return avgPosCache.get(i).get(j); + } + + /** + * Returns cached curvature ratio. + * @param i index of the i-th significant curvature point + * @param j index of the j-th significant curvature point + * @return cached value or {@code Double.NaN} + */ + public double getCurRatio(int i, int j) { + if (curRatioCache == null || i >= curRatioCache.size() || i >= curRatioCache.size() || i < 0 || j < 0) { + return Double.NaN; + } + return curRatioCache.get(i).get(j); + } + +} diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryConfig.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryConfig.java index 1b730fda3682f6657c14c03e297d1fb25b405000..827ff9817774aafe2478118fdf1e507908bc74f9 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryConfig.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryConfig.java @@ -1,5 +1,7 @@ package cz.fidentis.analyst.symmetry; +import cz.fidentis.analyst.visitors.mesh.sampling.CurvatureSampling; + /** * Representation of configuration for symmetry estimate. * Default numbers are given due to the best results on tested data. @@ -16,7 +18,7 @@ public class SymmetryConfig { private static final double DEFAULT_MAX_REL_DISTANCE = 1.0 / 100.0; private static final int DEFAULT_SIGNIFICANT_POINT_COUNT = 200; private static final boolean DEFAULT_AVERAGING = true; - private static final CurvatureAlg DEFAULT_CURVATURE_ALGORITHM = CurvatureAlg.GAUSSIAN; + private static final CurvatureSampling.CurvatureAlg DEFAULT_CURVATURE_ALGORITHM = CurvatureSampling.CurvatureAlg.GAUSSIAN; private double minCurvRatio; private double minAngleCos; @@ -24,7 +26,7 @@ public class SymmetryConfig { private double maxRelDistance; private int significantPointCount; private boolean averaging; - private CurvatureAlg curvatureAlg; + private CurvatureSampling.CurvatureAlg curvatureAlg; /** * Creates configuration with default values @@ -179,7 +181,7 @@ public class SymmetryConfig { * Returns curvature algorithm. * @return curvature algorithm */ - public CurvatureAlg getCurvatureAlg() { + public CurvatureSampling.CurvatureAlg getCurvatureAlg() { return this.curvatureAlg; } @@ -187,7 +189,7 @@ public class SymmetryConfig { * Sets curvature algorithm. * @param alg curvature algorithm */ - public void setCurvatureAlg(CurvatureAlg alg) { + public void setCurvatureAlg(CurvatureSampling.CurvatureAlg alg) { this.curvatureAlg = alg; } 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 7431560852e8ee3f643b0df08a16a4484cefc801..7988d530adc807bd1dbd37f0859b23fa897c4466 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java @@ -2,8 +2,11 @@ package cz.fidentis.analyst.symmetry; import cz.fidentis.analyst.mesh.MeshVisitor; import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshPoint; import cz.fidentis.analyst.visitors.mesh.BoundingBox; import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox; +import cz.fidentis.analyst.visitors.mesh.sampling.CurvatureSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; @@ -33,7 +36,7 @@ import java.util.logging.Level; public class SymmetryEstimator extends MeshVisitor { private final SymmetryConfig config; - private final SignificantPoints sigPointsVisitor; + private final PointSampling samplingStrategy; private final BoundingBox bbVisitor = new BoundingBox(); private Plane symmetryPlane; @@ -42,14 +45,18 @@ public class SymmetryEstimator extends MeshVisitor { * Constructor. * * @param config Algorithm options + * @param samplingStrategy Downsampling strategy. Must not be {@code null} * @throws IllegalArgumentException if some input parameter is missing */ - public SymmetryEstimator(SymmetryConfig config) { + public SymmetryEstimator(SymmetryConfig config, PointSampling samplingStrategy) { if (config == null) { throw new IllegalArgumentException("config"); } + if (samplingStrategy == null) { + throw new IllegalArgumentException("samplingStrategy"); + } this.config = config; - this.sigPointsVisitor = new SignificantPoints(config.getCurvatureAlg(), config.getSignificantPointCount()); + this.samplingStrategy = samplingStrategy; } @Override @@ -65,7 +72,7 @@ public class SymmetryEstimator extends MeshVisitor { facet.calculateVertexNormals(); } } - sigPointsVisitor.visitMeshFacet(facet); + samplingStrategy.visitMeshFacet(facet); bbVisitor.visitMeshFacet(facet); } @@ -109,16 +116,24 @@ public class SymmetryEstimator extends MeshVisitor { final List<Plane> planes = new ArrayList<>(); final double maxDistance = bbVisitor.getBoundingBox().getDiagonalLength() * config.getMaxRelDistance(); + List<MeshPoint> sigVertices = samplingStrategy.getSamples(); + SymmetryCache cache = new SymmetryCache( + sigVertices, + (samplingStrategy.getClass() == CurvatureSampling.class) + ? ((CurvatureSampling)samplingStrategy).getSampledCurvatures() + : null + ); + /* * Sequential computation */ if (!concurrently) { int maxVotes = 0; - for (int i = 0; i < sigPointsVisitor.size(); i++) { - for (int j = 0; j < sigPointsVisitor.size(); j++) { + for (int i = 0; i < sigVertices.size(); i++) { + for (int j = 0; j < sigVertices.size(); j++) { ApproxSymmetryPlane newPlane; try { - newPlane = new ApproxSymmetryPlane(sigPointsVisitor, config, i, j, maxDistance); + newPlane = new ApproxSymmetryPlane(sigVertices, cache, config, i, j, maxDistance); } catch(UnsupportedOperationException ex) { continue; } @@ -135,11 +150,11 @@ public class SymmetryEstimator extends MeshVisitor { // Initiate structure for concurrent computation: ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); - final List<Future<ApproxSymmetryPlane>> results = new ArrayList<>(sigPointsVisitor.size()*sigPointsVisitor.size()); + final List<Future<ApproxSymmetryPlane>> results = new ArrayList<>(sigVertices.size() * sigVertices.size()); // Compute candidate planes concurrently: - for (int i = 0; i < sigPointsVisitor.size(); i++) { - for (int j = 0; j < sigPointsVisitor.size(); j++) { + for (int i = 0; i < sigVertices.size(); i++) { + for (int j = 0; j < sigVertices.size(); j++) { int finalI = i; int finalJ = j; @@ -147,7 +162,7 @@ public class SymmetryEstimator extends MeshVisitor { @Override public ApproxSymmetryPlane call() throws Exception { try { - return new ApproxSymmetryPlane(sigPointsVisitor, config, finalI, finalJ, maxDistance); + return new ApproxSymmetryPlane(sigVertices, cache, config, finalI, finalJ, maxDistance); } catch(UnsupportedOperationException ex) { return null; } diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java index 98850ff527713a1112961f2c3b45664fc8934fd3..722e0917bdc4a7f9e9c6f14692951b05b493bcb8 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java @@ -234,7 +234,7 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor { @Override public void visitHumanFace(HumanFace humanFace) { - // Compute the Hasudorff distance using the 'distanceVisitor', but only once + // Compute the (standard) Hasudorff distance using the 'distanceVisitor', but only once if (!distanceVisitor.getDistances().keySet().containsAll(humanFace.getMeshModel().getFacets())) { humanFace.getMeshModel().compute(distanceVisitor, inParallel()); } diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/WeightedAverageCollector.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/WeightedAverageCollector.java index 1ad08cd93a043d476c5e6f6101f8cca9d4c2317c..046912fc7a325e296448a941d3c112acfad7629b 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/WeightedAverageCollector.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/WeightedAverageCollector.java @@ -143,10 +143,14 @@ class IntemediateResults { } public void addWeightedValSum(double weightedValSum) { - this.weightedValSum += weightedValSum; + if (Double.isFinite(weightedValSum)) { + this.weightedValSum += weightedValSum; + } } public void addWeightSum(double weightSum) { - this.weightSum += weightSum; + if (Double.isFinite(weightSum)) { + this.weightSum += weightSum; + } } } diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/CurvatureSampling.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/CurvatureSampling.java new file mode 100644 index 0000000000000000000000000000000000000000..b66c4133af250e7ef54c2e534feae4ceced64f27 --- /dev/null +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/CurvatureSampling.java @@ -0,0 +1,231 @@ +package cz.fidentis.analyst.visitors.mesh.sampling; + +import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshPoint; +import cz.fidentis.analyst.visitors.mesh.Curvature; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.stream.Collectors; + +/** + * A relevance-based point sampling using highest curvature. + * {@see https://graphics.stanford.edu/papers/zipper/zipper.pdf} + * + * <p> + * This visitor <b>is not thread-safe</b> for the efficiency reasons, i.e., + * a single instance of the visitor cannot be used to inspect multiple meshes + * simultaneously (sequential inspection is okay). + * </p> + * + * @author Radek Oslejsek + * @author Natalia Bebjakova + */ +public class CurvatureSampling extends PointSampling { + + /** + * Curvature algorithm used for the selection of the top X significant points. + * + * @author Radek Oslejsek + */ + public enum CurvatureAlg { + MEAN, + GAUSSIAN, + MAX, + MIN + } + + //private final int maxPoints; + + private final Curvature curvatureVisitor; + + private final CurvatureAlg curvatureAlg; + + private List<VertCurvature> significantPoints; + + /** + * Constructor. + * + * @param cAlg Curvature strategy to be used for the selection of significant points. + * @param max Maximal number of significant points + * @throws IllegalArgumentException if the {@code curbatureVisitor} is missing + */ + public CurvatureSampling(CurvatureAlg cAlg, int max) { + this(new Curvature(), cAlg, max); + } + + /** + * Constructor. + * + * @param cAlg Curvature strategy to be used for the selection of significant points. + * @param perc Maximal number of significant points as percents of the original size + */ + public CurvatureSampling(CurvatureAlg cAlg, double perc) { + this(new Curvature(), cAlg, perc); + } + + /** + * Constructor. + * + * @param curvatureVisitor Curvature. If the object has + * pre-filled curvatures of meshes, then they are also used for the computation + * of significant points. In this way, it is possible to reuse already computed + * curvatures and skip calling the {@link #visitMeshFacet} inspection method. + * @param cAlg Curvature strategy to be used for the selection of significant points. + * @param max Maximal number of significant points + * @throws IllegalArgumentException if the {@code curbatureVisitor} is missing + */ + public CurvatureSampling(Curvature curvatureVisitor, CurvatureAlg cAlg, int max) { + super(max); + + if (curvatureVisitor == null) { + throw new IllegalArgumentException("curvatureVisitor"); + } + + this.curvatureVisitor = curvatureVisitor; + this.curvatureAlg = cAlg; + } + + /** + * Constructor. + * + * @param curvatureVisitor Curvature. If the object has + * pre-filled curvatures of meshes, then they are also used for the computation + * of significant points. In this way, it is possible to reuse already computed + * curvatures and skip calling the {@link #visitMeshFacet} inspection method. + * @param cAlg Curvature strategy to be used for the selection of significant points. + * @param perc Maximal number of significant points as percents of the original size + */ + public CurvatureSampling(Curvature curvatureVisitor, CurvatureAlg cAlg, double perc) { + super(perc); + + if (curvatureVisitor == null) { + throw new IllegalArgumentException("curvatureVisitor"); + } + + this.curvatureVisitor = curvatureVisitor; + this.curvatureAlg = cAlg; + } + + @Override + public boolean isThreadSafe() { + return false; + } + + @Override + public void visitMeshFacet(MeshFacet facet) { + significantPoints = null; // clear previous results + curvatureVisitor.visitMeshFacet(facet); // compute curvature for new inspected facet + } + + @Override + public List<MeshPoint> getSamples() { + checkAndComputeSignificantPoints(); + return significantPoints.stream().map(vc -> vc.point).collect(Collectors.toList()); + } + + public CurvatureAlg getCurvatureAlg() { + return this.curvatureAlg; + } + + /** + * Returns curvatures of selected samples + * + * @return curvatures of selected samples + */ + public List<Double> getSampledCurvatures() { + checkAndComputeSignificantPoints(); + return significantPoints.stream().map(vc -> vc.curvature).collect(Collectors.toList()); + } + + @Override + public String toString() { + return this.curvatureAlg + " curvature " + super.toString(); + } + + protected void checkAndComputeSignificantPoints() { + if (significantPoints != null) { + return; // already computed + } + + // fill the priority queue + final PriorityQueue<VertCurvature> priorityQueue = new PriorityQueue<>(); + + Map<MeshFacet, List<Double>> curvMap = null; + switch (curvatureAlg) { + case MEAN: + curvMap = curvatureVisitor.getMeanCurvatures(); + break; + case GAUSSIAN: + curvMap = curvatureVisitor.getGaussianCurvatures(); + break; + case MAX: + curvMap = curvatureVisitor.getMaxPrincipalCurvatures(); + break; + case MIN: + curvMap = curvatureVisitor.getMinPrincipalCurvatures(); + break; + default: + throw new IllegalArgumentException("curvatureAlg"); + } + + for (MeshFacet facet: curvMap.keySet()) { + List<Double> curvs = curvMap.get(facet); + for (int i = 0; i < curvs.size(); i++) { + // store helper objects, replace NaN with 0.0: + double c = curvs.get(i); + priorityQueue.add(new VertCurvature(facet.getVertex(i), (Double.isNaN(c) ? 0.0 : c))); + } + } + + // select top significant points + int maxPoints = getNumDownsampledPoints(priorityQueue.size()); + significantPoints = new ArrayList<>(maxPoints); + for (int i = 0; i < maxPoints; i++) { + VertCurvature vc = priorityQueue.poll(); + if (vc == null) { + break; // no more points available + } else { + significantPoints.add(vc); + } + } + } + + /** + * Helper class for sorting points with respect to their curvature. + * @author Radek Oslejsek + */ + private class VertCurvature implements Comparable<VertCurvature> { + + private final double curvature; + private final MeshPoint point; + + VertCurvature(MeshPoint point, double curvature) { + this.curvature = curvature; + this.point = point; + } + + /** + * Compares this object with the specified object for order. + * Curvature is taken into consideration as the primary value (descendant ordering). + * If the curvature equals, then the objects are sorted randomly (1 is always returned). + * This approach preserves all points in sorted collections. + * + * @param arg the object to be compared. + * @return a negative integer, zero, or a positive integer as this object + * is less than, equal to, or greater than the specified object. + */ + @Override + public int compareTo(VertCurvature arg) { + int comp = Double.compare(arg.curvature, this.curvature); + return (comp == 0) ? 1 : comp; + } + + @Override + public String toString() { + return ""+curvature; + } + } + +} diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/NoSampling.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/NoSampling.java new file mode 100644 index 0000000000000000000000000000000000000000..ac3cc7162b39341dc49829e70bf18c6673d04d23 --- /dev/null +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/NoSampling.java @@ -0,0 +1,36 @@ +package cz.fidentis.analyst.visitors.mesh.sampling; + +import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshPoint; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * No point sampling. All mesh points are returned. + * + * @author Radek Oslejsek + */ +public class NoSampling extends PointSampling { + + private List<MeshPoint> allVertices = new ArrayList<>(); + + @Override + public void visitMeshFacet(MeshFacet facet) { + if (facet != null) { + allVertices.addAll(facet.getVertices()); + } + } + + @Override + public List<MeshPoint> getSamples() { + return Collections.unmodifiableList(allVertices); + } + + @Override + public String toString() { + return "none"; + } + + +} diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/PointSampling.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/PointSampling.java new file mode 100644 index 0000000000000000000000000000000000000000..132b683bca045eaacbd2402baf1f69624d68c809 --- /dev/null +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/PointSampling.java @@ -0,0 +1,106 @@ +package cz.fidentis.analyst.visitors.mesh.sampling; + +import cz.fidentis.analyst.mesh.MeshVisitor; +import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshPoint; +import java.util.List; + +/** + * Point sampling strategies used to reduce the size of meshes. + * + * @author Radek Oslejsek + */ +public abstract class PointSampling extends MeshVisitor { + + /** + * If point sampling is used, then this configuration parameter + * encodes whether the reduction of mesh size is expressed as the percentage + * of the original size (number of vertices) or as the maximal number of vertices + * to be used. + * + * @author Radek Oslejsek + */ + public enum PointSamplingType { + PERCENTAGE, + MAX_VERTICES + }; + + private final PointSamplingType samplingType; + private final double samplingLimit; + + /** + * Constructor for no point sampling. + */ + public PointSampling() { + this.samplingType = null; + this.samplingLimit = 0.0; + } + + /** + * Constructor for PERCENTAGE point sampling type. + * + * @param perc Percentage - a value in (0.0, 1.0> + * @throws IllegalArgumentException if the input parameter is wrong + */ + public PointSampling(double perc) { + if (perc <= 0.0 || perc > 1) { + throw new IllegalArgumentException("perc"); + } + this.samplingType = PointSamplingType.PERCENTAGE; + this.samplingLimit = perc; + } + + /** + * Constructor for MAX_VERTICES point sampling type. + * + * @param max Maximal number of vertices. Must be bigger than zero + * @throws IllegalArgumentException if the input parameter is wrong + */ + public PointSampling(int max) { + if (max <= 0) { + throw new IllegalArgumentException("max"); + } + this.samplingType = PointSamplingType.MAX_VERTICES; + this.samplingLimit = max; + } + + @Override + public abstract void visitMeshFacet(MeshFacet facet); + + /** + * Returns a list of vertices reduced according to the strategy. + * The returned mesh points are backed by original points. + * + * @return selected vertices of inspected meshes + */ + public abstract List<MeshPoint> getSamples(); + + @Override + public String toString() { + if (this.samplingType == PointSamplingType.PERCENTAGE) { + return "sampling to " + (int) (samplingLimit * 100) + "%"; + } else { + return "sampling of " + (int) samplingLimit + " points"; + } + } + + /** + * Returns number of points to be returned after downsampling. + * + * @param origPoints Original number of vertices + * @return number of points to be returned after downsampling. + */ + protected int getNumDownsampledPoints(int origPoints) { + switch (this.samplingType) { + case PERCENTAGE: + return (int) (origPoints * this.samplingLimit); + case MAX_VERTICES: + //nt limit = (int) this.undersamplingLimit; + //return (limit <= origVertices) ? limit : origVertices; + return Math.min((int) this.samplingLimit, origPoints); + default: + return 0; + } + } + +} diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/RandomSampling.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/RandomSampling.java new file mode 100644 index 0000000000000000000000000000000000000000..87800504e32b19fc2cc5cadcd3ae958d5c96e966 --- /dev/null +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/RandomSampling.java @@ -0,0 +1,80 @@ +package cz.fidentis.analyst.visitors.mesh.sampling; + +import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshPoint; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * Random sampling when vertices are selected randomly. + * + * @author Radek Oslejsek + */ +public class RandomSampling extends PointSampling { + + private List<MeshPoint> allVertices = new ArrayList<>(); + + /** + * Constructor for PERCENTAGE point sampling type. + * + * @param perc Percentage - a value in (0.0, 1.0> + * @throws IllegalArgumentException if the input parameter is wrong + */ + public RandomSampling(double perc) { + super(perc); + } + + /** + * Constructor for MAX_VERTICES point sampling type. + * + * @param max Maximal number of vertices. Must be bigger than zero + * @throws IllegalArgumentException if the input parameter is wrong + */ + public RandomSampling(int max) { + super(max); + } + + @Override + public void visitMeshFacet(MeshFacet facet) { + if (facet != null) { + allVertices.addAll(facet.getVertices()); + } + } + + @Override + public List<MeshPoint> getSamples() { + int numReducedVertices = getNumDownsampledPoints(allVertices.size()); + + if (allVertices.size() == numReducedVertices) { + return Collections.unmodifiableList(allVertices); + } + + // generate randomly ordered indexes: + List<Integer> range = IntStream.range(0, allVertices.size()).boxed().collect(Collectors.toCollection(ArrayList::new)); + Collections.shuffle(range); + + if (numReducedVertices < allVertices.size() / 2) { // copy indices + MeshPoint[] array = new MeshPoint[allVertices.size()]; + range.stream().limit(numReducedVertices).forEach( + i -> array[i] = allVertices.get(i) + ); + return Arrays.stream(array).filter(p -> p != null).collect(Collectors.<MeshPoint>toList()); + } else { // remove indices + List<MeshPoint> copy = new ArrayList<>(allVertices); + range.stream().limit(allVertices.size() - numReducedVertices).forEach( + i -> copy.set(i, null) + ); + return copy.parallelStream().filter(p -> p != null).collect(Collectors.<MeshPoint>toList()); + } + } + + @Override + public String toString() { + return "random " + super.toString(); + } + +} diff --git a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form index 831516dd0d672de61e1f03b6ecf324a3731b9c58..a3f2757eb1431749c6705183e983d9c8032c80f1 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form @@ -41,7 +41,7 @@ <Component id="jPanel1" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="jPanel3" min="-2" max="-2" attributes="0"/> - <EmptySpace max="32767" attributes="0"/> + <EmptySpace pref="99" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> @@ -73,16 +73,14 @@ <EmptySpace max="-2" attributes="0"/> <Component id="jButton5" min="-2" max="-2" attributes="0"/> </Group> - <Group type="102" attributes="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Component id="jLabel2" min="-2" max="-2" attributes="0"/> - <Component id="jCheckBox4" alignment="0" min="-2" max="-2" attributes="0"/> - </Group> + <Group type="102" alignment="0" attributes="0"> + <Component id="jLabel2" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> - <Component id="comboSliderInteger1" min="-2" max="-2" attributes="0"/> + <Component id="spinSlider1" min="-2" max="-2" attributes="0"/> </Group> + <Component id="jCheckBox4" alignment="0" min="-2" max="-2" attributes="0"/> </Group> - <EmptySpace pref="22" max="32767" attributes="0"/> + <EmptySpace pref="96" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> @@ -90,15 +88,18 @@ <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" attributes="0"> <Group type="103" groupAlignment="0" attributes="0"> + <Group type="102" attributes="0"> + <EmptySpace max="-2" attributes="0"/> + <Component id="spinSlider1" min="-2" max="-2" attributes="0"/> + </Group> <Group type="102" alignment="0" attributes="0"> - <EmptySpace min="-2" pref="20" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="25" max="-2" attributes="0"/> <Component id="jLabel2" min="-2" max="-2" attributes="0"/> - <EmptySpace type="unrelated" max="-2" attributes="0"/> - <Component id="jCheckBox4" min="-2" max="-2" attributes="0"/> </Group> - <Component id="comboSliderInteger1" alignment="0" min="-2" max="-2" attributes="0"/> </Group> - <EmptySpace type="separate" max="-2" attributes="0"/> + <EmptySpace pref="14" max="32767" attributes="0"/> + <Component id="jCheckBox4" min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> <Component id="jCheckBox2" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/> @@ -109,7 +110,7 @@ <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="jButton5" alignment="3" min="-2" max="-2" attributes="0"/> </Group> - <EmptySpace max="32767" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> </Group> </Group> </DimensionLayout> @@ -132,8 +133,6 @@ </Property> </Properties> </Component> - <Component class="cz.fidentis.analyst.core.ComboSliderInteger" name="comboSliderInteger1"> - </Component> <Component class="javax.swing.JCheckBox" name="jCheckBox1"> <Properties> <Property name="selected" type="boolean" value="true"/> @@ -179,6 +178,8 @@ <Property name="enabled" type="boolean" value="false"/> </Properties> </Component> + <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider1"> + </Component> </SubComponents> </Container> <Container class="javax.swing.JPanel" name="jPanel2"> 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 1be2375c85b26cafe3b9270085628018caa59e0a..37fd2e6e1360d037e6e47ad386834cd5b24794c3 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java @@ -72,11 +72,9 @@ public class BatchPanel extends ControlPanel { jButton1.addActionListener(createListener(action, ACTION_COMMAND_COMPUTE_ICP)); - 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(); + spinSlider1.initPercentage(this.undersampling); + spinSlider1.addSpinnerListener((ActionEvent ae) -> { // update slider when the input field changed + this.undersampling = (Integer) spinSlider1.getValue(); }); jComboBox2.addItem(SIMILARITY_APPROX_HD); @@ -324,12 +322,12 @@ public class BatchPanel extends ControlPanel { jPanel1 = new javax.swing.JPanel(); jButton1 = new javax.swing.JButton(); jLabel2 = new javax.swing.JLabel(); - comboSliderInteger1 = new cz.fidentis.analyst.core.ComboSliderInteger(); jCheckBox1 = new javax.swing.JCheckBox(); jCheckBox2 = new javax.swing.JCheckBox(); jCheckBox3 = new javax.swing.JCheckBox(); jCheckBox4 = new javax.swing.JCheckBox(); jButton5 = new javax.swing.JButton(); + spinSlider1 = new cz.fidentis.analyst.core.SpinSlider(); jPanel2 = new javax.swing.JPanel(); jLabel1 = new javax.swing.JLabel(); jComboBox1 = new javax.swing.JComboBox<>(); @@ -393,24 +391,25 @@ public class BatchPanel extends ControlPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jButton5)) .addGroup(jPanel1Layout.createSequentialGroup() - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel2) - .addComponent(jCheckBox4)) + .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(comboSliderInteger1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addContainerGap(22, Short.MAX_VALUE)) + .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(jCheckBox4)) + .addContainerGap(96, Short.MAX_VALUE)) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(20, 20, 20) - .addComponent(jLabel2) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jCheckBox4)) - .addComponent(comboSliderInteger1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(18, 18, 18) + .addContainerGap() + .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(25, 25, 25) + .addComponent(jLabel2))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 14, Short.MAX_VALUE) + .addComponent(jCheckBox4) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jCheckBox2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jCheckBox1) @@ -420,7 +419,7 @@ public class BatchPanel extends ControlPanel { .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jButton1) .addComponent(jButton5)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) ); jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(BatchPanel.class, "BatchPanel.jPanel2.border.title_1"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 12))); // NOI18N @@ -537,7 +536,7 @@ public class BatchPanel extends ControlPanel { .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(99, Short.MAX_VALUE)) ); }// </editor-fold>//GEN-END:initComponents @@ -555,7 +554,6 @@ public class BatchPanel extends ControlPanel { // Variables declaration - do not modify//GEN-BEGIN:variables - private cz.fidentis.analyst.core.ComboSliderInteger comboSliderInteger1; private javax.swing.JButton jButton1; private javax.swing.JButton jButton4; private javax.swing.JButton jButton5; @@ -574,6 +572,7 @@ public class BatchPanel extends ControlPanel { private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel2; private javax.swing.JPanel jPanel3; + private cz.fidentis.analyst.core.SpinSlider spinSlider1; // End of variables declaration//GEN-END:variables } 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 ecd13150a8c63f47646dd126b46c960b3caef3e8..f62bf7e5f15183b1515a78423f14592b708ad4b6 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java @@ -10,6 +10,7 @@ import cz.fidentis.analyst.mesh.core.MeshModel; import cz.fidentis.analyst.mesh.io.MeshObjExporter; import cz.fidentis.analyst.scene.DrawableFace; import cz.fidentis.analyst.visitors.kdtree.AvgFaceConstructor; +import cz.fidentis.analyst.visitors.mesh.sampling.RandomSampling; import java.io.File; import java.io.IOException; import java.nio.file.Path; @@ -120,7 +121,7 @@ public class IcpTask extends SwingWorker<MeshModel, HumanFace> { 100, // max iterations controlPanel.scaleIcp(), 0.3, // error - undersampling, + new RandomSampling(undersampling), false // drop k-d tree, if exists ); icpComputationTime.stop(); diff --git a/GUI/src/main/java/cz/fidentis/analyst/canvas/toolbar/RenderingModeToolbox.java b/GUI/src/main/java/cz/fidentis/analyst/canvas/toolbar/RenderingModeToolbox.java index 5aa3e880df3afd89a0d66bc08eefa064cfeac253..8ae8cd93716df9c13839f5ba38561bafac378a42 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/canvas/toolbar/RenderingModeToolbox.java +++ b/GUI/src/main/java/cz/fidentis/analyst/canvas/toolbar/RenderingModeToolbox.java @@ -2,6 +2,7 @@ package cz.fidentis.analyst.canvas.toolbar; import com.jogamp.opengl.GL2; import cz.fidentis.analyst.canvas.Canvas; +import cz.fidentis.analyst.scene.DrawableFace; import java.awt.GridLayout; import java.awt.event.ActionEvent; import javax.swing.AbstractAction; @@ -63,9 +64,68 @@ public class RenderingModeToolbox extends JPopupMenu { } }); + JMenuItem menuItem4 = new JMenuItem(new AbstractAction() { // Random sampling + @Override + public void actionPerformed(ActionEvent e) { + canvas.getScene().getFaceSlots().forEach(i -> { + canvas.getScene().getDrawableFace(i).setRenderMode(DrawableFace.RANDOM_SAMPLING); + }); + canvas.renderScene(); + } + }); + + JMenuItem menuItem5 = new JMenuItem(new AbstractAction() { // Curvature sampling + @Override + public void actionPerformed(ActionEvent e) { + canvas.getScene().getFaceSlots().forEach(i -> { + canvas.getScene().getDrawableFace(i).setRenderMode(DrawableFace.CURVATURE_SAMPLING_MIN); + }); + canvas.renderScene(); + } + }); + + JMenuItem menuItem6 = new JMenuItem(new AbstractAction() { // Curvature sampling + @Override + public void actionPerformed(ActionEvent e) { + canvas.getScene().getFaceSlots().forEach(i -> { + canvas.getScene().getDrawableFace(i).setRenderMode(DrawableFace.CURVATURE_SAMPLING_MAX); + }); + canvas.renderScene(); + } + }); + + JMenuItem menuItem7 = new JMenuItem(new AbstractAction() { // Curvature sampling + @Override + public void actionPerformed(ActionEvent e) { + canvas.getScene().getFaceSlots().forEach(i -> { + canvas.getScene().getDrawableFace(i).setRenderMode(DrawableFace.CURVATURE_SAMPLING_MEAN); + }); + canvas.renderScene(); + } + }); + + JMenuItem menuItem8 = new JMenuItem(new AbstractAction() { // Curvature sampling + @Override + public void actionPerformed(ActionEvent e) { + canvas.getScene().getFaceSlots().forEach(i -> { + canvas.getScene().getDrawableFace(i).setRenderMode(DrawableFace.CURVATURE_SAMPLING_GAUSSIAN); + }); + canvas.renderScene(); + } + }); + menuItem1.setIcon(new ImageIcon(getClass().getResource("/" + RenderingModeToolbox.SMOOT_BUTTON_ICON))); menuItem2.setIcon(new ImageIcon(getClass().getResource("/" + RenderingModeToolbox.WIREFRAME_BUTTON_ICON))); menuItem3.setIcon(new ImageIcon(getClass().getResource("/" + RenderingModeToolbox.POINTS_BUTTON_ICON))); + + menuItem1.setText("Shading"); + menuItem2.setText("Wire-frame"); + menuItem3.setText("Points Cloud"); + menuItem4.setText("Random Sampling"); + menuItem5.setText("Min. Curvature"); + menuItem6.setText("Max. Curvature"); + menuItem7.setText("Mean Curvature"); + menuItem8.setText("Gaussian Curvature"); menuItem1.setToolTipText(NbBundle.getMessage(RenderingModeToolbox.class, "RenderingModeToolbox.smooth.text")); menuItem2.setToolTipText(NbBundle.getMessage(RenderingModeToolbox.class, "RenderingModeToolbox.wireframe.text")); @@ -74,7 +134,14 @@ public class RenderingModeToolbox extends JPopupMenu { add(menuItem1); add(menuItem2); add(menuItem3); + addSeparator(); + add(menuItem4); + add(menuItem5); + add(menuItem6); + add(menuItem7); + add(menuItem8); - setLayout(new GridLayout(1,0)); + //setLayout(new GridLayout(1,0)); + setLayout(new GridLayout(0,1)); } } diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/ComboSlider.java b/GUI/src/main/java/cz/fidentis/analyst/core/ComboSlider.java deleted file mode 100644 index 26e30149ee219feb0fcc317a180d28ef951222cd..0000000000000000000000000000000000000000 --- a/GUI/src/main/java/cz/fidentis/analyst/core/ComboSlider.java +++ /dev/null @@ -1,144 +0,0 @@ -package cz.fidentis.analyst.core; - -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import javax.swing.JFormattedTextField; -import javax.swing.JPanel; -import javax.swing.JSlider; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -/** - * ComboSlider implements a combination of horizontal slider and input text field. - * The slider and input field are synchronized automatically. - * - * @author Radek Oslejsek - */ -public abstract class ComboSlider extends JPanel { - - private final JSlider slider = new JSlider(); - private final JFormattedTextField inputField = new JFormattedTextField(); - private boolean continousSync; - - public static final int DEFAULT_TEXT_FIELD_WIDTH = 70; - public static final int DEFAULT_TEXT_FIELD_HEIGHT = 40; - - private ChangeListener changeListener = new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - setValueFromSlider(); - } - }; - - private MouseListener mouseListener = new MouseAdapter() { - @Override - public void mouseReleased(MouseEvent e) { - setValueFromSlider(); - } - }; - - /** - * Constructor. - */ - public ComboSlider() { - initComponents(); - - setContinousSync(true); // update input field on slider's change and inform external listeners - - inputField.addActionListener((ActionEvent ae) -> { // update slider when the input field changed - setValueFromInputField(); - }); - - } - - /** - * Connects the specified listener with the input field. - * The listener is invoked on the input field change, which is affected - * by the {@link #setContinousSync(boolean)}. - * Event's source is set to {@code JFormattedTextField} - * - * @param listener the action listener to be added - */ - public synchronized void addInputFieldListener(ActionListener listener) { - inputField.addActionListener(listener); - } - - /** - * Connects the specified listener with the slider. - * Event's source is set to {@code JSlider} - * - * @param listener the action listener to be added - */ - public synchronized void addSliderListener(ActionListener listener) { - slider.addChangeListener((ChangeEvent e) -> { - listener.actionPerformed(new ActionEvent(slider, ActionEvent.ACTION_PERFORMED, null)); - }); - } - - /** - * The slider is on the left, followed by ti input field, by default. - * This method switches the order. - */ - public void setSliderEast() { - remove(slider); - add(slider); - } - - /** - * If {@code true}, then the input field is updated during the slider move. - * Otherwise, the input field is updated only on mouse key release. - * - * @param continousSync Whether to update input field continuously. - */ - public final void setContinousSync(boolean continousSync) { - this.continousSync = continousSync; - if (this.continousSync) { - slider.removeMouseListener(mouseListener); - slider.addChangeListener(changeListener); - } else { - slider.addMouseListener(mouseListener); - slider.removeChangeListener(changeListener); - } - } - - /** - * If {@code true}, then the input field is updated during the slider move. - * Otherwise, the input field is updated only on mouse key release. - * - * @return {@code true} if the slider and input field are synchronized continuously. - */ - public boolean isContinousSync() { - return continousSync; - } - - public JSlider getSlider() { - return slider; - } - - public JFormattedTextField getInputField() { - return inputField; - } - - /** - * Reads the value of the slider, scales it into the range of the input field, - * updates the input field and triggers input field change event. - */ - protected abstract void setValueFromSlider(); - - /** - * Reads the value of the input field, scales it into the range of the slider, - * and updates the slider. No change event is triggered. - */ - protected abstract void setValueFromInputField(); - - protected final void initComponents() { - inputField.setPreferredSize(new Dimension(DEFAULT_TEXT_FIELD_WIDTH, DEFAULT_TEXT_FIELD_HEIGHT)); - add(slider); - add(inputField); - } - -} diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/ComboSliderDouble.java b/GUI/src/main/java/cz/fidentis/analyst/core/ComboSliderDouble.java deleted file mode 100644 index f5f7a1b36db70b9689c5be7750672466a111891a..0000000000000000000000000000000000000000 --- a/GUI/src/main/java/cz/fidentis/analyst/core/ComboSliderDouble.java +++ /dev/null @@ -1,166 +0,0 @@ -package cz.fidentis.analyst.core; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.math.RoundingMode; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.Locale; -import javax.swing.text.DefaultFormatterFactory; -import javax.swing.text.NumberFormatter; - -/** - * ComboSlider with real numbers. - * The input field handles real number in given precision (number of fraction digits). - * The slider shows real numbers on an automatic integer scale. - * - * @author Radek Oslejsek - */ -public class ComboSliderDouble extends ComboSlider { - - private double min = 0.0; - private double max = 1.0; - private int fractionDigits = 2; - - /** - * Constructor. The default scale and precision is from 0.00 to 1.00. - */ - public ComboSliderDouble() { - setRange(0.0, 1.0, 2); - } - - /** - * Sets the slider and input field range. - * - * @param min Minimum value - * @param max Maximum value. If less than {@code min}, then {@code min} is used instead. - * @param fractionDigits precision, i.e., the number of allowed digits in the fraction part of real numbers - */ - public final void setRange(double min, double max, int fractionDigits) { - this.min = min; - this.max = (max < min) ? min : max; - this.fractionDigits = fractionDigits; - - NumberFormat format = DecimalFormat.getInstance(Locale.getDefault()); - format.setMinimumFractionDigits(1); - format.setMaximumFractionDigits(fractionDigits); - format.setRoundingMode(RoundingMode.HALF_UP); - - NumberFormatter formatter = new NumberFormatter(format); - formatter.setValueClass(Double.class); - formatter.setMinimum(this.min); - formatter.setMaximum(this.max); - formatter.setAllowsInvalid(false); - - getInputField().setFormatterFactory(new DefaultFormatterFactory(formatter)); - getInputField().setText(TextFieldUtils.doubleToStringLocale(this.min, fractionDigits)); - - getSlider().setMinimum((int) (this.min * getRecomputationFactor())); - getSlider().setMaximum((int) (this.max * getRecomputationFactor())); - - setValue(this.min); - } - - /** - * Connects the specified listener witch the input field. - * The listener is invoked on the input field change, which is affected - * by the {@link #setContinousSync(boolean)}. - * In contrast to the {@link #addInputFieldListener(java.awt.event.ActionListener)}, - * the event's source is set to {@code ComboSliderDouble}. Therefore, - * the {@link #getValue()} method can be used directly. - * - * @param listener the action listener to be added - */ - public synchronized void addListener(ActionListener listener) { - getInputField().addActionListener((ActionEvent e) -> { - listener.actionPerformed(new ActionEvent(this, e.getID(), e.getActionCommand())); - }); - } - - - - /** - * Returns the value. - * - * @return the value. - */ - public double getValue() { - return TextFieldUtils.parseLocaleDouble(getInputField()); - } - - /** - * Sets the value of the slider and input field. - * If the value is outside of the range then nothing happens. - * - * @param value a new value - */ - public void setValue(double value) { - if (value >= getMinimum() && value <= getMaximum()) { - getInputField().setText(TextFieldUtils.doubleToStringLocale(value, fractionDigits)); - getInputField().postActionEvent(); // invoke textField action listener - } - } - - /** - * Sets the value of the slider and input field. - * In contrast to the {@link #setValue(double)} method, - * this call does not trigger change event. - * - * @param value a new value - */ - public void setValueSilently(double value) { - if (value >= getMinimum() && value <= getMaximum()) { - getInputField().setText(TextFieldUtils.doubleToStringLocale(value, fractionDigits)); - setValueFromInputField(); - } - } - - /** - * Returns the lower limit of the range. - * - * @return the lower limit - */ - public double getMinimum() { - return this.min; - } - - /** - * Returns the upper limit of the range. - * - * @return the upper limit - */ - public double getMaximum() { - return this.max; - } - - /** - * Returns the number of allowed digits in the fraction part of real numbers. - * - * @return the number of allowed digits in the fraction part of real numbers - */ - public int getFractionDigits() { - return this.fractionDigits; - } - - @Override - protected void setValueFromSlider() { - setValue(getSlider().getValue() / getRecomputationFactor()); // also triggers the event - } - - @Override - protected void setValueFromInputField() { - getSlider().setValue((int) (TextFieldUtils.parseLocaleDouble(getInputField()) * getRecomputationFactor())); - } - - /** - * Returns 1, 10, 100, etc., based on the fraction digits. - * @return 1, 10, 100, etc., based on the fraction digits. - */ - public double getRecomputationFactor() { - int scale = 1; - for (int i = 0; i < fractionDigits; i++) { - scale *= 10; - } - return scale; - } -} diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/ComboSliderInteger.java b/GUI/src/main/java/cz/fidentis/analyst/core/ComboSliderInteger.java deleted file mode 100644 index cc626be175614601e49389c9e3322e00d7e4ea74..0000000000000000000000000000000000000000 --- a/GUI/src/main/java/cz/fidentis/analyst/core/ComboSliderInteger.java +++ /dev/null @@ -1,100 +0,0 @@ -package cz.fidentis.analyst.core; - -import java.text.NumberFormat; -import javax.swing.text.DefaultFormatterFactory; -import javax.swing.text.NumberFormatter; - -/** - * ComboSlider with integer numbers. - * - * @author Radek Oslejsek - */ -public class ComboSliderInteger extends ComboSlider { - - private int min = 0; - private int max = 100; - - /** - * Constructor. The default scale is from 0 to 100. - */ - public ComboSliderInteger() { - setRange(0, 100); - } - - /** - * Sets the slider and input field range. - * - * @param min Minimum value - * @param max Maximum value. If less than {@code min}, then {@code min} is used instead. - */ - public final void setRange(int min, int max) { - this.min = min; - this.max = (max < min) ? min : max; - - NumberFormatter formatter = new NumberFormatter(NumberFormat.getInstance()); - formatter.setValueClass(Integer.class); - formatter.setMinimum(this.min); - formatter.setMaximum(this.max); - formatter.setAllowsInvalid(false); - - getInputField().setFormatterFactory(new DefaultFormatterFactory(formatter)); - getInputField().setText(Integer.toString(this.min)); - - getSlider().setMinimum(this.min); - getSlider().setMaximum(this.max); - - setValue(this.min); - getInputField().postActionEvent(); // invoke textField action listener - } - - /** - * Returns the value. - * - * @return the value. - */ - public int getValue() { - return TextFieldUtils.parseLocaleInt(getInputField()); - } - - /** - * Sets the value of the slider and input field. - * If the value is outside of the range then nothing happens. - * - * @param value a new value - */ - public void setValue(int value) { - if (value >= getMinimum() && value <= getMaximum()) { - getInputField().setText(TextFieldUtils.intToStringLocale(value)); - getInputField().postActionEvent(); // invoke textField action listener - } - } - - /** - * Returns the lower limit of the range. - * - * @return the lower limit - */ - public int getMinimum() { - return this.min; - } - - /** - * Returns the upper limit of the range. - * - * @return the upper limit - */ - public int getMaximum() { - return this.max; - } - - @Override - protected void setValueFromSlider() { - setValue(getSlider().getValue()); // also triggers the event - } - - @Override - protected void setValueFromInputField() { - getSlider().setValue(TextFieldUtils.parseLocaleInt(getInputField())); - } - -} diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/DoubleSpinner.java b/GUI/src/main/java/cz/fidentis/analyst/core/DoubleSpinner.java new file mode 100644 index 0000000000000000000000000000000000000000..c7f9c43ca0313931a54eab278a50b9346ede1a3f --- /dev/null +++ b/GUI/src/main/java/cz/fidentis/analyst/core/DoubleSpinner.java @@ -0,0 +1,71 @@ +package cz.fidentis.analyst.core; + +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * A single line input field that lets the user show and select real number + * from an ordered sequence. + * + * @author Radek Oslejsek + */ +public class DoubleSpinner extends JSpinner { + + private int fractionDigits = 2; + + private static final double STEP_RATIO = 0.1; + + private SpinnerNumberModel model; + + /** + * Constructor. + * + * @param fractionDigits precision of flouting numbers, + * i.e., the number of digits allowed after the floating dot + */ + public DoubleSpinner(int fractionDigits) { + this.fractionDigits = fractionDigits; + + model = new SpinnerNumberModel(0.0, -getRecomputationFactor(), getRecomputationFactor(), 0.1); + this.setModel(model); + //model.setMinimum(min); + //smodel.setMinimum(max); + + // Step recalculation + this.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + Double value = getDouble(); + // Steps are sensitive to the current magnitude of the value + long magnitude = Math.round(Math.log10(value)); + double stepSize = STEP_RATIO * Math.pow(10, magnitude); + if (stepSize == 0.0) { + stepSize = 0.1; + } + model.setStepSize(stepSize); + } + }); + + } + + /** + * Returns the current value as a Double + */ + public Double getDouble() { + return (Double) getValue(); + } + + /** + * Returns 1, 10, 100, etc., based on the fraction digits. + * @return 1, 10, 100, etc., based on the fraction digits. + */ + public double getRecomputationFactor() { + int scale = 1; + for (int i = 0; i < fractionDigits; i++) { + scale *= 10; + } + return scale; + } +} \ No newline at end of file diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/SpinSlider.java b/GUI/src/main/java/cz/fidentis/analyst/core/SpinSlider.java new file mode 100644 index 0000000000000000000000000000000000000000..211289a4b2ead39e56b0d34484b915b7ce8141b3 --- /dev/null +++ b/GUI/src/main/java/cz/fidentis/analyst/core/SpinSlider.java @@ -0,0 +1,292 @@ +package cz.fidentis.analyst.core; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * SpinSlider implements a combination of horizontal slider and input text field (Spinner). + * The slider and the spinner are synchronized automatically. + * + * @author Radek Oslejsek + */ +public class SpinSlider extends JPanel { + + /** + * SpinSlider type + * + * @author Radek Oslejsek + */ + public enum ValueType { + INTEGER, + DOUBLE, + PERCENTAGE + }; + + private final JSlider slider = new JSlider(); + private JSpinner spinner; + private ValueType type; + + private boolean continousSync; + + /** + * Listener for continuous synchronization of the slider and the spinner. + * The spinner value is updates whenever the slider is moved. + */ + private ChangeListener changeListener = new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider s = (JSlider) e.getSource(); + if (spinner.getClass() == DoubleSpinner.class) { + Double val = Double.valueOf((Integer) s.getValue()); + val /= ((DoubleSpinner) spinner).getRecomputationFactor(); + spinner.setValue(val); + } else { + spinner.setValue(s.getValue()); + } + } + }; + + /** + * Listener for postponed synchronization of the slider and the spinner. + * The spinner value remains unchanged until the mouse button is released. + */ + private MouseListener mouseListener = new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + JSlider s = (JSlider) e.getSource(); + if (spinner.getClass() == DoubleSpinner.class) { + Double val = Double.valueOf((Integer) s.getValue()); + val /= ((DoubleSpinner) spinner).getRecomputationFactor(); + spinner.setValue(val); + } else { + spinner.setValue(s.getValue()); + } + } + }; + + + /** + * Constructor that creates percentage spin-slider. + * Call {@link #initDouble(double, double, double, int)} + * or {@link #initInteger(int, int, int, int)} to change it. + */ + public SpinSlider() { + initPercentage(100); + initComponents(); + setContinousSync(true); // update input field on slider's change and inform external listeners + slider.setFont(new java.awt.Font("Dialog", 1, 0)); // hide numbers + + setBackground(Color.LIGHT_GRAY); + } + + /** + * Returns current value reflecting the slider's position. + * Even if the postponed synchronization is turned on (see {@link #setContinousSync(boolean)}), + * the correct value is computed and returned anyway. + * <p> + * Based on the type, the return value is + * <ul> + * <li>{@code INTEGER}: An {@code Integer} from set range.</li> + * <li>{@code DOUBLE}: An {@code Double} from set range.</li> + * <li>{@code PERCENTAGE}: An {@code Integer} between 0 and 100.</li> + * </ul> + * </p> + * <p> + * Usage: + * <ul> + * <li>After {@code initDouble()}: {@code double var = (Double) spinSlider.getValue()}.</li> + * <li>After {@code initInteger()}: {@code double var = (Integer) spinSlider.getValue()}.</li> + * <li>After {@code initPercentage()}: {@code double var = (Integer) spinSlider.getValue()}.</li> + * </ul> + * </p> + * + * @return Current value + */ + public Number getValue() { + if (this.type == ValueType.DOUBLE) { + DoubleSpinner ds = (DoubleSpinner) spinner; + return slider.getValue() / ds.getRecomputationFactor(); + } else { + return (Number) spinner.getValue(); + } + } + + /** + * Sets the value. + * + * @param value A new value + */ + public void setValue(Number value) { + spinner.setValue(value); + } + + /** + * Initializes this spin-slider to integers. + * + * @param value Initial value + * @param minimum Minimum value + * @param maximum Maximum value + * @param stepSize Spin step size + */ + public void initInteger(int value, int min, int max, int stepSize) { + remove(slider); + if (spinner != null) { + remove(spinner); + } + + this.type = ValueType.INTEGER; + spinner = new JSpinner(); + spinner.setModel(new SpinnerNumberModel(value, min, max, stepSize)); + spinner.setEditor(new JSpinner.NumberEditor(spinner)); + slider.setMinimum(min); + slider.setMaximum(max); + + initComponents(); + setValue(value); + } + + /** + * Initializes this spin-slider to doubles. + * + * @param value Initial value + * @param minimum Minimum value + * @param maximum Maximum value + * @param fractionDigits precision of flouting numbers, i.e., + * the number of digits allowed after the floating dot + */ + public void initDouble(double value, double min, double max, int fractionDigits) { + remove(slider); + if (spinner != null) { + remove(spinner); + } + + this.type = ValueType.DOUBLE; + DoubleSpinner ds = new DoubleSpinner(fractionDigits); + spinner = ds; + slider.setMinimum((int) (min * ds.getRecomputationFactor())); + slider.setMaximum((int) (max * ds.getRecomputationFactor())); + + initComponents(); + setValue(value); + } + + /** + * Initializes this spin-slider to percents. + * + * @param value Initial value between 0 and 100. + */ + public void initPercentage(int value) { + remove(slider); + if (spinner != null) { + remove(spinner); + } + + this.type = ValueType.PERCENTAGE; + spinner = new JSpinner(); + spinner.setModel(new SpinnerNumberModel(value, 0, 100, 1)); + spinner.setEditor(new JSpinner.NumberEditor(spinner, "0'%'")); + slider.setMinimum(0); + slider.setMaximum(100); + slider.setValue(value); + + initComponents(); + setValue(value); + } + + /** + * The slider is on the left, followed by ti input field, by default. + * This method switches the order. + */ + //public void setSliderEast() { + // remove(slider); + // add(slider); + //} + + /** + * If {@code true}, then the spinner is updated continuously during the slider move. + * Otherwise, the spinner remains unchanged until the mouse key releases. + * + * @param continousSync Whether to update the spinner continuously. + */ + public final void setContinousSync(boolean continousSync) { + this.continousSync = continousSync; + if (this.continousSync) { + slider.removeMouseListener(mouseListener); + slider.addChangeListener(changeListener); + } else { + slider.addMouseListener(mouseListener); + slider.removeChangeListener(changeListener); + } + } + + /** + * If {@code true}, then the spinner is updated continuously during the slider move. + * Otherwise, the spinner remains unchanged until the mouse key releases. + * + * @return {@code true} if the slider and the spinner are synchronized continuously. + */ + public boolean isContinousSync() { + return continousSync; + } + + /** + * Be informed when the spinner's value changes. + * See also {@link #setContinousSync(boolean)}. + * Event's source is set to {@code JSpinner}. + * For the double type of the spin-slider, it is {@code DoubleSpinner} + * + * @param listener the action listener to be added + */ + public synchronized void addSpinnerListener(ActionListener listener) { + spinner.addChangeListener((ChangeEvent e) -> { + listener.actionPerformed(new ActionEvent(spinner, ActionEvent.ACTION_PERFORMED, null)); + }); + } + + /** + * Be informed when the slider changes. + * See also {@link #setContinousSync(boolean)}. + * Event's source is set to {@code JSlider} + * + * @param listener the action listener to be added + */ + public synchronized void addSliderListener(ActionListener listener) { + slider.addChangeListener((ChangeEvent e) -> { + listener.actionPerformed(new ActionEvent(slider, ActionEvent.ACTION_PERFORMED, null)); + }); + } + + protected final void initComponents() { + //this.setLayout(new FlowLayout()); + this.setLayout(new BorderLayout()); + + add(slider, BorderLayout.LINE_START); + add(spinner, BorderLayout.LINE_END); + + spinner.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSpinner s = (JSpinner) e.getSource(); + if (spinner.getClass() == DoubleSpinner.class) { + Double val = (Double) s.getValue(); + val *= ((DoubleSpinner) spinner).getRecomputationFactor(); + slider.setValue(val.intValue()); + //slider.setValue(((Double) s.getValue()).intValue()); + } else { + slider.setValue((Integer) s.getValue()); + } + } + }); + } +} diff --git a/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java b/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java index 28798ab389df4daee6ae1fef6d76c0c000980706..a2ffebfbebb4ea513901d110f02e0a68fa4f131d 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java @@ -193,7 +193,7 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe resizeFeaturePoint((LoadedActionEvent) ae); break; case FeaturePointsPanel.ACTION_COMMAND_DISTANCE_RECOMPUTE: - computeAndUpdateHausdorffDistance(true); + computeAndUpdateHausdorffDistance(false); break; default: // to nothing @@ -319,17 +319,18 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe controlPanel.getFeaturePointsListPanel().updateFeaturePointWeights( whdVisitor.getFeaturePointWeights() .get(getSecondaryDrawableFace().getHumanFace()) // Get FP weights for the secondary face - .entrySet() + .entrySet() // Entry of Map<FeaturePointType, Map<MeshFacet, Double>> .stream() - .collect( - Collectors.toMap( - Map.Entry::getKey, // For each FP type at the secondary face... - weights -> weights.getValue() // ... compute average FP weight over all its facets - .values() - .stream() - .mapToDouble(Double::doubleValue) - .average() - .orElse(Double.NaN)))); + .collect(Collectors.toMap( + Map.Entry::getKey, // For each FP type at the secondary face... + weights -> weights.getValue() // ... compute average FP weight over all its facets + .values() // Collection<Double> + .stream() + .mapToDouble(Double::doubleValue) + .average() + .orElse(Double.NaN)) + ) + ); } private void selectFeaturePoint(LoadedActionEvent actionEvent) { diff --git a/GUI/src/main/java/cz/fidentis/analyst/distance/FeaturePointsPanel.java b/GUI/src/main/java/cz/fidentis/analyst/distance/FeaturePointsPanel.java index 605aa4c9e3b58e1b09379fa0b6a464ad15454708..a0d7565ccc6dbc22d48f20b5607b0e6a588deb21 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/distance/FeaturePointsPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/distance/FeaturePointsPanel.java @@ -1,7 +1,7 @@ package cz.fidentis.analyst.distance; -import cz.fidentis.analyst.core.ComboSliderDouble; import cz.fidentis.analyst.core.LoadedActionEvent; +import cz.fidentis.analyst.core.SpinSlider; import cz.fidentis.analyst.feature.FeaturePoint; import cz.fidentis.analyst.feature.FeaturePointType; import cz.fidentis.analyst.scene.DrawableFpWeights; @@ -37,7 +37,7 @@ public class FeaturePointsPanel extends JPanel { private Map<FeaturePointType, JLabel> featurePointStats; private List<JCheckBox> featurePointCheckBoxes; - private List<ComboSliderDouble> comboSliders; + private List<SpinSlider> comboSliders; /** * Constructor. @@ -112,8 +112,8 @@ public class FeaturePointsPanel extends JPanel { if (index < 0 || index >= comboSliders.size()) { return Double.NaN; } - ComboSliderDouble sl = comboSliders.get(index); - return sl.getSlider().getValue() / sl.getRecomputationFactor(); + SpinSlider sl = comboSliders.get(index); + return (Double) sl.getValue(); } /** @@ -190,26 +190,23 @@ public class FeaturePointsPanel extends JPanel { c2.fill = GridBagConstraints.NONE; c2.insets = new Insets(0, 0, 0, 0); - final ComboSliderDouble slider = new ComboSliderDouble(); // 0.0 .. 1.0 by default + final SpinSlider slider = new SpinSlider(); // 0.0 .. 1.0 by default + slider.initDouble(DrawableFpWeights.FPW_DEFAULT_SIZE, 0.0, 100.0, 2); slider.setContinousSync(false); - slider.setRange(0, 100, 2); - slider.setValue(DrawableFpWeights.FPW_DEFAULT_SIZE); //slider.addInputFieldListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_RESIZE, row)); slider.addSliderListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_RESIZE, row)); - slider.addInputFieldListener( - (ActionEvent e) -> { - if (!checkBox.isSelected()) { - return; // Recompute only if the feature point is selected - } - action.actionPerformed(new ActionEvent( - e.getSource(), - ActionEvent.ACTION_PERFORMED, - ACTION_COMMAND_DISTANCE_RECOMPUTE - )); - } - ); + slider.addSpinnerListener((ActionEvent e) -> { + if (!checkBox.isSelected()) { + return; // Recompute only if the feature point is selected + } + action.actionPerformed(new ActionEvent( + e.getSource(), + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_DISTANCE_RECOMPUTE + )); + }); add(slider, c2); comboSliders.add(slider); 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 b8ceea78b48a0ea4b7010e594102f553b84cd638..3769d9fe416cf04c9240181bfcedcbf571b28091 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java @@ -9,6 +9,11 @@ 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.face.events.SymmetryPlaneChangedEvent; +import cz.fidentis.analyst.visitors.mesh.Curvature; +import cz.fidentis.analyst.visitors.mesh.sampling.CurvatureSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.RandomSampling; import java.awt.Color; import java.awt.event.ActionEvent; import javax.swing.JFormattedTextField; @@ -214,6 +219,7 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL protected void applyICP() { Logger out = Logger.measureTime(); + PointSampling samplingStrategy = getSamplingStrategy(getCanvas().getSecondaryFace().getCurvature()); HumanFaceUtils.alignMeshes( getCanvas().getPrimaryFace(), @@ -221,7 +227,7 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL controlPanel.getMaxIcpIterParam(), controlPanel.getScaleParam(), controlPanel.getMinIcpErrorParam(), - controlPanel.getIcpUndersampling(), + samplingStrategy, false // drop k-d tree, if exists ); @@ -229,7 +235,8 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL + getCanvas().getPrimaryFace().getMeshModel().getNumVertices() + "/" + getCanvas().getSecondaryFace().getMeshModel().getNumVertices() - + " vertices" + + " vertices. Downsampling of the secondary face: " + + samplingStrategy ); } @@ -278,4 +285,26 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL true // forbid rotation around normal ); } + + private PointSampling getSamplingStrategy(Curvature samplingCurvature) { + String st = controlPanel.getIcpUdersamplingStrategy(); + double strength = controlPanel.getIcpUndersamplingStrength() / 100.0; + + if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[0])) { + return new NoSampling(); + } else if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[1])) { + return new RandomSampling(strength); + } else if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[2])) { + return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.MEAN, strength); + } else if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[3])) { + return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.GAUSSIAN, strength); + } else if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[4])) { + return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.MAX, strength); + } else if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[5])) { + return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.MIN, strength); + } else { + return null; + } + } + } 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 d7774fda01f026a5626269f363185cb29cd662c7..3f0d2ad93b51e0bae609084bab9e6d169383a9d9 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.form +++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.form @@ -77,7 +77,7 @@ <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="451" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="481" 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"/> @@ -899,34 +899,53 @@ <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" attributes="0"> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jLabel9" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jLabel5" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jLabel6" alignment="0" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace min="-2" pref="15" max="-2" attributes="0"/> + </Group> + <Group type="102" alignment="1" attributes="0"> + <Component id="jLabel8" min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + </Group> + </Group> + <Group type="103" groupAlignment="0" attributes="0"> + <Group type="102" attributes="0"> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jFormattedTextField2" min="-2" pref="53" max="-2" attributes="0"/> + <Component id="jFormattedTextField1" min="-2" pref="49" max="-2" attributes="0"/> + <Component id="jCheckBox1" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="spinSlider1" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace min="0" pref="0" max="32767" attributes="0"/> + </Group> + <Group type="102" alignment="0" attributes="0"> + <Component id="jComboBox1" min="-2" max="-2" attributes="0"/> + <EmptySpace max="32767" attributes="0"/> + </Group> + </Group> + </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"/> + <Group type="103" groupAlignment="1" attributes="0"> + <Component id="jLabel3" alignment="0" 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> + </Group> <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"/> + <EmptySpace max="32767" attributes="0"/> </Group> </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> @@ -942,22 +961,37 @@ </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"/> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/> + <Component id="jLabel3" min="-2" max="-2" attributes="0"/> </Group> - <EmptySpace max="-2" attributes="0"/> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jFormattedTextField1" min="-2" pref="22" max="-2" attributes="0"/> + <Component id="jLabel5" alignment="0" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace type="unrelated" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Component id="comboSliderInteger1" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jFormattedTextField2" min="-2" pref="20" max="-2" attributes="0"/> + <Component id="jLabel6" min="-2" pref="16" max="-2" attributes="0"/> + </Group> + <Group type="103" groupAlignment="0" attributes="0"> + <Group type="102" attributes="0"> + <EmptySpace type="unrelated" max="-2" attributes="0"/> + <Component id="spinSlider1" min="-2" max="-2" attributes="0"/> + </Group> <Group type="102" alignment="0" attributes="0"> - <EmptySpace min="-2" pref="21" max="-2" attributes="0"/> + <EmptySpace type="separate" min="-2" max="-2" attributes="0"/> <Component id="jLabel8" min="-2" max="-2" attributes="0"/> </Group> </Group> + <EmptySpace type="unrelated" max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jComboBox1" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jLabel9" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> @@ -1015,8 +1049,6 @@ </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"> @@ -1079,6 +1111,32 @@ <Property name="rolloverEnabled" type="boolean" value="false"/> </Properties> </Component> + <Component class="javax.swing.JLabel" name="jLabel9"> + <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.jLabel9.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + </Component> + <Component class="javax.swing.JComboBox" name="jComboBox1"> + <Properties> + <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> + <StringArray count="0"/> + </Property> + </Properties> + <AuxValues> + <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/> + </AuxValues> + </Component> + <Component class="javax.swing.JLabel" name="jLabel3"> + <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.jLabel3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + </Component> + <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider1"> + </Component> </SubComponents> </Container> <Container class="javax.swing.JPanel" name="jPanel4"> 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 bc2214834b7ef705e82c155c344b62e5fa83bbeb..475df698353347b4fbd8274a6c0cee9da3e05206 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.java @@ -4,6 +4,7 @@ import cz.fidentis.analyst.canvas.Direction; import cz.fidentis.analyst.core.ControlPanel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.Arrays; import java.util.DoubleSummaryStatistics; import javax.swing.ImageIcon; import javax.swing.JOptionPane; @@ -37,12 +38,24 @@ public class RegistrationPanel extends ControlPanel { public static final String ACTION_COMMAND_ALIGN_SYMMETRY_PLANES = "align symmetry planes"; + public static final String ACTION_COMMAND_POINT_SAMPLING_STRATEGY = "Point sampling strategy"; + /* * Configuration of panel-specific GUI elements */ public static final String STRATEGY_POINT_TO_POINT = "Point to point"; public static final String STRATEGY_POINT_TO_TRIANGLE = "Point to triangle"; + public static final String[] POINT_SAMPLING_STRATEGIES = new String[] { + "None", + "Random Sampling", + "Mean Curvature", + "Gaussian Curvature", + "Max Curvature", + "Min Curvature", + }; + + /** * Animator which animates transformations */ @@ -53,7 +66,7 @@ public class RegistrationPanel extends ControlPanel { /** * ICP undersampling. 100 = none */ - private int undersampling = 100; + private int undersamplingStrength = 50; private Vector3d manualRotation = new Vector3d(); private Vector3d manualTranslation = new Vector3d(); @@ -78,11 +91,9 @@ public class RegistrationPanel extends ControlPanel { thersholdFTF.setValue(5.0); thersholdFTF.addActionListener(createListener(action, ACTION_COMMAND_FP_CLOSENESS_THRESHOLD)); - //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(); + spinSlider1.initPercentage(this.undersamplingStrength); + spinSlider1.addSpinnerListener((ActionEvent e) -> { // update slider when the input field changed + this.undersamplingStrength = (Integer) spinSlider1.getValue(); }); jButtonInfo1.addActionListener((ActionEvent e) -> { @@ -91,6 +102,10 @@ public class RegistrationPanel extends ControlPanel { jFormattedTextField1.setValue(Double.valueOf(0.3)); jFormattedTextField2.setValue(Integer.valueOf(50)); + + Arrays.stream(POINT_SAMPLING_STRATEGIES).forEach(v -> jComboBox1.addItem(v)); + jComboBox1.setSelectedIndex(0); + jComboBox1.addActionListener(createListener(action, ACTION_COMMAND_POINT_SAMPLING_STRATEGY)); } /** @@ -127,8 +142,16 @@ public class RegistrationPanel extends ControlPanel { * Returns ICP undersampling parameter * @return ICP undersampling parameter */ - public int getIcpUndersampling() { - return undersampling; + public int getIcpUndersamplingStrength() { + return undersamplingStrength; + } + + /** + * Return selected point sampling strategy + * @return selected point sampling strategy + */ + public String getIcpUdersamplingStrategy() { + return POINT_SAMPLING_STRATEGIES[jComboBox1.getSelectedIndex()]; } /** @@ -318,11 +341,14 @@ public class RegistrationPanel extends ControlPanel { jLabel6 = new javax.swing.JLabel(); jFormattedTextField2 = new javax.swing.JFormattedTextField(); jLabel8 = new javax.swing.JLabel(); - 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(); + jLabel9 = new javax.swing.JLabel(); + jComboBox1 = new javax.swing.JComboBox<>(); + jLabel3 = new javax.swing.JLabel(); + spinSlider1 = new cz.fidentis.analyst.core.SpinSlider(); jPanel4 = new javax.swing.JPanel(); featurePointsLabel = new javax.swing.JLabel(); thersholdFTF = new javax.swing.JFormattedTextField(); @@ -820,6 +846,10 @@ public class RegistrationPanel extends ControlPanel { jButtonInfo1.setRequestFocusEnabled(false); jButtonInfo1.setRolloverEnabled(false); + org.openide.awt.Mnemonics.setLocalizedText(jLabel9, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jLabel9.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jLabel3.text")); // NOI18N + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( @@ -828,29 +858,39 @@ public class RegistrationPanel extends ControlPanel { .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(jButton1) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jButton2) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel9) + .addComponent(jLabel5) + .addComponent(jLabel6)) + .addGap(15, 15, 15)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addComponent(jLabel8) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED))) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jFormattedTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jFormattedTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jCheckBox1) + .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(jLabel3, javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .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)) + .addComponent(jButtonInfo1) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -862,19 +902,30 @@ public class RegistrationPanel extends ControlPanel { .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) + .addGap(18, 18, 18) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jCheckBox1) - .addComponent(jLabel5) + .addComponent(jLabel3)) + .addGap(18, 18, 18) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .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) + .addComponent(jLabel5)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .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) + .addComponent(jFormattedTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(21, 21, 21) - .addComponent(jLabel8)))) + .addGap(18, 18, 18) + .addComponent(jLabel8))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel9)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); 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 @@ -1020,7 +1071,7 @@ public class RegistrationPanel extends ControlPanel { .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(451, 451, 451) + .addGap(481, 481, 481) .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) @@ -1236,20 +1287,22 @@ public class RegistrationPanel extends ControlPanel { // Variables declaration - do not modify//GEN-BEGIN:variables - private cz.fidentis.analyst.core.ComboSliderInteger comboSliderInteger1; private javax.swing.JLabel featurePointsLabel; 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.JComboBox<String> jComboBox1; private javax.swing.JFormattedTextField jFormattedTextField1; private javax.swing.JFormattedTextField jFormattedTextField2; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; private javax.swing.JLabel jLabel5; private javax.swing.JLabel jLabel6; private javax.swing.JLabel jLabel8; + private javax.swing.JLabel jLabel9; private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel2; private javax.swing.JPanel jPanel4; @@ -1282,6 +1335,7 @@ public class RegistrationPanel extends ControlPanel { private javax.swing.JButton scalePlusButton; private javax.swing.ButtonGroup secondaryRenerModeGroup; private javax.swing.JPanel shiftPanel; + private cz.fidentis.analyst.core.SpinSlider spinSlider1; private javax.swing.JFormattedTextField thersholdFTF; private javax.swing.JButton thersholdUpButton; private javax.swing.JButton thresholdDownButton; diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/Drawable.java b/GUI/src/main/java/cz/fidentis/analyst/scene/Drawable.java index 3b60dee7000f46b2a9f5ffadbe1070d89c01979c..1c31da3dac59bb042871e4d5e28ba27a893445b5 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/scene/Drawable.java +++ b/GUI/src/main/java/cz/fidentis/analyst/scene/Drawable.java @@ -163,11 +163,13 @@ public abstract class Drawable { * @throws IllegalArgumentException if renderMode isn't {@code GL_FILL}, {@code GL_LINE} or {@code GL_POINT} */ public void setRenderMode(int renderMode) { + /* if (renderMode != GL2.GL_FILL && renderMode != GL2.GL_LINE && renderMode != GL2.GL_POINT) { throw new IllegalArgumentException("invalid mode"); } + */ this.renderMode = renderMode; } diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java index 8537e830702bbda21e1a727eebace42461601579..1e2ee2277eabecdb25d8dcc14db799650550626e 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java +++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java @@ -1,8 +1,14 @@ package cz.fidentis.analyst.scene; import com.jogamp.opengl.GL2; +import com.jogamp.opengl.glu.GLU; +import com.jogamp.opengl.glu.GLUquadric; import cz.fidentis.analyst.face.HumanFace; import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshPoint; +import cz.fidentis.analyst.visitors.mesh.sampling.CurvatureSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.RandomSampling; import java.awt.Color; import java.util.Collections; import java.util.HashMap; @@ -16,6 +22,14 @@ import java.util.Map; * @author Daniel Schramm */ public class DrawableFace extends DrawableMesh { + + public static final int RANDOM_SAMPLING = 100000; + public static final int CURVATURE_SAMPLING_MIN = 100001; + public static final int CURVATURE_SAMPLING_MAX = 100002; + public static final int CURVATURE_SAMPLING_MEAN = 100003; + public static final int CURVATURE_SAMPLING_GAUSSIAN = 100004; + + private PointSampling sampling = null; public static final Color SKIN_COLOR_PRIMARY = new Color(224, 172, 105); public static final Color SKIN_COLOR_SECONDARY = new Color(242, 214, 208); //new Color(236, 188, 180); @@ -130,8 +144,70 @@ public class DrawableFace extends DrawableMesh { if (isHeatmapRendered()) { new HeatmapRenderer().drawMeshModel(gl, getModel(), heatmap, heatmapSaturation, this.getTransparency()); } else { - super.renderObject(gl); + switch (getRenderMode()) { + case RANDOM_SAMPLING: + if (sampling == null || sampling.getClass() != RandomSampling.class) { + sampling = new RandomSampling(500); + getFacets().stream().forEach(facet -> sampling.visitMeshFacet(facet)); + } + break; + case CURVATURE_SAMPLING_MIN: + if (sampling == null + || sampling.getClass() != CurvatureSampling.class + || ((CurvatureSampling)sampling).getCurvatureAlg() != CurvatureSampling.CurvatureAlg.MIN) { + sampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.MIN, 500); + getFacets().stream().forEach(facet -> sampling.visitMeshFacet(facet)); + } + break; + case CURVATURE_SAMPLING_MAX: + if (sampling == null + || sampling.getClass() != CurvatureSampling.class + || ((CurvatureSampling)sampling).getCurvatureAlg() != CurvatureSampling.CurvatureAlg.MAX) { + sampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.MAX, 500); + getFacets().stream().forEach(facet -> sampling.visitMeshFacet(facet)); + } + break; + case CURVATURE_SAMPLING_MEAN: + if (sampling == null + || sampling.getClass() != CurvatureSampling.class + || ((CurvatureSampling)sampling).getCurvatureAlg() != CurvatureSampling.CurvatureAlg.MEAN) { + sampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.MEAN, 500); + getFacets().stream().forEach(facet -> sampling.visitMeshFacet(facet)); + } + break; + case CURVATURE_SAMPLING_GAUSSIAN: + if (sampling == null + || sampling.getClass() != CurvatureSampling.class + || ((CurvatureSampling)sampling).getCurvatureAlg() != CurvatureSampling.CurvatureAlg.GAUSSIAN) { + sampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.GAUSSIAN, 500); + getFacets().stream().forEach(facet -> sampling.visitMeshFacet(facet)); + } + break; + default: + super.renderObject(gl); + return; + } + + getFacets().stream().forEach(facet -> sampling.visitMeshFacet(facet)); + renderPoints(gl, sampling.getSamples()); } } + protected void renderPoints(GL2 gl, List<MeshPoint> points) { + GLU gluContext = new GLU(); + + gl.glBegin(GL2.GL_POINTS); //vertices are rendered as triangles + points.stream().forEach(mp -> { + gl.glPushMatrix(); + gl.glTranslated(mp.getX(), mp.getY(), mp.getZ()); + GLUquadric center = gluContext.gluNewQuadric(); + gluContext.gluQuadricDrawStyle(center, GLU.GLU_FILL); + gluContext.gluQuadricNormals(center, GLU.GLU_FLAT); + gluContext.gluQuadricOrientation(center, GLU.GLU_OUTSIDE); + gluContext.gluSphere(center, 0.7, 16, 16); + gluContext.gluDeleteQuadric(center); + gl.glPopMatrix(); + }); + gl.glEnd(); + } } diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePointCloud.java b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePointCloud.java new file mode 100644 index 0000000000000000000000000000000000000000..07b20c2c0142e1de7cd4d027f661519145bc5169 --- /dev/null +++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePointCloud.java @@ -0,0 +1,60 @@ +package cz.fidentis.analyst.scene; + +import com.jogamp.opengl.GL2; +import com.jogamp.opengl.glu.GLU; +import com.jogamp.opengl.glu.GLUquadric; +import cz.fidentis.analyst.mesh.core.MeshPoint; +import java.awt.Color; +import java.util.List; + +/** + * Points rendered as small spheres. + * + * @author Radek Oslejsek + */ +public class DrawablePointCloud extends Drawable { + + public static final Color DEFAULT_COLOR = Color.RED.brighter(); + public static final double FP_DEFAULT_SIZE = .7f; + + private static final GLU GLU_CONTEXT = new GLU(); + + private final List<MeshPoint> points; + + /** + * Constructor. + * + * @param featurePoints Feature points + * @param defaultColor Default color + * @param defaultPerimeter Default perimeter + */ + public DrawablePointCloud(List<MeshPoint> points) { + this.points = points; + setColor(DEFAULT_COLOR); + } + + @Override + protected void renderObject(GL2 gl) { + float[] rgba = { + getColor().getRed() / 255f, + getColor().getGreen() / 255f, + getColor().getBlue() / 255f, + getTransparency() + }; + gl.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_DIFFUSE, rgba, 0); // set default color + + gl.glBegin(GL2.GL_POINTS); //vertices are rendered as triangles + points.stream().forEach(mp -> { + gl.glPushMatrix(); + gl.glTranslated(mp.getX(), mp.getY(), mp.getZ()); + GLUquadric center = GLU_CONTEXT.gluNewQuadric(); + GLU_CONTEXT.gluQuadricDrawStyle(center, GLU.GLU_FILL); + GLU_CONTEXT.gluQuadricNormals(center, GLU.GLU_FLAT); + GLU_CONTEXT.gluQuadricOrientation(center, GLU.GLU_OUTSIDE); + GLU_CONTEXT.gluSphere(center, 0.7, 16, 16); + GLU_CONTEXT.gluDeleteQuadric(center); + gl.glPopMatrix(); + }); + gl.glEnd(); + } +} 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 3534229e31312cadf1650786c9e1870036c26f54..224e0ce48c7cb9d30468314d21ff6c0fa9badb68 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java @@ -1,8 +1,8 @@ package cz.fidentis.analyst.symmetry; import cz.fidentis.analyst.canvas.Canvas; -import cz.fidentis.analyst.core.ComboSliderDouble; import cz.fidentis.analyst.core.ControlPanelAction; +import cz.fidentis.analyst.core.DoubleSpinner; import cz.fidentis.analyst.face.HumanFace; import cz.fidentis.analyst.face.events.HumanFaceEvent; import cz.fidentis.analyst.face.events.HumanFaceListener; @@ -118,7 +118,7 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe } break; case ProfilesPanel.ACTION_SHIFT_CUTTING_PLANE: - double sliderVal = ((ComboSliderDouble) ae.getSource()).getValue(); + double sliderVal = (Double) ((DoubleSpinner) ae.getSource()).getValue(); double xExtent = maxExtentX(); double shift = (sliderVal >= 0.5) ? ( (sliderVal - 0.5) / 0.5) * xExtent // move right @@ -143,10 +143,10 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe controlPanel.setDefaultPlaneSelection(); return; } - controlPanel.resetSliderSilently(); + controlPanel.resetSlider(); computeCuttingPlanesFromSymmetry(); } else if (option.equals(ProfilesPanel.OPTION_VERTICAL_PLANE)) { - controlPanel.resetSliderSilently(); + controlPanel.resetSlider(); computeVerticalCuttingPlanes(); } @@ -167,7 +167,8 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe public void acceptEvent(HumanFaceEvent event) { // If some human face is transformed, then cutting planes have to updated. if (event instanceof HumanFaceTransformedEvent) { - controlPanel.resetSliderSilently(); + //controlPanel.resetSliderSilently(); + controlPanel.resetSlider(); if (cuttingPlaneFromSymmetry) { computeCuttingPlanesFromSymmetry(); // recompute cutting planes //getCanvas().getScene().showCuttingPlanes(false, controlPanel.isMirrorCutsChecked()); diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.form b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.form index 652258b3a0b315ab88eb05e8c1852e0c7dbcb788..d7bc5be13d3ce00508a0780adc5b49e9a4a0614c 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.form +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.form @@ -61,9 +61,9 @@ <EmptySpace type="separate" max="-2" attributes="0"/> <Component id="jButton1" min="-2" max="-2" attributes="0"/> </Group> - <Component id="comboSliderDouble1" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="spinSlider1" alignment="0" min="-2" max="-2" attributes="0"/> </Group> - <EmptySpace pref="197" max="32767" attributes="0"/> + <EmptySpace pref="174" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> @@ -71,21 +71,19 @@ <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" attributes="0"> <EmptySpace max="-2" attributes="0"/> - <Component id="comboSliderDouble1" min="-2" max="-2" attributes="0"/> + <Component id="spinSlider1" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0"> <Component id="jComboBox1" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="jCheckBox1" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/> </Group> - <EmptySpace max="32767" attributes="0"/> + <EmptySpace pref="25" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> </Layout> <SubComponents> - <Component class="cz.fidentis.analyst.core.ComboSliderDouble" name="comboSliderDouble1"> - </Component> <Component class="javax.swing.JCheckBox" name="jCheckBox1"> <Properties> <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> @@ -113,6 +111,8 @@ <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/> </AuxValues> </Component> + <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider1"> + </Component> </SubComponents> </Container> <Container class="cz.fidentis.analyst.symmetry.CurveRenderingPanel" name="polylinePanel1"> diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java index 347c46eaba2516164c99e4919de0baa7b976029d..f87eaaa3607da04334c8c9b003c667bc76031963 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java @@ -34,6 +34,8 @@ public class ProfilesPanel extends ControlPanel { public static final String ICON = "profiles28x28.png"; public static final String NAME = "Profiles"; + private final ActionListener action; + /** * Constructor for one face. * @@ -55,12 +57,13 @@ public class ProfilesPanel extends ControlPanel { setName(NAME); initComponents(); + this.action = action; + polylinePanel1.setPrimarySegments(primaryCurve); polylinePanel1.setSecondarySegments(secondaryCurve); - comboSliderDouble1.setRange(0, 1, 2); - comboSliderDouble1.setValue(0.5); - comboSliderDouble1.addListener(createListener(action, ACTION_SHIFT_CUTTING_PLANE)); + this.resetSlider(); + jCheckBox1.addActionListener(createListener(action, ACTION_MIRROR_CUTS)); jButton1.addActionListener(createListener(action, ACTION_COMMAND_EXPORT)); @@ -115,10 +118,11 @@ public class ProfilesPanel extends ControlPanel { } /** - * Resets the slider without triggering action event. + * Resets the slider into initial values */ - public void resetSliderSilently() { - comboSliderDouble1.setValueSilently(0.5); + public void resetSlider() { + spinSlider1.initDouble(0.5, 0.0, 1.0, 2); + spinSlider1.addSpinnerListener(createListener(this.action, ACTION_SHIFT_CUTTING_PLANE)); } /** @@ -138,10 +142,10 @@ public class ProfilesPanel extends ControlPanel { private void initComponents() { jPanel1 = new javax.swing.JPanel(); - comboSliderDouble1 = new cz.fidentis.analyst.core.ComboSliderDouble(); jCheckBox1 = new javax.swing.JCheckBox(); jButton1 = new javax.swing.JButton(); jComboBox1 = new javax.swing.JComboBox<>(); + spinSlider1 = new cz.fidentis.analyst.core.SpinSlider(); polylinePanel1 = new cz.fidentis.analyst.symmetry.CurveRenderingPanel(); jPanel1.setBorder(javax.swing.BorderFactory.createEtchedBorder()); @@ -168,20 +172,20 @@ public class ProfilesPanel extends ControlPanel { .addComponent(jCheckBox1) .addGap(18, 18, 18) .addComponent(jButton1)) - .addComponent(comboSliderDouble1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(197, Short.MAX_VALUE)) + .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(174, Short.MAX_VALUE)) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() - .addComponent(comboSliderDouble1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jCheckBox1) .addComponent(jButton1)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap(25, Short.MAX_VALUE)) ); polylinePanel1.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); @@ -194,7 +198,7 @@ public class ProfilesPanel extends ControlPanel { ); polylinePanel1Layout.setVerticalGroup( polylinePanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 498, Short.MAX_VALUE) + .addGap(0, 398, Short.MAX_VALUE) ); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); @@ -205,7 +209,7 @@ public class ProfilesPanel extends ControlPanel { .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(polylinePanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(polylinePanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( @@ -225,11 +229,11 @@ public class ProfilesPanel extends ControlPanel { // Variables declaration - do not modify//GEN-BEGIN:variables - private cz.fidentis.analyst.core.ComboSliderDouble comboSliderDouble1; private javax.swing.JButton jButton1; private javax.swing.JCheckBox jCheckBox1; private javax.swing.JComboBox<String> jComboBox1; private javax.swing.JPanel jPanel1; private cz.fidentis.analyst.symmetry.CurveRenderingPanel polylinePanel1; + private cz.fidentis.analyst.core.SpinSlider spinSlider1; // End of variables declaration//GEN-END:variables } 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 c6a46ba130367314824d83d8196a13326796ea20..a433f83ecf3c7de601073888109e6cb3a214dd98 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java @@ -3,6 +3,7 @@ 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.core.SpinSlider; import cz.fidentis.analyst.face.HumanFace; import cz.fidentis.analyst.face.events.HumanFaceEvent; import cz.fidentis.analyst.face.events.HumanFaceListener; @@ -11,8 +12,14 @@ import cz.fidentis.analyst.feature.FeaturePoint; import cz.fidentis.analyst.mesh.core.MeshFacet; import cz.fidentis.analyst.mesh.core.MeshModel; import cz.fidentis.analyst.scene.DrawableFace; +import cz.fidentis.analyst.scene.DrawablePointCloud; +import cz.fidentis.analyst.visitors.mesh.Curvature; import cz.fidentis.analyst.visitors.mesh.HausdorffDistance; import cz.fidentis.analyst.visitors.mesh.HausdorffDistance.Strategy; +import cz.fidentis.analyst.visitors.mesh.sampling.CurvatureSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.RandomSampling; import java.awt.event.ActionEvent; import java.util.ArrayList; @@ -33,6 +40,9 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe private final SymmetryPanel controlPanel; private final JTabbedPane topControlPanel; private int sPlane = 0; // 0 = none, 1 = from mesh, 2 = from FPs + + private int primCloudSlot = -1; + //private int secCloudSlot = -1; /** * Constructor. @@ -53,19 +63,30 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe // Place control panel to the topControlPanel this.topControlPanel.addTab(controlPanel.getName(), controlPanel.getIcon(), controlPanel); - /* this.topControlPanel.addChangeListener(e -> { // If the symmetry panel is focused... if (((JTabbedPane) e.getSource()).getSelectedComponent() instanceof SymmetryPanel) { - getCanvas().getScene().setDefaultColors(); - getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getPrimaryFaceSlot(), true); - getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getSecondaryFaceSlot(), true); - } else { - getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getPrimaryFaceSlot(), false); - getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getSecondaryFaceSlot(), false); + // show point cloud: + if (getCanvas().getSecondaryFace() == null) { // single face analysis + primCloudSlot = drawPointSamples( + getCanvas().getScene().getPrimaryFaceSlot(), + primCloudSlot, + controlPanel.getPointSamplingStrength() + ); + } + //getCanvas().getScene().setDefaultColors(); + //getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getPrimaryFaceSlot(), true); + //getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getSecondaryFaceSlot(), true); + } else { + // hide point cloud: + getScene().setOtherDrawable(primCloudSlot, null); + //getScene().setOtherDrawable(secCloudSlot, null); + primCloudSlot = -1; + //secCloudSlot = -1; + //getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getPrimaryFaceSlot(), false); + //getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getSecondaryFaceSlot(), false); } }); - */ // Be informed about changes in faces perfomed by other GUI elements getPrimaryDrawableFace().getHumanFace().registerListener(this); @@ -89,6 +110,19 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe recomputeFromFeaturePoints(getCanvas().getScene().getPrimaryFaceSlot()); recomputeFromFeaturePoints(getCanvas().getScene().getSecondaryFaceSlot()); break; + case SymmetryPanel.ACTION_COMMAND_POINT_SAMPLING_STRENGTH: + int numSamples = (Integer) ((SpinSlider) ae.getSource()).getValue(); + if (getCanvas().getSecondaryFace() == null) { // single face analysis + primCloudSlot = drawPointSamples(getCanvas().getScene().getPrimaryFaceSlot(), primCloudSlot, numSamples); + } + //secCloudSlot = drawPointSamples(getCanvas().getScene().getSecondaryFaceSlot(), secCloudSlot, numSamples); + break; + case SymmetryPanel.ACTION_COMMAND_POINT_SAMPLING_STRATEGY: + if (getCanvas().getSecondaryFace() == null) { // single face analysis + primCloudSlot = drawPointSamples(getCanvas().getScene().getPrimaryFaceSlot(), primCloudSlot, controlPanel.getPointSamplingStrength()); + } + //secCloudSlot = drawPointSamples(getCanvas().getScene().getSecondaryFaceSlot(), secCloudSlot, controlPanel.getPointSamplingStrength()); + break; default: // do nothing } @@ -121,15 +155,18 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe } } - protected void recomputeFromMesh(int index) { - HumanFace face = getCanvas().getHumanFace(index); + protected void recomputeFromMesh(int faceSlot) { + HumanFace face = getCanvas().getHumanFace(faceSlot); if (face != null) { Logger log = Logger.measureTime(); - SymmetryEstimator estimator = new SymmetryEstimator(controlPanel.getSymmetryConfig()); + SymmetryEstimator estimator = new SymmetryEstimator( + controlPanel.getSymmetryConfig(), + getSamplingStrategy(controlPanel.getPointSamplingStrength(), face.getCurvature()) + ); face.getMeshModel().compute(estimator); face.setSymmetryPlane(estimator.getSymmetryPlane()); log.printDuration("Symmetry plane estimation from mesh for " + face.getShortName()); - setDrawablePlane(face, index); + setDrawablePlane(face, faceSlot); face.announceEvent(new SymmetryPlaneChangedEvent(face, "", this)); } } @@ -208,4 +245,46 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe clone.compute(visitor); controlPanel.updateHausdorffDistanceStats(visitor.getStats(), getCanvas().getHumanFace(0) == face); } + + private int drawPointSamples(int faceSlot, int cloudSlot, int numSamples) { + HumanFace face = getCanvas().getHumanFace(faceSlot); + if (face == null) { + return -1; + } + + PointSampling sampling = getSamplingStrategy(numSamples, face.getCurvature()); + face.getMeshModel().compute(sampling); + + if (sampling.getClass() == NoSampling.class) { // don't show + if (cloudSlot != -1) { + getScene().setOtherDrawable(cloudSlot, null); + } + return -1; + } else { + if (cloudSlot == -1) { + cloudSlot = getCanvas().getScene().getFreeSlot(); + } + getScene().setOtherDrawable(cloudSlot, new DrawablePointCloud(sampling.getSamples())); + return cloudSlot; + } + } + + private PointSampling getSamplingStrategy(int numSamples, Curvature samplingCurvature) { + String st = controlPanel.getPointSamplingStrategy(); + + if (st.equals(SymmetryPanel.POINT_SAMPLING_STRATEGIES[0])) { + return new RandomSampling(numSamples); + } else if (st.equals(SymmetryPanel.POINT_SAMPLING_STRATEGIES[1])) { + return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.MEAN, numSamples); + } else if (st.equals(SymmetryPanel.POINT_SAMPLING_STRATEGIES[2])) { + return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.GAUSSIAN, numSamples); + } else if (st.equals(SymmetryPanel.POINT_SAMPLING_STRATEGIES[3])) { + return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.MAX, numSamples); + } else if (st.equals(SymmetryPanel.POINT_SAMPLING_STRATEGIES[4])) { + return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.MIN, numSamples); + } else { + return null; + } + } + } 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 4147dbe6ef263e9f35b8634dc24ec2d4df902d3c..f3c452cce00c99bc0a4717e7c4d68fd1992f3942 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.form +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.form @@ -16,16 +16,12 @@ <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> + <Group type="102" alignment="0" 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="103" groupAlignment="1" max="-2" attributes="0"> + <Component id="jPanel3" max="32767" attributes="0"/> + <Component id="jPanel1" alignment="1" max="32767" attributes="0"/> <Component id="jPanel2" alignment="0" min="-2" max="-2" attributes="0"/> - <Group type="102" alignment="0" attributes="0"> - <Component id="jComboBox1" min="-2" max="-2" attributes="0"/> - <EmptySpace type="separate" max="-2" attributes="0"/> - <Component id="jButton2" min="-2" max="-2" attributes="0"/> - </Group> </Group> <EmptySpace max="32767" attributes="0"/> </Group> @@ -35,15 +31,12 @@ <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0"> <EmptySpace max="-2" attributes="0"/> - <Component id="jPanel1" min="-2" max="-2" attributes="0"/> + <Component id="jPanel3" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jComboBox1" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> + <Component id="jPanel1" min="-2" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="jPanel2" min="-2" max="-2" attributes="0"/> - <EmptySpace pref="82" max="32767" attributes="0"/> + <EmptySpace pref="98" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> @@ -67,50 +60,48 @@ <Group type="102" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jLabel9" max="32767" attributes="0"/> <Group type="102" attributes="0"> - <Group type="103" groupAlignment="0" max="-2" attributes="0"> - <Component id="jLabel5" pref="143" max="32767" attributes="0"/> - <Component id="jLabel6" max="32767" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Group type="103" alignment="0" groupAlignment="1" max="-2" attributes="0"> + <Component id="jLabel4" max="32767" attributes="0"/> + <Component id="jLabel3" min="-2" max="-2" attributes="0"/> + </Group> + <Component id="jLabel2" alignment="0" min="-2" pref="165" max="-2" attributes="0"/> + <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jLabel5" min="-2" pref="143" max="-2" attributes="0"/> + <Component id="jLabel6" alignment="0" min="-2" pref="143" max="-2" attributes="0"/> </Group> - <EmptySpace type="separate" max="-2" attributes="0"/> - <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/> + <EmptySpace min="0" pref="12" max="32767" attributes="0"/> </Group> - <Group type="102" attributes="0"> + </Group> + <EmptySpace max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jComboBox2" min="-2" max="-2" attributes="0"/> + <Group type="102" alignment="0" attributes="0"> <Group type="103" groupAlignment="1" max="-2" attributes="0"> - <Component id="jLabel4" max="32767" attributes="0"/> - <Component id="jLabel3" max="32767" attributes="0"/> - <Component id="jLabel1" max="32767" attributes="0"/> - <Component id="jLabel2" pref="143" max="32767" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="0" 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"> - <Group type="103" alignment="0" groupAlignment="0" attributes="0"> - <Component id="comboSliderDouble1" alignment="0" min="-2" max="-2" attributes="0"/> - <Component id="comboSliderInteger1" alignment="1" min="-2" max="-2" attributes="0"/> - </Group> - <Component id="comboSliderDouble2" alignment="1" min="-2" max="-2" attributes="0"/> - </Group> - <Component id="comboSliderDouble3" alignment="1" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="0" attributes="0"> - <Component id="jButtonInfo1" min="-2" max="-2" attributes="0"/> - <Component id="jButtonInfo2" min="-2" max="-2" attributes="0"/> - <Component id="jButtonInfo4" min="-2" max="-2" attributes="0"/> - <Component id="jButtonInfo3" min="-2" max="-2" attributes="0"/> - </Group> + <Group type="102" attributes="0"> + <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/> + <EmptySpace max="32767" attributes="0"/> + <Component id="jButton1" min="-2" max="-2" attributes="0"/> </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 min="-2" pref="81" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Group type="103" alignment="0" groupAlignment="0" attributes="0"> + <Component id="spinSlider2" min="-2" max="-2" attributes="0"/> + <Component id="spinSlider3" alignment="1" min="-2" max="-2" attributes="0"/> </Group> - <Component id="comboSliderDouble4" min="-2" max="-2" attributes="0"/> + <Component id="spinSlider4" min="-2" max="-2" attributes="0"/> + <Component id="spinSlider5" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="spinSlider1" alignment="0" min="-2" max="-2" attributes="0"/> </Group> </Group> + <EmptySpace min="-2" pref="18" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jButtonInfo1" min="-2" max="-2" attributes="0"/> + <Component id="jButtonInfo2" min="-2" max="-2" attributes="0"/> + <Component id="jButtonInfo4" min="-2" max="-2" attributes="0"/> + <Component id="jButtonInfo3" min="-2" max="-2" attributes="0"/> + </Group> </Group> </Group> <EmptySpace max="32767" attributes="0"/> @@ -120,70 +111,61 @@ <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> - <EmptySpace min="-2" pref="20" max="-2" attributes="0"/> - <Component id="jLabel1" 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 max="-2" attributes="0"/> - <Component id="jButtonInfo1" min="-2" max="-2" attributes="0"/> - </Group> - </Group> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> - <EmptySpace min="-2" pref="26" max="-2" attributes="0"/> - <Component id="jLabel2" min="-2" max="-2" attributes="0"/> - </Group> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace max="-2" attributes="0"/> - <Component id="comboSliderDouble1" min="-2" max="-2" attributes="0"/> - </Group> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace min="-2" pref="17" max="-2" attributes="0"/> - <Component id="jButtonInfo2" min="-2" max="-2" attributes="0"/> - </Group> + <EmptySpace max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jLabel9" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jComboBox2" alignment="3" min="-2" max="-2" attributes="0"/> </Group> + <EmptySpace type="separate" max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> - <EmptySpace min="-2" pref="26" max="-2" attributes="0"/> - <Component id="jLabel3" min="-2" max="-2" attributes="0"/> - </Group> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace max="-2" attributes="0"/> - <Component id="comboSliderDouble2" min="-2" max="-2" attributes="0"/> - </Group> + <Component id="jButtonInfo1" min="-2" max="-2" attributes="0"/> + <Component id="spinSlider1" min="-2" max="-2" attributes="0"/> <Group type="102" alignment="0" attributes="0"> - <EmptySpace min="-2" pref="16" max="-2" attributes="0"/> - <Component id="jButtonInfo3" min="-2" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="8" max="-2" attributes="0"/> + <Component id="jLabel1" min="-2" max="-2" attributes="0"/> </Group> </Group> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0"> - <EmptySpace min="-2" pref="20" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="8" max="-2" attributes="0"/> + <Component id="jLabel2" min="-2" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="26" max="-2" attributes="0"/> + <Component id="jLabel3" min="-2" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="26" max="-2" attributes="0"/> <Component id="jLabel4" min="-2" max="-2" attributes="0"/> - <EmptySpace min="-2" pref="47" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="28" max="-2" attributes="0"/> <Component id="jLabel5" min="-2" max="-2" attributes="0"/> </Group> <Group type="102" alignment="0" attributes="0"> - <Component id="comboSliderDouble3" min="-2" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="spinSlider2" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jButtonInfo2" alignment="0" min="-2" max="-2" attributes="0"/> + </Group> <EmptySpace max="-2" attributes="0"/> - <Component id="comboSliderDouble4" min="-2" max="-2" attributes="0"/> - </Group> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace min="-2" pref="11" max="-2" attributes="0"/> - <Component id="jButtonInfo4" min="-2" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="spinSlider3" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jButtonInfo3" alignment="0" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jButtonInfo4" min="-2" max="-2" attributes="0"/> + <Component id="spinSlider4" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace max="-2" attributes="0"/> + <Component id="spinSlider5" min="-2" max="-2" attributes="0"/> </Group> </Group> <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="0" attributes="0"> <Component id="jButton1" min="-2" max="-2" attributes="0"/> + <Group type="102" alignment="0" attributes="0"> + <EmptySpace min="-2" pref="5" max="-2" attributes="0"/> + <Component id="jLabel6" min="-2" max="-2" attributes="0"/> + </Group> + <Component id="jCheckBox1" alignment="1" min="-2" max="-2" attributes="0"/> </Group> - <EmptySpace max="32767" attributes="0"/> + <EmptySpace pref="26" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> @@ -239,16 +221,6 @@ </Property> </Properties> </Component> - <Component class="cz.fidentis.analyst.core.ComboSliderInteger" name="comboSliderInteger1"> - </Component> - <Component class="cz.fidentis.analyst.core.ComboSliderDouble" name="comboSliderDouble4"> - </Component> - <Component class="cz.fidentis.analyst.core.ComboSliderDouble" name="comboSliderDouble1"> - </Component> - <Component class="cz.fidentis.analyst.core.ComboSliderDouble" name="comboSliderDouble2"> - </Component> - <Component class="cz.fidentis.analyst.core.ComboSliderDouble" name="comboSliderDouble3"> - </Component> <Component class="javax.swing.JButton" name="jButtonInfo1"> <Properties> <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> @@ -292,6 +264,9 @@ </Property> <Property name="borderPainted" type="boolean" value="false"/> </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButtonInfo3ActionPerformed"/> + </Events> </Component> <Component class="javax.swing.JButton" name="jButton1"> <Properties> @@ -300,6 +275,33 @@ </Property> </Properties> </Component> + <Component class="javax.swing.JLabel" name="jLabel9"> + <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.jLabel9.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + </Property> + </Properties> + </Component> + <Component class="javax.swing.JComboBox" name="jComboBox2"> + <Properties> + <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> + <StringArray count="0"/> + </Property> + </Properties> + <AuxValues> + <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/> + </AuxValues> + </Component> + <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider1"> + </Component> + <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider2"> + </Component> + <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider3"> + </Component> + <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider4"> + </Component> + <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider5"> + </Component> </SubComponents> </Container> <Container class="javax.swing.JPanel" name="jPanel2"> @@ -376,25 +378,68 @@ </Component> </SubComponents> </Container> - <Component class="javax.swing.JComboBox" name="jComboBox1"> - <Properties> - <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> - <StringArray count="0"/> - </Property> - </Properties> - <AuxValues> - <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/> - </AuxValues> - </Component> - <Component class="javax.swing.JButton" name="jButton2"> + <Container class="javax.swing.JPanel" name="jPanel3"> <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 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="Compute from"> + <ResourceString PropertyName="titleX" bundle="cz/fidentis/analyst/symmetry/Bundle.properties" key="SymmetryPanel.jPanel3.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> + <Font PropertyName="font" name="Dialog" size="12" style="1"/> + </TitledBorder> + </Border> </Property> </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/> - </Events> - </Component> + + <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="jComboBox1" min="-2" max="-2" attributes="0"/> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Component id="jButton2" 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="3" attributes="0"> + <Component id="jComboBox1" 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"/> + </Group> + </Group> + </DimensionLayout> + </Layout> + <SubComponents> + <Component class="javax.swing.JComboBox" name="jComboBox1"> + <Properties> + <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> + <StringArray count="0"/> + </Property> + </Properties> + <AuxValues> + <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/> + </AuxValues> + </Component> + <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/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> </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 683ebf52cdedce7dd0ac0d96582860537ae8e700..ea8901e9c62c9db801de1ff3e227c827a82eb08c 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java @@ -3,6 +3,7 @@ package cz.fidentis.analyst.symmetry; import cz.fidentis.analyst.core.ControlPanel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.Arrays; import java.util.DoubleSummaryStatistics; import javax.swing.ImageIcon; import javax.swing.JOptionPane; @@ -18,8 +19,18 @@ public class SymmetryPanel extends ControlPanel { /* * Handled actions */ - public static final String ACTION_COMMAND_RECOMPUTE_FROM_MESH = "From triangular mesh"; - public static final String ACTION_COMMAND_COMPUTE_FROM_FPS = "From feature points"; + public static final String ACTION_COMMAND_RECOMPUTE_FROM_MESH = "Mesh vertices"; + public static final String ACTION_COMMAND_COMPUTE_FROM_FPS = "Feature points"; + public static final String ACTION_COMMAND_POINT_SAMPLING_STRENGTH = "Point sampling strength"; + public static final String ACTION_COMMAND_POINT_SAMPLING_STRATEGY = "Point sampling strategy"; + + public static final String[] POINT_SAMPLING_STRATEGIES = new String[] { + "Random Sampling", + "Mean Curvature", + "Gaussian Curvature", + "Max Curvature", + "Min Curvature", + }; /* * Mandatory design elements @@ -44,95 +55,73 @@ public class SymmetryPanel extends ControlPanel { this.setName(NAME); initComponents(); - comboSliderInteger1.setRange(0, MAX_SIGNIFICANT_POINTS); - comboSliderInteger1.setValue(config.getSignificantPointCount()); - comboSliderInteger1.setSliderEast(); - comboSliderInteger1.addInputFieldListener( - (ActionEvent e) -> { - config.setSignificantPointCount(comboSliderInteger1.getValue()); - } - ); + Arrays.stream(POINT_SAMPLING_STRATEGIES).forEach(v -> jComboBox2.addItem(v)); + jComboBox2.setSelectedIndex(0); + jComboBox2.addActionListener(createListener(action, ACTION_COMMAND_POINT_SAMPLING_STRATEGY)); - comboSliderDouble1.setRange(0, 1, 3); - comboSliderDouble1.setSliderEast(); - comboSliderDouble1.setValue(config.getMinCurvRatio()); - comboSliderDouble1.addInputFieldListener( - (ActionEvent e) -> { - config.setMinCurvRatio(comboSliderDouble1.getValue()); - } - ); + spinSlider1.initInteger(config.getSignificantPointCount(), 10, MAX_SIGNIFICANT_POINTS, 1); + spinSlider1.addSpinnerListener((ActionEvent e) -> { + config.setSignificantPointCount((Integer) spinSlider1.getValue()); + action.actionPerformed(new ActionEvent( + spinSlider1, + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_POINT_SAMPLING_STRENGTH) + ); + }); - comboSliderDouble2.setRange(0, 1, 3); - comboSliderDouble2.setSliderEast(); - comboSliderDouble2.setValue(config.getMinAngleCos()); - comboSliderDouble2.addInputFieldListener( - (ActionEvent e) -> { - config.setMinAngleCos(comboSliderDouble2.getValue()); - } - ); + spinSlider2.initDouble(config.getMinCurvRatio(), 0.0, 1.0, 3); + spinSlider2.addSpinnerListener((ActionEvent e) -> { + config.setMinCurvRatio((Double) spinSlider2.getValue()); + }); - comboSliderDouble3.setRange(0, 1, 3); - comboSliderDouble3.setSliderEast(); - comboSliderDouble3.setValue(config.getMinNormAngleCos()); - comboSliderDouble3.addInputFieldListener( - (ActionEvent e) -> { - config.setMinNormAngleCos(comboSliderDouble3.getValue()); - } - ); + spinSlider3.initDouble(config.getMinAngleCos(), 0.0, 1.0, 3); + spinSlider3.addSpinnerListener((ActionEvent e) -> { + config.setMinAngleCos((Double) spinSlider3.getValue()); + }); - comboSliderDouble4.setRange(0, 1, 2); - comboSliderDouble4.setSliderEast(); - comboSliderDouble4.setValue(config.getMaxRelDistance()); - comboSliderDouble4.addInputFieldListener( - (ActionEvent e) -> { - config.setMaxRelDistance(comboSliderDouble4.getValue()); - } - ); + spinSlider4.initDouble(config.getMinNormAngleCos(), 0.0, 1.0, 3); + spinSlider4.addSpinnerListener((ActionEvent e) -> { + config.setMinNormAngleCos((Double) spinSlider4.getValue()); + }); - jButton1.addActionListener( - (ActionEvent e) -> { - config = new SymmetryConfig(); - comboSliderInteger1.setValue(config.getSignificantPointCount()); - comboSliderDouble1.setValue(config.getMinCurvRatio()); - comboSliderDouble2.setValue(config.getMinAngleCos()); - comboSliderDouble3.setValue(config.getMinNormAngleCos()); - comboSliderDouble4.setValue(config.getMaxRelDistance()); - //setConfig(new SymmetryConfig()); - } - ); + spinSlider5.initDouble(config.getMaxRelDistance(), 0.0, 1.0, 2); + spinSlider5.addSpinnerListener((ActionEvent e) -> { + config.setMaxRelDistance((Double) spinSlider5.getValue()); + }); + + jButton1.addActionListener((ActionEvent e) -> { + config = new SymmetryConfig(); + spinSlider1.setValue(config.getSignificantPointCount()); + spinSlider2.setValue(config.getMinCurvRatio()); + spinSlider3.setValue(config.getMinAngleCos()); + spinSlider4.setValue(config.getMinNormAngleCos()); + spinSlider5.setValue(config.getMaxRelDistance()); + //setConfig(new SymmetryConfig()); + }); jButton2.addActionListener((ActionEvent e) -> { - action.actionPerformed(new ActionEvent( // recompute - e.getSource(), - ActionEvent.ACTION_PERFORMED, - jComboBox1.getSelectedItem().toString() - )); - } - ); + action.actionPerformed(new ActionEvent( // recompute + e.getSource(), + ActionEvent.ACTION_PERFORMED, + jComboBox1.getSelectedItem().toString() + )); + }); - jButtonInfo1.addActionListener( - (ActionEvent e) -> { - this.showSignPointsHelp(); - } - ); + jButtonInfo1.addActionListener((ActionEvent e) -> { + this.showSignPointsHelp(); + }); - jButtonInfo2.addActionListener( - (ActionEvent e) -> { - this.showMinCurvHelp(); - } - ); + jButtonInfo2.addActionListener((ActionEvent e) -> { + this.showMinCurvHelp(); + }); - jButtonInfo3.addActionListener( - (ActionEvent e) -> { - this.showMinAngleCosHelp(); - } - ); + jButtonInfo3.addActionListener((ActionEvent e) -> { + this.showMinAngleCosHelp(); + }); - jButtonInfo4.addActionListener( - (ActionEvent e) -> { - this.showNormalAngleHelp(); - } - ); + jButtonInfo4.addActionListener((ActionEvent e) -> { + this.showNormalAngleHelp(); + }); jComboBox1.addItem(SymmetryPanel.ACTION_COMMAND_RECOMPUTE_FROM_MESH); //jComboBox1.addItem(SymmetryPanel.ACTION_COMMAND_COMPUTE_FROM_FPS); @@ -144,6 +133,22 @@ public class SymmetryPanel extends ControlPanel { return getStaticIcon(); } + /** + * Return the number of point samples + * @return the number of point samples + */ + public int getPointSamplingStrength() { + return (Integer) spinSlider1.getValue(); + } + + /** + * Return selected point sampling strategy + * @return selected point sampling strategy + */ + public String getPointSamplingStrategy() { + return POINT_SAMPLING_STRATEGIES[jComboBox2.getSelectedIndex()]; + } + /** * Returns symmetry plane configuration * @return symmetry plane configuration @@ -273,21 +278,24 @@ public class SymmetryPanel extends ControlPanel { jLabel5 = new javax.swing.JLabel(); jLabel6 = new javax.swing.JLabel(); jCheckBox1 = new javax.swing.JCheckBox(); - comboSliderInteger1 = new cz.fidentis.analyst.core.ComboSliderInteger(); - comboSliderDouble4 = new cz.fidentis.analyst.core.ComboSliderDouble(); - comboSliderDouble1 = new cz.fidentis.analyst.core.ComboSliderDouble(); - comboSliderDouble2 = new cz.fidentis.analyst.core.ComboSliderDouble(); - comboSliderDouble3 = new cz.fidentis.analyst.core.ComboSliderDouble(); jButtonInfo1 = new javax.swing.JButton(); jButtonInfo2 = new javax.swing.JButton(); jButtonInfo4 = new javax.swing.JButton(); jButtonInfo3 = new javax.swing.JButton(); jButton1 = new javax.swing.JButton(); + jLabel9 = new javax.swing.JLabel(); + jComboBox2 = new javax.swing.JComboBox<>(); + spinSlider1 = new cz.fidentis.analyst.core.SpinSlider(); + spinSlider2 = new cz.fidentis.analyst.core.SpinSlider(); + spinSlider3 = new cz.fidentis.analyst.core.SpinSlider(); + spinSlider4 = new cz.fidentis.analyst.core.SpinSlider(); + spinSlider5 = new cz.fidentis.analyst.core.SpinSlider(); jPanel2 = new javax.swing.JPanel(); jTextField1 = new javax.swing.JTextField(); jLabel7 = new javax.swing.JLabel(); jLabel8 = new javax.swing.JLabel(); jTextField2 = new javax.swing.JTextField(); + jPanel3 = new javax.swing.JPanel(); jComboBox1 = new javax.swing.JComboBox<>(); jButton2 = new javax.swing.JButton(); @@ -323,9 +331,16 @@ public class SymmetryPanel extends ControlPanel { jButtonInfo3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/info.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jButtonInfo3, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jButtonInfo3.text")); // NOI18N jButtonInfo3.setBorderPainted(false); + jButtonInfo3.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButtonInfo3ActionPerformed(evt); + } + }); org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jButton1.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jLabel9, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jLabel9.text")); // NOI18N + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( @@ -333,92 +348,88 @@ public class SymmetryPanel extends ControlPanel { .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel9, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(jPanel1Layout.createSequentialGroup() - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(jLabel5, javax.swing.GroupLayout.DEFAULT_SIZE, 143, Short.MAX_VALUE) - .addComponent(jLabel6, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGap(18, 18, 18) - .addComponent(jCheckBox1)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(jLabel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jLabel3)) + .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 165, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel1) + .addComponent(jLabel5, javax.swing.GroupLayout.PREFERRED_SIZE, 143, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, 143, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(0, 12, Short.MAX_VALUE))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jComboBox2, 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.TRAILING, false) - .addComponent(jLabel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jLabel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .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) .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(jCheckBox1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jButton1)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(comboSliderDouble1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(comboSliderInteger1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(comboSliderDouble2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(comboSliderDouble3, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jButtonInfo1) - .addComponent(jButtonInfo2) - .addComponent(jButtonInfo4) - .addComponent(jButtonInfo3))) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(jButton1) - .addGap(81, 81, 81)) - .addComponent(comboSliderDouble4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))) + .addComponent(spinSlider2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(spinSlider3, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(spinSlider4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(spinSlider5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGap(18, 18, 18) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jButtonInfo1) + .addComponent(jButtonInfo2) + .addComponent(jButtonInfo4) + .addComponent(jButtonInfo3)))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel9) + .addComponent(jComboBox2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jButtonInfo1) + .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(20, 20, 20) - .addComponent(jLabel1)) - .addComponent(comboSliderInteger1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(jPanel1Layout.createSequentialGroup() - .addContainerGap() - .addComponent(jButtonInfo1))) + .addGap(8, 8, 8) + .addComponent(jLabel1))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(8, 8, 8) + .addComponent(jLabel2) .addGap(26, 26, 26) - .addComponent(jLabel2)) - .addGroup(jPanel1Layout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(comboSliderDouble1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(17, 17, 17) - .addComponent(jButtonInfo2))) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(jLabel3) .addGap(26, 26, 26) - .addComponent(jLabel3)) - .addGroup(jPanel1Layout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(comboSliderDouble2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(16, 16, 16) - .addComponent(jButtonInfo3))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(20, 20, 20) .addComponent(jLabel4) - .addGap(47, 47, 47) + .addGap(28, 28, 28) .addComponent(jLabel5)) .addGroup(jPanel1Layout.createSequentialGroup() - .addComponent(comboSliderDouble3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(spinSlider2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jButtonInfo2)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(comboSliderDouble4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(jPanel1Layout.createSequentialGroup() - .addGap(11, 11, 11) - .addComponent(jButtonInfo4))) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(spinSlider3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jButtonInfo3)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jButtonInfo4) + .addComponent(spinSlider4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(spinSlider5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addGap(24, 24, 24) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(jLabel6) - .addComponent(jCheckBox1) - .addComponent(jButton1)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jButton1) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(jLabel6)) + .addComponent(jCheckBox1, javax.swing.GroupLayout.Alignment.TRAILING)) + .addContainerGap(26, Short.MAX_VALUE)) ); jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jPanel2.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 12))); // NOI18N @@ -458,6 +469,9 @@ public class SymmetryPanel extends ControlPanel { .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); + jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jPanel3.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 12))); // NOI18N + + jButton2.setFont(new java.awt.Font("Ubuntu", 1, 15)); // 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) { @@ -465,33 +479,49 @@ public class SymmetryPanel extends ControlPanel { } }); + javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3); + jPanel3.setLayout(jPanel3Layout); + jPanel3Layout.setHorizontalGroup( + jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel3Layout.createSequentialGroup() + .addContainerGap() + .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addComponent(jButton2) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + jPanel3Layout.setVerticalGroup( + jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel3Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jButton2)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .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) - .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createSequentialGroup() - .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(18, 18, 18) - .addComponent(jButton2))) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jPanel2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jPanel3, 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(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jButton2)) + .addComponent(jPanel1, 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) - .addContainerGap(82, Short.MAX_VALUE)) + .addContainerGap(98, Short.MAX_VALUE)) ); }// </editor-fold>//GEN-END:initComponents @@ -499,13 +529,12 @@ public class SymmetryPanel extends ControlPanel { // TODO add your handling code here: }//GEN-LAST:event_jButton2ActionPerformed + private void jButtonInfo3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonInfo3ActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_jButtonInfo3ActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables - private cz.fidentis.analyst.core.ComboSliderDouble comboSliderDouble1; - private cz.fidentis.analyst.core.ComboSliderDouble comboSliderDouble2; - private cz.fidentis.analyst.core.ComboSliderDouble comboSliderDouble3; - private cz.fidentis.analyst.core.ComboSliderDouble comboSliderDouble4; - private cz.fidentis.analyst.core.ComboSliderInteger comboSliderInteger1; private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; private javax.swing.JButton jButtonInfo1; @@ -514,6 +543,7 @@ public class SymmetryPanel extends ControlPanel { private javax.swing.JButton jButtonInfo4; private javax.swing.JCheckBox jCheckBox1; private javax.swing.JComboBox<String> jComboBox1; + private javax.swing.JComboBox<String> jComboBox2; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JLabel jLabel3; @@ -522,9 +552,16 @@ public class SymmetryPanel extends ControlPanel { private javax.swing.JLabel jLabel6; private javax.swing.JLabel jLabel7; private javax.swing.JLabel jLabel8; + private javax.swing.JLabel jLabel9; private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel2; + private javax.swing.JPanel jPanel3; private javax.swing.JTextField jTextField1; private javax.swing.JTextField jTextField2; + private cz.fidentis.analyst.core.SpinSlider spinSlider1; + private cz.fidentis.analyst.core.SpinSlider spinSlider2; + private cz.fidentis.analyst.core.SpinSlider spinSlider3; + private cz.fidentis.analyst.core.SpinSlider spinSlider4; + private cz.fidentis.analyst.core.SpinSlider spinSlider5; // End of variables declaration//GEN-END:variables } diff --git a/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruth.java b/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruth.java index f8dd484f18afa4150e29d19b38f4e45efe3f5376..3945cf584e53ed748528a8b4f8f2b8af7ae03122 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruth.java +++ b/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruth.java @@ -3,6 +3,7 @@ package cz.fidentis.analyst.tests; import cz.fidentis.analyst.face.HumanFace; import cz.fidentis.analyst.face.HumanFaceUtils; import cz.fidentis.analyst.visitors.mesh.HausdorffDistance; +import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; @@ -81,7 +82,7 @@ public class BatchSimilarityGroundTruth { 100, // max iterations false,// scale 0.3, // error - 100, // no undersampling + new NoSampling(), // no undersampling false // drop k-d tree, if exists ); icpComputationTime += System.currentTimeMillis() - icpTime; diff --git a/GUI/src/main/resources/cz/fidentis/analyst/batch/Bundle.properties b/GUI/src/main/resources/cz/fidentis/analyst/batch/Bundle.properties index 4c560d531bf6448cfb43b5aa05ad6c22751822d3..1d136de071aad207a972bc9db6984fb79eca211c 100644 --- a/GUI/src/main/resources/cz/fidentis/analyst/batch/Bundle.properties +++ b/GUI/src/main/resources/cz/fidentis/analyst/batch/Bundle.properties @@ -8,7 +8,7 @@ BatchPanel.jCheckBox4.text=ICP scaling BatchPanel.jCheckBox3.text=Show ICP transformations in the 3D preview BatchPanel.jCheckBox2.text=Transform faces towards the selected one using ICP BatchPanel.jCheckBox1.text_1=Compute an average face from the selected one -BatchPanel.jLabel2.text=ICP undersampling (100% = none): +BatchPanel.jLabel2.text=ICP undersampling : BatchPanel.jButton1.text=Compute BatchPanel.jButtonInfo1.text= BatchPanel.jButton6.text=Export results diff --git a/GUI/src/main/resources/cz/fidentis/analyst/core/Bundle.properties b/GUI/src/main/resources/cz/fidentis/analyst/core/Bundle.properties index fb98808fdf280afd4593d85f9b9ffe4464aa6b4f..900e53446e3aececf9cf2f808764db76da1c6f24 100644 --- a/GUI/src/main/resources/cz/fidentis/analyst/core/Bundle.properties +++ b/GUI/src/main/resources/cz/fidentis/analyst/core/Bundle.properties @@ -41,3 +41,4 @@ ProjectTopComp.selectAllButton.text=Select all ProjectTopComp.deselectAllButton.text=Deselect all ProjectTopComp.inflateButton.text=Inflate ProjectTopComp.oneOnOneButton.text=Open 1:1 +TestPanel.jFormattedTextField1.text=jFormattedTextField1 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 4d2dcd52e7983cb9e610f330eb44122e2075696f..15a33f9b70ec84ac7586897bc4bc97b6c06bdb79 100644 --- a/GUI/src/main/resources/cz/fidentis/analyst/registration/Bundle.properties +++ b/GUI/src/main/resources/cz/fidentis/analyst/registration/Bundle.properties @@ -1,10 +1,10 @@ RegistrationPanel.jButton1.text=Mesh -RegistrationPanel.jCheckBox1.text=scale +RegistrationPanel.jCheckBox1.text= RegistrationPanel.jFormattedTextField1.text=0,3 -RegistrationPanel.jLabel5.text=min ICP error: +RegistrationPanel.jLabel5.text=ICP min error: RegistrationPanel.jFormattedTextField2.text=50 RegistrationPanel.jLabel6.text=ICP iterations: -RegistrationPanel.jLabel8.text=ICP undersampling (100% = none) +RegistrationPanel.jLabel8.text=ICP downsampling strength RegistrationPanel.thersholdUpButton.text= RegistrationPanel.thresholdDownButton.text= RegistrationPanel.featurePointsLabel.text=Highlight feature point pairs closer than: @@ -51,3 +51,5 @@ RegistrationPanel.rightTranslationXButton.text= RegistrationPanel.translationPanel.border.title=translation RegistrationPanel.transformationPanel.border.title=Manual alignment: RegistrationPanel.jButtonInfo1.text= +RegistrationPanel.jLabel9.text=ICP downsampling strategy +RegistrationPanel.jLabel3.text=scale: diff --git a/GUI/src/main/resources/cz/fidentis/analyst/sampling/Bundle.properties b/GUI/src/main/resources/cz/fidentis/analyst/sampling/Bundle.properties new file mode 100644 index 0000000000000000000000000000000000000000..c32a418c046144327c90fac5d0694357377356a7 --- /dev/null +++ b/GUI/src/main/resources/cz/fidentis/analyst/sampling/Bundle.properties @@ -0,0 +1,3 @@ +PointSamplingPanel.jLabel1.text=Strength: +PointSamplingPanel.jLabel2.text=Strategy: +PointSamplingPanel.jLabel3.text=100% = no downsampling 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 b0657e6a629bdbafa58121bc1f1165cb4e5abe66..24d640400e81ce289086aee02e25532dc4b38c59 100644 --- a/GUI/src/main/resources/cz/fidentis/analyst/symmetry/Bundle.properties +++ b/GUI/src/main/resources/cz/fidentis/analyst/symmetry/Bundle.properties @@ -20,20 +20,7 @@ SymmetryPanelNew.minAngleCosTF.text= SymmetryPanelNew.minNormAngleCosTF.text= SymmetryPanelNew.maxRelDistTF.text= SymmetryPanelNew.averaging.text= -SymmetryPanel.jLabel4.text_1=Normal angle -SymmetryPanel.jButtonInfo2.text= -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=Symmetry from mesh SymmetryPanel.jButton2.text=Compute -SymmetryPanel.jButton1.text=Reset to defaults -SymmetryPanel.jCheckBox1.text= -SymmetryPanel.jLabel6.text_1=Averaging -SymmetryPanel.jButtonInfo3.text= -SymmetryPanel.jLabel5.text_1=Relative distance -SymmetryPanel.jButtonInfo4.text= ProfilesPanel.jCheckBox1.text=Mirror cuts ProfilesPanel.jButton1.text=Export SymmetryPanel.jPanel2.border.title=Precision (0 = best fit): @@ -41,3 +28,18 @@ SymmetryPanel.jTextField1.text= SymmetryPanel.jLabel7.text=Face 1: SymmetryPanel.jLabel8.text=Face 2: SymmetryPanel.jTextField2.text= +SymmetryPanel.jPanel3.border.title=Compute from +SymmetryPanel.jLabel9.text=Point sampling strategy +SymmetryPanel.jButton1.text=Reset to defaults +SymmetryPanel.jButtonInfo3.text= +SymmetryPanel.jButtonInfo4.text= +SymmetryPanel.jButtonInfo2.text= +SymmetryPanel.jButtonInfo1.text= +SymmetryPanel.jCheckBox1.text= +SymmetryPanel.jLabel6.text_1=Averaging +SymmetryPanel.jLabel5.text_1=Relative distance +SymmetryPanel.jLabel4.text_1=Normal angle +SymmetryPanel.jLabel3.text_1=Min. angle cosine +SymmetryPanel.jLabel2.text_1=Min. curvature ratio +SymmetryPanel.jLabel1.text_1=Point sampling strength +SymmetryPanel.jPanel1.border.title_1=Symmetry from mesh