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 b11761b08dea8c7b55fe7a5baedff09ecaed1fd4..022861ed796af59bdf5d128b0b3d823f846c6178 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java @@ -284,9 +284,11 @@ public class IcpTransformer extends MeshVisitor { transformation = computeIcpTransformation(nearestPoints, distances, reducedFacet.getVertices()); transformations.get(transformedFacet).add(transformation); applyTransformation(transformedFacet, transformation); - if (!samplingStrategy.isBackedByOrigMesh()) { // transform samples as well + + if (!samplingStrategy.isBackedByOrigMesh()) { // samples have to be transfomed as well applyTransformation(reducedFacet, transformation); } + currentIteration ++; } } 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 index e248620eab2ae7181cbbcb575dc76ed18d02cd1c..45e399b3e3697d68e11b48244dd203596b231179 100644 --- 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 @@ -29,7 +29,7 @@ public class NoSampling extends PointSampling { @Override public String toString() { - return "none"; + return "no downsampling = " + this.getNumDownsampledPoints(allVertices.size()) + " real points"; } 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 index 8199fc634fb87023a56047fbe878299ede1a84ec..12202b757e2b2eef05e13e772aa2eaa94a28a89d 100644 --- 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 @@ -32,8 +32,8 @@ public abstract class PointSampling extends MeshVisitor { * Constructor for no point sampling. */ public PointSampling() { - this.samplingType = null; - this.samplingLimit = 0.0; + this.samplingType = PointSamplingType.PERCENTAGE; + this.samplingLimit = 1.0; } /** @@ -70,7 +70,7 @@ public abstract class PointSampling extends MeshVisitor { /** * If {@code true}, then the returned points samples are points * from the original mesh. Therefore, any change changes the original mesh! - * If {@code}, then new points are returned. + * If {@code false}, then new points are returned that are independent on the original mesh. * * @return {@code true} if the point samples include points of the original mesh */ @@ -78,6 +78,21 @@ public abstract class PointSampling extends MeshVisitor { return true; } + /** + * If {@code true}, then the returned points samples are stored a space-ordering + * structure. Therefore, their 3D transformation requires to re-arrange the + * ordering structure (i.e., to reg-generate samples by calling {@link #getSamples()} + * again. + * + * from the original mesh. Therefore, any change changes the original mesh! + * If {@code}, then new points are returned. + * + * @return {@code true} if the point samples include points of the original mesh + */ + public boolean usesSpaceOrdering() { + return false; + } + /** * Changes the number of required samples. * @@ -107,9 +122,9 @@ public abstract class PointSampling extends MeshVisitor { @Override public String toString() { if (this.samplingType == PointSamplingType.PERCENTAGE) { - return "sampling to " + (int) (samplingLimit * 100) + "%"; + return "downsampling to " + (int) (samplingLimit * 100) + "%"; } else { - return "sampling of " + (int) samplingLimit + " points"; + return "downsampling to " + (int) samplingLimit + " points"; } } @@ -132,4 +147,11 @@ public abstract class PointSampling extends MeshVisitor { } } + protected PointSamplingType getSamplingType() { + return this.samplingType; + } + + protected double getSamplingLimit() { + return this.samplingLimit; + } } 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 index 67d75c4455ca59f021f614d6ae91fcb5841fb4dc..31ccf49a0d5b99b0a36f1558b7ed93e36d2900d4 100644 --- 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 @@ -97,7 +97,7 @@ public class RandomSampling extends PointSampling { @Override public String toString() { - return "random " + super.toString(); + return "random " + super.toString() + ". Current state: " + this.getNumDownsampledPoints(allVertices.size()) + " samples"; } } diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/UniformSpaceSampling.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/UniformSpaceSampling.java index 95facf9c2689580bbdda6d27d8376dd4891ff598..c4cbcfd464166789518b75578f04114cfd6a4b79 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/UniformSpaceSampling.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/UniformSpaceSampling.java @@ -6,7 +6,6 @@ import cz.fidentis.analyst.mesh.core.MeshPoint; import cz.fidentis.analyst.mesh.core.MeshPointImpl; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import javax.vecmath.Point3d; @@ -60,6 +59,11 @@ public class UniformSpaceSampling extends PointSampling { public boolean isBackedByOrigMesh() { return false; } + + @Override + public boolean usesSpaceOrdering() { + return true; + } @Override public void visitMeshFacet(MeshFacet facet) { @@ -70,18 +74,54 @@ public class UniformSpaceSampling extends PointSampling { @Override public List<MeshPoint> getSamples() { - int numReducedVertices = getNumDownsampledPoints(allVertices.size()); - if (allVertices.size() == numReducedVertices) { - return Collections.unmodifiableList(allVertices); + // no downsampling: + if (allVertices.size() == getNumDownsampledPoints(allVertices.size())) { + // this sampling method is NOT backed by the original mesh. Therefore, meke a copy. + return allVertices.stream() + .map(p -> new MeshPointImpl(p)) + .collect(Collectors.toList()); } - MeshPoint centroid = new MeshPointImpl(allVertices); - double avgDist = getAvgDist(allVertices, centroid.getPosition()); - + // compute controid of cell's points: + return createGrid().getNonEmptyCells().stream() + .map(list -> new MeshPointImpl(list)) + .collect(Collectors.toList()); + } + + @Override + public String toString() { + return "uniform space " + super.toString(); + } + + protected List<MeshPoint> getOrigPoints() { + return this.allVertices; + } + + /** + * Computes the average distance between vertices and their centroid. + * + * @param vertices vertices + * @param centroid their centroid + * @return the average distance between vertices and their centroid. + */ + protected double getAvgDist(Collection<MeshPoint> vertices, Point3d centroid) { + return vertices.stream() + .map(p -> p.getPosition()) + .mapToDouble(p -> centroid.distance(p)) + .average() + .orElse(0); + } + + protected UniformGrid3d<MeshPoint> createGrid() { + int numReducedVertices = getNumDownsampledPoints(allVertices.size()); double k = CELL_RESIZE_INIT_VALUE; double step = CELL_RESIZE_INIT_STEP; int numCells = 0; UniformGrid3d<MeshPoint> grid; + + MeshPoint centroid = new MeshPointImpl(allVertices); + double avgDist = getAvgDist(allVertices, centroid.getPosition()); + int counter = 0; do { double cellSize = avgDist / k; @@ -100,28 +140,6 @@ public class UniformSpaceSampling extends PointSampling { k += step; } while (numCells < numReducedVertices || numCells > numReducedVertices + DOWNSAMPLING_TOLERANCE); - return grid.getNonEmptyCells().stream() - .map(list -> new MeshPointImpl(list)) // compute controid of cell's points - .collect(Collectors.toList()); - } - - @Override - public String toString() { - return "uniform space " + super.toString(); - } - - /** - * Computes the average distance between vertices and their centroid. - * - * @param vertices vertices - * @param centroid their centroid - * @return the average distance between vertices and their centroid. - */ - protected double getAvgDist(Collection<MeshPoint> vertices, Point3d centroid) { - return vertices.stream() - .map(p -> p.getPosition()) - .mapToDouble(p -> centroid.distance(p)) - .average() - .orElse(0); + return grid; } } \ No newline at end of file diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/UniformSurfaceSampling.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/UniformSurfaceSampling.java new file mode 100644 index 0000000000000000000000000000000000000000..ae87803083413a907fa3bdcc03c1307b781c7427 --- /dev/null +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/UniformSurfaceSampling.java @@ -0,0 +1,72 @@ +package cz.fidentis.analyst.visitors.mesh.sampling; + +import cz.fidentis.analyst.mesh.core.MeshPoint; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +/** + * Similar downsampling strategy to the {@link UniformSpaceSampling}. + * However, a random vertex from the mesh is selected from each non-empty cell instead + * of computing a new average point. Therefore, the samples lie on the mesh surface. + * + * @author Radek Oslejsek + */ +public class UniformSurfaceSampling extends UniformSpaceSampling { + + /** + * Constructor for no point sampling (100 %). + */ + public UniformSurfaceSampling() { + this(1.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 UniformSurfaceSampling(double perc) { + super(perc); + } + + /** + * Constructor for MAX_VERTICES point sampling type. + * + * @param max Required number of samples. Must be bigger than zero + * @throws IllegalArgumentException if the input parameter is wrong + */ + public UniformSurfaceSampling(int max) { + super(max); + } + + @Override + public boolean isBackedByOrigMesh() { + return true; + } + + @Override + public List<MeshPoint> getSamples() { + // no downsampling: + if (getOrigPoints().size() == getNumDownsampledPoints(getOrigPoints().size())) { + return Collections.unmodifiableList(getOrigPoints()); + } + + // Get a random vertex of the cell: + Random rand = new Random(); + return createGrid().getNonEmptyCells().stream() + .map(list -> list.get(rand.nextInt(list.size()))) + .collect(Collectors.toList()); + } + + @Override + public String toString() { + if (getSamplingType() == PointSamplingType.PERCENTAGE) { + return "uniform surface downsampling to " + (int) (getSamplingLimit() * 100) + "%"; + } else { + return "uniform surface downsampling to " + (int) getSamplingLimit() + " points"; + } + } +} diff --git a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchAction.java b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchAction.java index a7d3211c6e2a1e25c4e9f79b718a1a87f492da74..2358895bf7f0abd6682d483496cc1bc452776dba 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchAction.java @@ -119,7 +119,7 @@ public class BatchAction extends ControlPanelAction implements HumanFaceListener HumanFace selectedFace = controlPanel.getSelectedFace(); if (controlPanel.showIcpPreview() && selectedFace != null) { getScene().clearScene(); // scene is recovered automatically - see the event handler above - faceSceneSlot = getScene().getFreeSlot(); + faceSceneSlot = getScene().getFreeSlotForFace(); // Add inital face to the scene: getScene().setDrawableFace(faceSceneSlot, selectedFace); @@ -168,7 +168,7 @@ public class BatchAction extends ControlPanelAction implements HumanFaceListener HumanFace face = controlPanel.getSelectedFace(); if (face != null) { if (faceSceneSlot == -1) { - faceSceneSlot = getScene().getFreeSlot(); + faceSceneSlot = getScene().getFreeSlotForFace(); } getScene().setDrawableFace(faceSceneSlot, face); getScene().setFaceAsPrimary(faceSceneSlot); 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 a3f2757eb1431749c6705183e983d9c8032c80f1..d6d0cdf5811b8538298e929b37404865e8d52675 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form @@ -80,7 +80,7 @@ </Group> <Component id="jCheckBox4" alignment="0" min="-2" max="-2" attributes="0"/> </Group> - <EmptySpace pref="96" max="32767" attributes="0"/> + <EmptySpace max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> 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 37fd2e6e1360d037e6e47ad386834cd5b24794c3..be2eaeb35119355b61a7672e6378c8a53a642fd5 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java @@ -45,12 +45,6 @@ public class BatchPanel extends ControlPanel { private final HumanFaceFactory factory = new HumanFaceFactory(); private boolean haveAvgFace = false; - /** - * ICP undersampling. 100 = none - */ - private int undersampling = 100; - - /** * Constructor. * @param action Action listener @@ -72,10 +66,7 @@ public class BatchPanel extends ControlPanel { jButton1.addActionListener(createListener(action, ACTION_COMMAND_COMPUTE_ICP)); - spinSlider1.initPercentage(this.undersampling); - spinSlider1.addSpinnerListener((ActionEvent ae) -> { // update slider when the input field changed - this.undersampling = (Integer) spinSlider1.getValue(); - }); + spinSlider1.initInteger(2000, 0, 5000, 1); jComboBox2.addItem(SIMILARITY_APPROX_HD); jComboBox2.addItem(SIMILARITY_COMPLETE_HD); @@ -202,11 +193,11 @@ public class BatchPanel extends ControlPanel { } /** - * Returns ICP undersampling parameter - * @return ICP undersampling parameter + * Returns the number of desired points, 0 for no downsampling + * @return the number of desired points, 0 for no downsampling */ public int getIcpUndersampling() { - return undersampling; + return (int) spinSlider1.getValue(); } /** @@ -395,7 +386,7 @@ public class BatchPanel extends ControlPanel { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(jCheckBox4)) - .addContainerGap(96, Short.MAX_VALUE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 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 f62bf7e5f15183b1515a78423f14592b708ad4b6..67d6cf51a44497be34a84d5e271cee51043c6fb9 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,8 @@ 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.NoSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling; import cz.fidentis.analyst.visitors.mesh.sampling.RandomSampling; import java.io.File; import java.io.IOException; @@ -114,6 +116,7 @@ public class IcpTask extends SwingWorker<MeshModel, HumanFace> { ////////////////////// */ + PointSampling sampling = (undersampling == 0)? new NoSampling() : new RandomSampling(undersampling); icpComputationTime.start(); HumanFaceUtils.alignMeshes( initFace, @@ -121,10 +124,11 @@ public class IcpTask extends SwingWorker<MeshModel, HumanFace> { 100, // max iterations controlPanel.scaleIcp(), 0.3, // error - new RandomSampling(undersampling), + sampling, false // drop k-d tree, if exists ); icpComputationTime.stop(); + System.out.println(sampling); } if (computeAvgFace) { // AVG template face @@ -184,7 +188,7 @@ public class IcpTask extends SwingWorker<MeshModel, HumanFace> { } if (controlPanel.showIcpPreview()) { if (faceSceneSlot == -1) { - faceSceneSlot = canvas.getScene().getFreeSlot(); + faceSceneSlot = canvas.getScene().getFreeSlotForFace(); } canvas.getScene().setDrawableFace(faceSceneSlot, f); canvas.getScene().getDrawableFace(faceSceneSlot).setTransparency(0.5f); diff --git a/GUI/src/main/java/cz/fidentis/analyst/canvas/Canvas.java b/GUI/src/main/java/cz/fidentis/analyst/canvas/Canvas.java index 053f750f4a12ea28922e26108cad818f32054828..5f03fde1027eb03b669b85464e807456b72fbf39 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/canvas/Canvas.java +++ b/GUI/src/main/java/cz/fidentis/analyst/canvas/Canvas.java @@ -89,7 +89,7 @@ public class Canvas extends JPanel implements HumanFaceListener { public int addPrimaryFace(HumanFace face) { if (face != null) { faces.add(face); - int index = scene.getFreeSlot(); + int index = scene.getFreeSlotForFace(); scene.setHumanFace(index, face); scene.setFaceAsPrimary(index); face.registerListener(this); @@ -107,7 +107,7 @@ public class Canvas extends JPanel implements HumanFaceListener { public int addSecondaryFace(HumanFace face) { if (face != null) { faces.add(face); - int index = scene.getFreeSlot(); + int index = scene.getFreeSlotForFace(); scene.setHumanFace(index, face); scene.setFaceAsSecondary(index); face.registerListener(this); 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 8ae8cd93716df9c13839f5ba38561bafac378a42..83565e8278c9fe9cabc249a8c1e447d3ed68f44d 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,7 +2,6 @@ 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; @@ -64,68 +63,9 @@ 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")); @@ -134,14 +74,7 @@ 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(0,1)); + setLayout(new GridLayout(1,0)); } } diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/ProgressDialog.java b/GUI/src/main/java/cz/fidentis/analyst/core/ProgressDialog.java index 746b897162797c0172aab63afaea1afbabe42c77..3ffcc1a6dfafebbada78eeb3a39afcaeacb47351 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/core/ProgressDialog.java +++ b/GUI/src/main/java/cz/fidentis/analyst/core/ProgressDialog.java @@ -107,7 +107,7 @@ public class ProgressDialog<T,V> extends JDialog { setSize(450, 100); cancelButton.addActionListener((ActionEvent e) -> { - System.out.println("ZZZ " + task.cancel(true)); + task.cancel(true); dispose(); }); } diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/combobox/ConditionalComboBoxListener.java b/GUI/src/main/java/cz/fidentis/analyst/core/combobox/ConditionalComboBoxListener.java new file mode 100644 index 0000000000000000000000000000000000000000..ee4e42c5a269d57bff9fe99640896126bdd86949 --- /dev/null +++ b/GUI/src/main/java/cz/fidentis/analyst/core/combobox/ConditionalComboBoxListener.java @@ -0,0 +1,38 @@ +package cz.fidentis.analyst.core.combobox; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * A listener responsible for selecting an enabled combo box item + * when a disabled item is selected. + * + * @author Radek Oslejsek + */ +public class ConditionalComboBoxListener implements ActionListener { + + private final SelectableComboBox combobox; + private Object oldItem; + + /** + * Constructor. + * + * @param combobox A selectable combo box. + */ + public ConditionalComboBoxListener(SelectableComboBox combobox) { + this.combobox = combobox; + combobox.setSelectedIndex(combobox.firstTrueItem()); + oldItem = combobox.getSelectedItem(); + } + + @Override + public void actionPerformed(ActionEvent e) { + Object selectedItem = combobox.getSelectedItem(); + if (!((ConditionalItem) selectedItem).isEnabled()) { + combobox.setSelectedItem(oldItem); + } else { + oldItem = selectedItem; + } + } + +} diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/combobox/ConditionalComboBoxRenderer.java b/GUI/src/main/java/cz/fidentis/analyst/core/combobox/ConditionalComboBoxRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..dadb58826cce12a38676776145be5d1d702610b0 --- /dev/null +++ b/GUI/src/main/java/cz/fidentis/analyst/core/combobox/ConditionalComboBoxRenderer.java @@ -0,0 +1,44 @@ +package cz.fidentis.analyst.core.combobox; + +import java.awt.Color; +import java.awt.Component; +import javax.swing.JList; +import javax.swing.ListCellRenderer; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.basic.BasicComboBoxRenderer; + +/** + * A combo box renderer adapted for displaying disabled items in gray color. + * + * @author Radek Oslejsek + */ +public class ConditionalComboBoxRenderer extends BasicComboBoxRenderer implements ListCellRenderer<Object> { + + private static final Color DISABLED_COLOR = Color.LIGHT_GRAY; + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + setBorder(new EmptyBorder(5,5,5,5)); + + if (isSelected) { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } else { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + + try { + if (value != null && !((ConditionalItem) value).isEnabled()) { + setBackground(list.getBackground()); + setForeground(DISABLED_COLOR); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + setFont(list.getFont()); + setText((value == null) ? "" : value.toString()); + } + return this; + } +} diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/combobox/ConditionalItem.java b/GUI/src/main/java/cz/fidentis/analyst/core/combobox/ConditionalItem.java new file mode 100644 index 0000000000000000000000000000000000000000..cead4e4799ef149211d6fdadbdef701b3aa87348 --- /dev/null +++ b/GUI/src/main/java/cz/fidentis/analyst/core/combobox/ConditionalItem.java @@ -0,0 +1,55 @@ +package cz.fidentis.analyst.core.combobox; + +/** + * A combo box item with "is enabled" state. + * + * @author Radek Oslejsek + */ +public class ConditionalItem { + + private Object object; + private boolean isEnabled; + + /** + * Constructor. + * + * @param object Item content + * @param isEnabled The state + */ + public ConditionalItem(Object object, boolean isEnabled) { + this.object = object; + this.isEnabled = isEnabled; + } + + /** + * Constructor. + * + * @param object Item content + */ + public ConditionalItem(Object object) { + this(object, true); + } + + /** + * Returns the state. + * + * @return the state + */ + public boolean isEnabled() { + return isEnabled; + } + + /** + * Sets the state. + * + * @param isEnabled Desired state. + */ + public void setEnabled(boolean isEnabled) { + this.isEnabled = isEnabled; + } + + @Override + public String toString() { + return object.toString(); + } +} diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/combobox/SelectableComboBox.java b/GUI/src/main/java/cz/fidentis/analyst/core/combobox/SelectableComboBox.java new file mode 100644 index 0000000000000000000000000000000000000000..2055d849c647f375063799448096c1dbe97361f3 --- /dev/null +++ b/GUI/src/main/java/cz/fidentis/analyst/core/combobox/SelectableComboBox.java @@ -0,0 +1,107 @@ +package cz.fidentis.analyst.core.combobox; + +import java.awt.Component; +import java.awt.event.ActionListener; +import javax.swing.JComboBox; + +/** + * A combobox adapted to show enabled/disabled items. + * + * @author Radek Oslejsek + */ +public class SelectableComboBox extends JComboBox<Object> { + + private ActionListener listener; + + /** + * Constructor. + */ + public SelectableComboBox() { + this.setRenderer(new ConditionalComboBoxRenderer()); + listener = new ConditionalComboBoxListener(this); + this.addActionListener(listener); + } + + /** + * Adds an enabled item into the combo box + * + * @param str Text of the item + */ + public void addItem(String str) { + addItem(new ConditionalItem(str, true)); + } + + /** + * Adds an item into the combobox in either enabled or disabled state, + * + * @param str Text of the item + * @param enabled The state + */ + public void addItem(String str, boolean enabled) { + addItem(new ConditionalItem(str, enabled)); + } + + /** + * Add an existing item into the combobox. + * + * @param ci An existing item + */ + public void addItem(Component ci) { + this.add(ci); + this.setRenderer(new ConditionalComboBoxRenderer()); + this.addActionListener(new ConditionalComboBoxListener(this)); + } + + /** + * If the combobox contains {@code str}, sets its state to [@code bool}. + * If it's not yet an item, ignores it. The method also re-sets the selected item to the first + * one shown in the list as "true", and disables the listeners in this + * process to avoid firing an action when reorganizing the list. + * + * @param str Text of the existing item + * @param enabled New state + */ + public void setItem(String str, boolean enabled) { + int n = this.getItemCount(); + for (int i = 0; i < n; i++) { + if (this.getItemAt(i).toString().equals(str)) { + + this.removeActionListener(listener); + this.removeItemAt(i); + this.insertItemAt(new ConditionalItem(str, enabled), i); + int k = this.firstTrueItem(); + if (k < 0) { + k = 0; // default index 0 if no true item is shown as true + } + this.setSelectedIndex(k); + this.addActionListener(listener); + + return; + } + } + System.err.println("Warning: item " + str + " is not a member of this combobox: ignoring it..."); + } + + protected Object[] getItems() { + int n = this.getItemCount(); + Object[] obj = new Object[n]; + for (int i = 0; i < n; i++) { + obj[i] = this.getItemAt(i); + } + return obj; + } + + /** + * @return -1 if no item is true + */ + protected int firstTrueItem() { + int i = 0; + for (Object obj : this.getItems()) { + if (((ConditionalItem) obj).isEnabled()) { + return i; + } + i++; + } + return -1; + } +} diff --git a/GUI/src/main/java/cz/fidentis/analyst/gui/Installer.java b/GUI/src/main/java/cz/fidentis/analyst/gui/Installer.java index cb50c9a7a8baa92590d8d0f7d6f4d215d93d605e..6be0f44fba8d11ad22784701ae18b3aa8984d19a 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/gui/Installer.java +++ b/GUI/src/main/java/cz/fidentis/analyst/gui/Installer.java @@ -32,8 +32,10 @@ public class Installer extends ModuleInstall { @Override public void restored() { + /* try { for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { + //System.out.println(info.getName()); if ("Nimbus".equals(info.getName())) { javax.swing.UIManager.setLookAndFeel(info.getClassName()); break; @@ -42,6 +44,7 @@ public class Installer extends ModuleInstall { } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) { java.util.logging.Logger.getLogger(Installer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } + */ java.awt.EventQueue.invokeLater(() -> { //old JFrame, disabled /*frameMain = new UserInterface(); 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 b0bace831f0823479e7429871cd04ecf236d0cff..b143d24c2a0b3822f1de3b3bf2a9c936bce14ca0 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java @@ -17,6 +17,7 @@ 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 cz.fidentis.analyst.visitors.mesh.sampling.UniformSpaceSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.UniformSurfaceSampling; import java.awt.Color; import java.awt.event.ActionEvent; import javax.swing.JFormattedTextField; @@ -106,12 +107,12 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL getPrimaryDrawableFace().getHumanFace().registerListener(this); getSecondaryDrawableFace().getHumanFace().registerListener(this); - controlPanel.setEnabledPlanesButton( + controlPanel.setEnabledPlanes( getCanvas().getPrimaryFace().hasSymmetryPlane() && getCanvas().getSecondaryFace().hasSymmetryPlane() ); - controlPanel.setEnabledProcrustesButton( + controlPanel.setEnabledProcrustes( getCanvas().getPrimaryFace().hasFeaturePoints() && getCanvas().getSecondaryFace().hasFeaturePoints() ); @@ -122,13 +123,23 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL String action = ae.getActionCommand(); switch (action) { - case RegistrationPanel.ACTION_COMMAND_APPLY_ICP: - applyICP(); + case RegistrationPanel.ACTION_COMMAND_REGISTER: + String alg = controlPanel.getRegistrationAlgorihm(); + if (RegistrationPanel.AUTO_ALIGNMENT_ALGORITHM[0].equals(alg)) { // ICP + applyICP(); + } else if (RegistrationPanel.AUTO_ALIGNMENT_ALGORITHM[1].equals(alg)) { // Procrustes + applyProcrustes(); + } else if (RegistrationPanel.AUTO_ALIGNMENT_ALGORITHM[2].equals(alg)) { // Symmetry + alignSymmetryPlanes(); + } highlightCloseFeaturePoints(); - getCanvas().getSecondaryFace().announceEvent(new HumanFaceTransformedEvent( - getCanvas().getSecondaryFace(), "", this) - ); - pointCloudSlot = drawPointSamples(getCanvas().getScene().getSecondaryFaceSlot(), pointCloudSlot, controlPanel.getIcpUndersamplingStrength()); + if (RegistrationPanel.AUTO_ALIGNMENT_ALGORITHM[1].equals(alg)) { // Procrustes also transforms the primary face + getCanvas().getPrimaryFace().announceEvent(new HumanFaceTransformedEvent(getCanvas().getPrimaryFace(), "", this)); + } + getCanvas().getSecondaryFace().announceEvent(new HumanFaceTransformedEvent(getCanvas().getSecondaryFace(), "", this)); + pointCloudSlot = drawPointSamples(getCanvas().getScene().getSecondaryFaceSlot(), + pointCloudSlot, controlPanel.getIcpUndersamplingStrength()); + break; case RegistrationPanel.ACTION_COMMAND_MANUAL_TRANSFORMATION_IN_PROGRESS: HumanFaceUtils.transformFace( @@ -153,25 +164,6 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL fpThreshold = ((Number) (((JFormattedTextField) ae.getSource()).getValue())).doubleValue(); highlightCloseFeaturePoints(); break; - case RegistrationPanel.ACTION_COMMAND_PROCRUSTES_APPLY: - applyProcrustes(); - highlightCloseFeaturePoints(); - getCanvas().getPrimaryFace().announceEvent(new HumanFaceTransformedEvent( - getCanvas().getPrimaryFace(), "", this) - ); - getCanvas().getSecondaryFace().announceEvent(new HumanFaceTransformedEvent( - getCanvas().getSecondaryFace(), "", this) - ); - pointCloudSlot = drawPointSamples(getCanvas().getScene().getSecondaryFaceSlot(), pointCloudSlot, controlPanel.getIcpUndersamplingStrength()); - break; - case RegistrationPanel.ACTION_COMMAND_ALIGN_SYMMETRY_PLANES: - alignSymmetryPlanes(); - highlightCloseFeaturePoints(); - getCanvas().getSecondaryFace().announceEvent(new HumanFaceTransformedEvent( - getCanvas().getSecondaryFace(), "", this) - ); - pointCloudSlot = drawPointSamples(getCanvas().getScene().getSecondaryFaceSlot(), pointCloudSlot, controlPanel.getIcpUndersamplingStrength()); - break; case RegistrationPanel.ACTION_COMMAND_POINT_SAMPLING_STRENGTH: int numSamples = (Integer) ((SpinSlider) ae.getSource()).getValue(); pointCloudSlot = drawPointSamples(getCanvas().getScene().getSecondaryFaceSlot(), pointCloudSlot, numSamples); @@ -201,7 +193,7 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL } if (event instanceof SymmetryPlaneChangedEvent) { - controlPanel.setEnabledPlanesButton( + controlPanel.setEnabledPlanes( getCanvas().getPrimaryFace().hasSymmetryPlane() && getCanvas().getSecondaryFace().hasSymmetryPlane() ); @@ -331,6 +323,8 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL return new CurvatureSampling(CurvatureSampling.CurvatureAlg.MIN, strength); } else if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[6])) { return new UniformSpaceSampling(strength); + } else if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[7])) { + return new UniformSurfaceSampling(strength); } else { return null; } @@ -348,14 +342,16 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL if (sampling.getClass() == NoSampling.class) { // don't show if (cloudSlot != -1) { - getScene().setOtherDrawable(cloudSlot, null); + getScene().setOtherDrawable(cloudSlot, null); // hide existing points } return -1; } else { - if (cloudSlot == -1) { - cloudSlot = getCanvas().getScene().getFreeSlot(); + synchronized (this) { + if (cloudSlot == -1) { + cloudSlot = getCanvas().getScene().getFreeSlotForOtherDrawables(); + } + getScene().setOtherDrawable(cloudSlot, new DrawablePointCloud(sampling.getSamples())); } - getScene().setOtherDrawable(cloudSlot, new DrawablePointCloud(sampling.getSamples())); return cloudSlot; } } 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 87d48cc6d40d438ec318420751b14863223031bf..6bfe69798a65fee022d88c8d4464ced6b97c2531 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.form +++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.form @@ -931,18 +931,16 @@ </Group> </Group> <Group type="102" alignment="0" attributes="0"> - <Group type="103" groupAlignment="1" attributes="0"> + <Group type="103" groupAlignment="0" attributes="0"> <Component id="jLabel3" alignment="0" min="-2" max="-2" attributes="0"/> - <Group type="102" attributes="0"> + <Group type="102" alignment="0" attributes="0"> + <Component id="selectableComboBox1" min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" 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"/> + <Component id="jButtonInfo1" 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"/> <EmptySpace max="32767" attributes="0"/> </Group> </Group> @@ -956,12 +954,11 @@ <Group type="103" groupAlignment="0" max="-2" attributes="0"> <Group type="103" groupAlignment="3" attributes="0"> <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="jButton3" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="selectableComboBox1" alignment="3" min="-2" max="-2" attributes="0"/> </Group> <Component id="jButtonInfo1" pref="0" max="32767" attributes="0"/> </Group> - <EmptySpace type="separate" max="-2" attributes="0"/> + <EmptySpace min="-2" pref="21" 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"/> @@ -1060,32 +1057,6 @@ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton1ActionPerformed"/> </Events> </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/registration/Bundle.properties" key="RegistrationPanel.jButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> - </Property> - <Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jButton2.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> - </Property> - </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/> - </Events> - </Component> - <Component class="javax.swing.JButton" name="jButton3"> - <Properties> - <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> - <Font name="Ubuntu" size="15" style="1"/> - </Property> - <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> - <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jButton3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/> - </Property> - </Properties> - </Component> <Component class="javax.swing.JButton" name="jButtonInfo1"> <Properties> <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor"> @@ -1132,6 +1103,8 @@ </Component> <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider1"> </Component> + <Component class="cz.fidentis.analyst.core.combobox.SelectableComboBox" name="selectableComboBox1"> + </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 e40d77b13ddddfb4eea3452b2be224de7a6d24bd..d019d3c7e526bb0042afb833af540fb00e11cfe4 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.java @@ -27,22 +27,18 @@ public class RegistrationPanel extends ControlPanel { /* * External actions */ - public static final String ACTION_COMMAND_APPLY_ICP = "apply ICP"; - public static final String ACTION_COMMAND_MANUAL_TRANSFORMATION_FINISHED = "manual transformation"; public static final String ACTION_COMMAND_MANUAL_TRANSFORMATION_IN_PROGRESS = "manual transformation in progress"; public static final String ACTION_COMMAND_FP_CLOSENESS_THRESHOLD = "fp closeness treshold"; - public static final String ACTION_COMMAND_PROCRUSTES_APPLY = "Procrustes apply"; - - public static final String ACTION_COMMAND_ALIGN_SYMMETRY_PLANES = "align symmetry planes"; - public static final String ACTION_COMMAND_POINT_SAMPLING_STRATEGY = "Point sampling strategy"; public static final String ACTION_COMMAND_POINT_SAMPLING_STRENGTH = "Point sampling strength"; - /* + public static final String ACTION_COMMAND_REGISTER = "Register"; + + /* * Configuration of panel-specific GUI elements */ public static final String STRATEGY_POINT_TO_POINT = "Point to point"; @@ -55,7 +51,14 @@ public class RegistrationPanel extends ControlPanel { "Gaussian Curvature", "Max Curvature", "Min Curvature", - "Uniform Space Sampling" + "Uniform Space Sampling", + "Uniform Mesh Sampling", + }; + + public static final String[] AUTO_ALIGNMENT_ALGORITHM = new String[] { + "Align meshes (ICP)", + "Align feature points (Procrustes)", + "Align symmetry planes" }; @@ -86,10 +89,7 @@ public class RegistrationPanel extends ControlPanel { initComponents(); this.action = action; - // connect GUI element with RegistrationAction listener: - jButton1.addActionListener(createListener(action, ACTION_COMMAND_APPLY_ICP)); - jButton2.addActionListener(createListener(action, ACTION_COMMAND_PROCRUSTES_APPLY)); - jButton3.addActionListener(createListener(action, ACTION_COMMAND_ALIGN_SYMMETRY_PLANES)); + jButton1.addActionListener(createListener(action, ACTION_COMMAND_REGISTER)); thersholdFTF.setValue(5.0); thersholdFTF.addActionListener(createListener(action, ACTION_COMMAND_FP_CLOSENESS_THRESHOLD)); @@ -114,6 +114,11 @@ public class RegistrationPanel extends ControlPanel { Arrays.stream(POINT_SAMPLING_STRATEGIES).forEach(v -> jComboBox1.addItem(v)); jComboBox1.setSelectedIndex(0); jComboBox1.addActionListener(createListener(action, ACTION_COMMAND_POINT_SAMPLING_STRATEGY)); + + selectableComboBox1.addItem(AUTO_ALIGNMENT_ALGORITHM[0], true); + selectableComboBox1.addItem(AUTO_ALIGNMENT_ALGORITHM[1], true); + selectableComboBox1.addItem(AUTO_ALIGNMENT_ALGORITHM[2], true); + //selectableComboBox1.setSelectedIndex(0); } /** @@ -154,6 +159,14 @@ public class RegistrationPanel extends ControlPanel { return undersamplingStrength; } + /** + * Returns actually selected algorithm + * @return actually selected algorithm + */ + public String getRegistrationAlgorihm() { + return this.selectableComboBox1.getSelectedItem().toString(); + } + /** * Return selected point sampling strategy * @return selected point sampling strategy @@ -163,21 +176,19 @@ public class RegistrationPanel extends ControlPanel { } /** - * Turns ob or off the plane alignment button + * Turns on or off the plane alignment * @param on on-off value */ - public void setEnabledPlanesButton(boolean on) { - jButton3.setEnabled(on); - jButton3.setFont(new java.awt.Font("Ubuntu", (on) ? 1 : 0, 15)); // NOI18N + public void setEnabledPlanes(boolean on) { + selectableComboBox1.setItem(AUTO_ALIGNMENT_ALGORITHM[2], on); } /** - * Turns ob or off the plane alignment button + * Turns on or off the plane alignment * @param on on-off value */ - public void setEnabledProcrustesButton(boolean on) { - jButton2.setEnabled(on); - jButton2.setFont(new java.awt.Font("Ubuntu", (on) ? 1 : 0, 15)); // NOI18N + public void setEnabledProcrustes(boolean on) { + selectableComboBox1.setItem(AUTO_ALIGNMENT_ALGORITHM[1], on); } /** @@ -283,16 +294,16 @@ public class RegistrationPanel extends ControlPanel { JOptionPane.showMessageDialog( this, "<html>" - + "<strong>Mesh</strong>: <br/>" + + "<strong>Align meshes (ICP)</strong>: <br/>" + "Superimposition of two faces by applying iterative closest points (ICP) algorithm<br/>" + "on their triangular meshes. This approach is slow, unsharp, but universal.<br/>" + "<br/>" - + "<strong>Feature points</strong>: <br/>" + + "<strong>Align feature points (Procrustes)</strong>: <br/>" + "Superimposition of two faces by applying Procrustes algorithm<br/>" + "on feature points. It is fast, precise, but feature points of the same types<br/>" + "have to be presented for both faces.<br/>" + "<br/>" - + "<strong>Symmetry planes</strong>: <br/>" + + "<strong>Align symmetry planes</strong>: <br/>" + "Superimposition of two faces by aligning their symmetry planes.<br/>" + "Symmetry planes have to be computed on the \"<i>Symmetry</i>\" tab first<br/>" + "to enable this feature.<br/>" @@ -350,13 +361,12 @@ public class RegistrationPanel extends ControlPanel { jFormattedTextField2 = new javax.swing.JFormattedTextField(); jLabel8 = new javax.swing.JLabel(); 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(); + selectableComboBox1 = new cz.fidentis.analyst.core.combobox.SelectableComboBox(); jPanel4 = new javax.swing.JPanel(); featurePointsLabel = new javax.swing.JLabel(); thersholdFTF = new javax.swing.JFormattedTextField(); @@ -833,18 +843,6 @@ public class RegistrationPanel extends ControlPanel { } }); - jButton2.setFont(new java.awt.Font("Ubuntu", 1, 15)); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(jButton2, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jButton2.text")); // NOI18N - jButton2.setToolTipText(org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jButton2.toolTipText")); // NOI18N - jButton2.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton2ActionPerformed(evt); - } - }); - - jButton3.setFont(new java.awt.Font("Ubuntu", 1, 15)); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(jButton3, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jButton3.text")); // NOI18N - jButtonInfo1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/info.png"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jButtonInfo1, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jButtonInfo1.text")); // NOI18N jButtonInfo1.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); @@ -888,16 +886,14 @@ public class RegistrationPanel extends ControlPanel { .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.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel3) .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(selectableComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .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) + .addComponent(jButtonInfo1))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) ); jPanel1Layout.setVerticalGroup( @@ -907,10 +903,9 @@ public class RegistrationPanel extends ControlPanel { .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jButton1) - .addComponent(jButton2) - .addComponent(jButton3)) + .addComponent(selectableComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(jButtonInfo1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)) - .addGap(18, 18, 18) + .addGap(21, 21, 21) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jCheckBox1) .addComponent(jLabel3)) @@ -922,13 +917,10 @@ public class RegistrationPanel extends ControlPanel { .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .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)) + .addGap(18, 18, 18) .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(18, 18, 18) - .addComponent(jLabel8))) + .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .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) @@ -1107,10 +1099,6 @@ public class RegistrationPanel extends ControlPanel { thersholdFTF.postActionEvent(); }//GEN-LAST:event_thersholdUpButtonActionPerformed - private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_jButton2ActionPerformed - private void scaleMinusButtonMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_scaleMinusButtonMouseReleased animator.stopModelAnimation(this); action.actionPerformed(new ActionEvent( @@ -1297,8 +1285,6 @@ public class RegistrationPanel extends ControlPanel { // Variables declaration - do not modify//GEN-BEGIN:variables 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; @@ -1342,6 +1328,7 @@ public class RegistrationPanel extends ControlPanel { private javax.swing.JPanel scalePanel; private javax.swing.JButton scalePlusButton; private javax.swing.ButtonGroup secondaryRenerModeGroup; + private cz.fidentis.analyst.core.combobox.SelectableComboBox selectableComboBox1; private javax.swing.JPanel shiftPanel; private cz.fidentis.analyst.core.SpinSlider spinSlider1; private javax.swing.JFormattedTextField thersholdFTF; diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/Scene.java b/GUI/src/main/java/cz/fidentis/analyst/scene/Scene.java index c8ef2126571a5a71a8fd3e4ba18b960ec5f74149..0645d1bc72b7f813e487cf904b3de76924e5bec6 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/scene/Scene.java +++ b/GUI/src/main/java/cz/fidentis/analyst/scene/Scene.java @@ -41,10 +41,12 @@ public class Scene { } /** - * Finds and returns a first free slot for human face and its drawables. - * @return a first free slot for human face and its drawables. + * Finds and returns a first free slot <b>for human face and its feature points and symmetry plane</b>. + * Use {@link #getFreeSlotForOtherDrawables()} to get free slot of other objects. + * + * @return a first free slot for human face and its feature points and symmetry plane. */ - public int getFreeSlot() { + public int getFreeSlotForFace() { int slot = -1; for (int i = 0; i < getArraySize(); i++) { if (this.drawableFaces.get(i) == null) { @@ -62,7 +64,7 @@ public class Scene { * Finds and returns a first free slot for other drawable objects. * @return a first free slot for other drawable objects. */ - public int getFreeSlotForOtherDrawables() { + public synchronized int getFreeSlotForOtherDrawables() { int slot = -1; for (int i = 0; i < this.otherDrawables.size(); i++) { if (this.otherDrawables.get(i) == null) { @@ -73,7 +75,7 @@ public class Scene { if (slot == -1) { slot = otherDrawables.size(); } - return (slot < Scene.MAX_FACES_IN_SCENE) ? slot : -1; + return slot; } /** 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 224e0ce48c7cb9d30468314d21ff6c0fa9badb68..a2bab1cd9ae5fa7034d66457a97c037d1516c72a 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java @@ -175,7 +175,7 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe } else { computeVerticalCuttingPlanes(); } - getDrawableCuttingPlane(priCuttingPlaneSlot).show(false); // will be shown on the panel focus + getDrawableCuttingPlane(priCuttingPlaneSlot).show(false); // will be shown when the panel is focused if (getDrawableCuttingPlane(secCuttingPlaneSlot) != null) { getDrawableCuttingPlane(secCuttingPlaneSlot).show(false); } @@ -277,20 +277,24 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe Plane plane = new Plane(new Vector3d(1,0,0), bbox.getMidPoint().x); DrawableCuttingPlane cuttingPlane = new DrawableCuttingPlane(plane, bbox, true); cuttingPlane.setTransparency(0.5f); - if (priCuttingPlaneSlot == -1) { - priCuttingPlaneSlot = getCanvas().getScene().getFreeSlotForOtherDrawables(); + synchronized (this) { + if (priCuttingPlaneSlot == -1) { + priCuttingPlaneSlot = getCanvas().getScene().getFreeSlotForOtherDrawables(); + } + getCanvas().getScene().setOtherDrawable(priCuttingPlaneSlot, cuttingPlane); } - getCanvas().getScene().setOtherDrawable(priCuttingPlaneSlot, cuttingPlane); if (getSecondaryDrawableFace() != null) { bbox = getCanvas().getSecondaryFace().getBoundingBox(); plane = new Plane(new Vector3d(1,0,0), bbox.getMidPoint().x); cuttingPlane = new DrawableCuttingPlane(plane, bbox, true); cuttingPlane.setTransparency(0.5f); - if (secCuttingPlaneSlot == -1) { - secCuttingPlaneSlot = getCanvas().getScene().getFreeSlotForOtherDrawables(); + synchronized (this) { + if (secCuttingPlaneSlot == -1) { + secCuttingPlaneSlot = getCanvas().getScene().getFreeSlotForOtherDrawables(); + } + getCanvas().getScene().setOtherDrawable(secCuttingPlaneSlot, new DrawableCuttingPlane(cuttingPlane)); } - getCanvas().getScene().setOtherDrawable(secCuttingPlaneSlot, new DrawableCuttingPlane(cuttingPlane)); } cuttingPlaneFromSymmetry = false; @@ -310,10 +314,12 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe false ); cuttingPlane.setTransparency(0.5f); - if (priCuttingPlaneSlot == -1) { - priCuttingPlaneSlot = getCanvas().getScene().getFreeSlotForOtherDrawables(); + synchronized (this) { + if (priCuttingPlaneSlot == -1) { + priCuttingPlaneSlot = getCanvas().getScene().getFreeSlotForOtherDrawables(); + } + getCanvas().getScene().setOtherDrawable(priCuttingPlaneSlot, cuttingPlane); } - getCanvas().getScene().setOtherDrawable(priCuttingPlaneSlot, cuttingPlane); recomputePrimaryProfile(); if (getCanvas().getSecondaryFace() != null) { @@ -327,10 +333,12 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe false ); cuttingPlane.setTransparency(0.5f); - if (secCuttingPlaneSlot == -1) { - secCuttingPlaneSlot = getCanvas().getScene().getFreeSlotForOtherDrawables(); + synchronized (this) { + if (secCuttingPlaneSlot == -1) { + secCuttingPlaneSlot = getCanvas().getScene().getFreeSlotForOtherDrawables(); + } + getCanvas().getScene().setOtherDrawable(secCuttingPlaneSlot, cuttingPlane); } - getCanvas().getScene().setOtherDrawable(secCuttingPlaneSlot, cuttingPlane); recomputeSecondaryProfile(); } cuttingPlaneFromSymmetry = true; @@ -348,8 +356,8 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe curve.projectFeaturePointsToCurve(close); } - protected DrawableCuttingPlane getDrawableCuttingPlane(int index) { - return (DrawableCuttingPlane) getScene().getOtherDrawable(index); + protected DrawableCuttingPlane getDrawableCuttingPlane(int slot) { + return (DrawableCuttingPlane) getScene().getOtherDrawable(slot); } protected void showCuttingPlanes(boolean show, boolean showMirrors) { 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 680748a1315558a92581dcebd2286d7e30d13808..12021420a64dc9e5ea95e5d7f01f5ccd5fb52ba1 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java @@ -177,8 +177,8 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe face.announceEvent(new SymmetryPlaneChangedEvent(face, "", this)); } - protected void recomputeFromFeaturePoints(int index) { - HumanFace face = getCanvas().getHumanFace(index); + protected void recomputeFromFeaturePoints(int slot) { + HumanFace face = getCanvas().getHumanFace(slot); if (face == null) { return; } @@ -215,16 +215,16 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe } face.setSymmetryPlane(new Plane(planes)); // creates an average plane - setDrawablePlane(face, index); + setDrawablePlane(face, slot); face.announceEvent(new SymmetryPlaneChangedEvent(face, "", this)); } - protected void setDrawablePlane(HumanFace face, int index) { - getCanvas().getScene().setDrawableSymmetryPlane(index, face); // add or remove + protected void setDrawablePlane(HumanFace face, int slot) { + getCanvas().getScene().setDrawableSymmetryPlane(slot, face); // add or remove if (face.hasSymmetryPlane()) { - getCanvas().getScene().getDrawableSymmetryPlane(index).setTransparency(0.5f); - getCanvas().getScene().getDrawableSymmetryPlane(index).setColor( - (index == 0) ? DrawableFace.SKIN_COLOR_PRIMARY.darker() : DrawableFace.SKIN_COLOR_SECONDARY.darker() + getCanvas().getScene().getDrawableSymmetryPlane(slot).setTransparency(0.5f); + getCanvas().getScene().getDrawableSymmetryPlane(slot).setColor( + (slot == 0) ? DrawableFace.SKIN_COLOR_PRIMARY.darker() : DrawableFace.SKIN_COLOR_SECONDARY.darker() ); } } @@ -271,7 +271,7 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe return -1; } else { if (cloudSlot == -1) { - cloudSlot = getCanvas().getScene().getFreeSlot(); + cloudSlot = getCanvas().getScene().getFreeSlotForFace(); } getScene().setOtherDrawable(cloudSlot, new DrawablePointCloud(sampling.getSamples())); return cloudSlot; 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 af111187b812a982823711c5f46daf7d8d116c5f..7e2685bc5c8b4978ae1a8842630d2244e34a87c9 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.form +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.form @@ -330,8 +330,8 @@ <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="selectableComboBox1" min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> <Component id="jButton2" min="-2" max="-2" attributes="0"/> <EmptySpace max="32767" attributes="0"/> </Group> @@ -342,8 +342,8 @@ <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"/> + <Component id="selectableComboBox1" alignment="3" min="-2" max="-2" attributes="0"/> </Group> <EmptySpace max="32767" attributes="0"/> </Group> @@ -351,16 +351,6 @@ </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"> @@ -374,6 +364,8 @@ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/> </Events> </Component> + <Component class="cz.fidentis.analyst.core.combobox.SelectableComboBox" name="selectableComboBox1"> + </Component> </SubComponents> </Container> </SubComponents> 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 33d9732550950b0501c11b7133fd7b3cae16cfcb..8c60eda639c8297f7818428d8934078dec4952a0 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java @@ -65,7 +65,7 @@ public class SymmetryPanel extends ControlPanel { action.actionPerformed(new ActionEvent( // recompute e.getSource(), ActionEvent.ACTION_PERFORMED, - jComboBox1.getSelectedItem().toString() + selectableComboBox1.getSelectedItem().toString() )); }); @@ -81,9 +81,9 @@ public class SymmetryPanel extends ControlPanel { this.showAlgorithmHelp(); }); - jComboBox1.addItem(SymmetryPanel.ACTION_COMMAND_RECOMPUTE_FROM_MESH); - //jComboBox1.addItem(SymmetryPanel.ACTION_COMMAND_COMPUTE_FROM_FPS); - jComboBox1.setSelectedIndex(0); + selectableComboBox1.addItem(SymmetryPanel.ACTION_COMMAND_RECOMPUTE_FROM_MESH, true); + selectableComboBox1.addItem(SymmetryPanel.ACTION_COMMAND_COMPUTE_FROM_FPS, true); + selectableComboBox1.setSelectedIndex(0); jComboBox3.addActionListener((ActionEvent e) -> { // set default values setDefaultValues(action); @@ -143,13 +143,7 @@ public class SymmetryPanel extends ControlPanel { * @param on {@code true} = enable */ public void setComputeFromFPs(boolean on) { - if (on && jComboBox1.getItemCount() == 1) { - jComboBox1.addItem(SymmetryPanel.ACTION_COMMAND_COMPUTE_FROM_FPS); - } - - if (!on && jComboBox1.getItemCount() == 2) { - jComboBox1.removeItemAt(1); - } + selectableComboBox1.setItem(SymmetryPanel.ACTION_COMMAND_COMPUTE_FROM_FPS, on); } /** @@ -271,8 +265,8 @@ public class SymmetryPanel extends ControlPanel { 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(); + selectableComboBox1 = new cz.fidentis.analyst.core.combobox.SelectableComboBox(); jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jPanel1.border.title_1"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 14))); // NOI18N @@ -427,8 +421,8 @@ public class SymmetryPanel extends ControlPanel { 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(selectableComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jButton2) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); @@ -437,8 +431,8 @@ public class SymmetryPanel extends ControlPanel { .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)) + .addComponent(jButton2) + .addComponent(selectableComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); @@ -493,7 +487,6 @@ public class SymmetryPanel extends ControlPanel { private javax.swing.JButton jButtonInfo1; private javax.swing.JButton jButtonInfo2; private javax.swing.JButton jButtonInfo3; - private javax.swing.JComboBox<String> jComboBox1; private javax.swing.JComboBox<String> jComboBox2; private javax.swing.JComboBox<String> jComboBox3; private javax.swing.JLabel jLabel1; @@ -507,6 +500,7 @@ public class SymmetryPanel extends ControlPanel { private javax.swing.JPanel jPanel3; private javax.swing.JTextField jTextField1; private javax.swing.JTextField jTextField2; + private cz.fidentis.analyst.core.combobox.SelectableComboBox selectableComboBox1; private cz.fidentis.analyst.core.SpinSlider spinSlider1; private cz.fidentis.analyst.core.SpinSlider spinSlider2; // End of variables declaration//GEN-END:variables diff --git a/GUI/src/main/java/cz/fidentis/analyst/tests/IcpDownsampling.java b/GUI/src/main/java/cz/fidentis/analyst/tests/IcpDownsampling.java index 50e9c25d45fb14ab24e1dfa18848df4bec1e80a7..216a461cc2b1fd2914156fc8a882f2c631bcb1f8 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/tests/IcpDownsampling.java +++ b/GUI/src/main/java/cz/fidentis/analyst/tests/IcpDownsampling.java @@ -3,17 +3,22 @@ package cz.fidentis.analyst.tests; import cz.fidentis.analyst.batch.Stopwatch; import cz.fidentis.analyst.face.HumanFace; import cz.fidentis.analyst.face.HumanFaceUtils; +import cz.fidentis.analyst.mesh.core.MeshModel; +import cz.fidentis.analyst.symmetry.Plane; +import cz.fidentis.analyst.symmetry.SymmetryEstimator; +import cz.fidentis.analyst.symmetry.SymmetryEstimatorRobust; import cz.fidentis.analyst.visitors.mesh.HausdorffDistance; 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 cz.fidentis.analyst.visitors.mesh.sampling.UniformSpaceSampling; +import cz.fidentis.analyst.visitors.mesh.sampling.UniformSurfaceSampling; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.SortedMap; @@ -29,7 +34,7 @@ import java.util.stream.Collectors; public class IcpDownsampling { private static final String DATA_DIR = "../../analyst-data-antropologie/_ECA"; - private static final int MAX_SAMPLES = 100; + private static final int MAX_SAMPLES = 500; /** * Main method @@ -43,18 +48,20 @@ public class IcpDownsampling { .limit(MAX_SAMPLES) .collect(Collectors.toList()); - SortedMap<Double, Stopwatch> efficiency = new TreeMap<>(); - SortedMap<Double, List<Double>> precision = new TreeMap<>(); + SortedMap<Integer, Stopwatch> efficiency = new TreeMap<>(); + SortedMap<Integer, List<Double>> precision = new TreeMap<>(); - String alg = "random"; + //String alg = "random"; //String alg = "gaussian"; //String alg = "uniform space"; + String alg = "uniform mesh"; int counter = 1; - for (int i = 0; i < faces.size(); i++) { - for (int j = i; j < faces.size(); j++) { // starts with "i"! + for (int i = 0; i < faces.size(); i += 5) { + for (int j = i; j < faces.size(); j += 5) { // starts with "i"! if (i != j) { // register only different faces - System.out.println(counter + " / " + (faces.size()*faces.size()/2)); + System.out.println(); + System.out.println("Iteration: " + counter); compareFaces(faces.get(i), faces.get(j), efficiency, precision, alg); printResults(efficiency, precision, counter, alg); counter++; @@ -63,36 +70,54 @@ public class IcpDownsampling { } } - protected static void printResults(SortedMap<Double, Stopwatch> efficiency, SortedMap<Double, List<Double>> precision, double counter, String sampling) { + protected static void printResults(SortedMap<Integer, Stopwatch> efficiency, SortedMap<Integer, List<Double>> precision, double counter, String sampling) { System.out.println(); System.out.println("Avg. Time (ms) - " + sampling + " sampling:"); efficiency.entrySet().forEach(e -> { System.out.println(e.getKey() + ";" + (e.getValue().getTotalTime() / counter)); }); System.out.println(); - System.out.println("Avg. Precision - " + sampling + " sampling:"); + System.out.println("Avg. Precision - " + sampling + " sampling:"); + System.out.println("# samples;average similarity;variance"); precision.entrySet().forEach(e -> { - System.out.println(e.getKey() + ";" - + e.getValue().get(0) + ";" // min - + e.getValue().get(1) + ";" // max - + (e.getValue().get(2) / counter)); // avg + double avg = e.getValue().stream() + .mapToDouble(val -> val) + .summaryStatistics() + .getAverage(); + double variance = e.getValue().stream() + .mapToDouble(val -> Math.pow(Math.abs(val - avg), 2)) + .average() + .orElse(-1); + System.out.println(e.getKey() + ";" + avg + ";" + variance); }); } protected static void compareFaces( Path priFacePath, Path secFacePath, - Map<Double, Stopwatch> efficiency, Map<Double, List<Double>> precision, + Map<Integer, Stopwatch> efficiency, Map<Integer, List<Double>> precision, String samp) throws IOException { //double[] percs = new double[]{0.5,1,2,4,6,8,10,15,20,30,40,50,60,70,80,85,90,92,94,96,98,100}; - double[] percs = new double[]{100,90,80,70,60,50,40,30,20,15,10,8,6,4,2,1,0.5}; + //double[] percs = new double[]{100,90,80,70,60,50,40,30,20,15,10,8,6,4,2,1,0.5}; + //int[] samples = new int[]{0,200,500,1000,2000,3000,5000,10000,20000,30000,40000}; + int[] samples = new int[]{0,200,500,1000,2000,3000,5000}; HumanFace priFace = new HumanFace(priFacePath.toFile()); priFace.computeKdTree(false); + SymmetryEstimator seVisitor = new SymmetryEstimatorRobust(new UniformSpaceSampling(), 100, 1000); + priFace.getMeshModel().compute(seVisitor); + priFace.setSymmetryPlane(seVisitor.getSymmetryPlane()); - for (double i: percs) { - System.out.println("" + i); - HumanFace secFace = new HumanFace(secFacePath.toFile()); + HumanFace secFaceFromFile = new HumanFace(secFacePath.toFile()); + seVisitor = new SymmetryEstimatorRobust(new UniformSpaceSampling(), 100, 1000); + secFaceFromFile.getMeshModel().compute(seVisitor); + secFaceFromFile.setSymmetryPlane(seVisitor.getSymmetryPlane()); + + + System.out.println(secFaceFromFile.getShortName()); + for (int i: samples) { + HumanFace secFace = new HumanFace(new MeshModel(secFaceFromFile.getMeshModel()), secFaceFromFile.getId()); + secFace.setSymmetryPlane(new Plane(secFaceFromFile.getSymmetryPlane())); PointSampling sampling; switch (samp) { @@ -105,24 +130,35 @@ public class IcpDownsampling { case "uniform space": sampling = new UniformSpaceSampling(); break; + case "uniform mesh": + sampling = new UniformSurfaceSampling(); + break; default: return; } - sampling.setRequiredSamples(i/100.0); + if (i == 0) { + sampling = new NoSampling(); + } else { + //sampling.setRequiredSamples(i/100.0); + sampling.setRequiredSamples(i); + } efficiency.computeIfAbsent(i, k-> new Stopwatch("")).start(); + HumanFaceUtils.alignSymmetryPlanes(priFace, secFace, false, true); HumanFaceUtils.alignMeshes( priFace, secFace, // is transformed 100, // max iterations false,// scale 0.3, // error - (i == 100) ? new NoSampling() : sampling, // no undersampling + sampling, false // drop k-d tree, if exists ); efficiency.get(i).stop(); + System.out.println("" + sampling); + HausdorffDistance hd = new HausdorffDistance( priFace.getKdTree(), HausdorffDistance.Strategy.POINT_TO_POINT, @@ -131,11 +167,7 @@ public class IcpDownsampling { true // crop ); secFace.getMeshModel().compute(hd); - - List<Double> val = precision.computeIfAbsent(i, k -> Arrays.asList(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 0.0)); - val.set(0, Math.min(val.get(0), hd.getStats().getAverage())); - val.set(1, Math.max(val.get(1), hd.getStats().getAverage())); - val.set(2, val.get(2) + hd.getStats().getAverage()); + precision.computeIfAbsent(i, k -> new ArrayList<>()).add(hd.getStats().getAverage()); } System.out.println(); } 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 1d136de071aad207a972bc9db6984fb79eca211c..bff63b85fe181fe816e13be215422a48b5993bbc 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 : +BatchPanel.jLabel2.text=ICP samples (0 = no downsampling) : BatchPanel.jButton1.text=Compute BatchPanel.jButtonInfo1.text= BatchPanel.jButton6.text=Export results 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 15a33f9b70ec84ac7586897bc4bc97b6c06bdb79..0392f11b8a3037d4e5fd8cbf962a09f15afa06df 100644 --- a/GUI/src/main/resources/cz/fidentis/analyst/registration/Bundle.properties +++ b/GUI/src/main/resources/cz/fidentis/analyst/registration/Bundle.properties @@ -1,4 +1,4 @@ -RegistrationPanel.jButton1.text=Mesh +RegistrationPanel.jButton1.text=Compute RegistrationPanel.jCheckBox1.text= RegistrationPanel.jFormattedTextField1.text=0,3 RegistrationPanel.jLabel5.text=ICP min error: @@ -9,9 +9,7 @@ RegistrationPanel.thersholdUpButton.text= RegistrationPanel.thresholdDownButton.text= RegistrationPanel.featurePointsLabel.text=Highlight feature point pairs closer than: 2 -RegistrationPanel.jButton2.text=Feature points RegistrationPanel.jButton1.toolTipText=Apply ICP -RegistrationPanel.jButton2.toolTipText=Apply Procrustes RegistrationPanel.jPanel1.border.title=Auto-alignment RegistrationPanel.jPanel4.border.title=View: RegistrationPanel.jLabel1.text=Hausdorff distance: @@ -22,7 +20,6 @@ RegistrationPanel.jTextField1.text= BatchRegistrationPanel.jPanel2.border.title=Dataset BatchRegistrationPanel.jCheckBox1.text=compute average face from BatchRegistrationPanel.jPanel3.border.title=Similarity -RegistrationPanel.jButton3.text=Symmetry planes RegistrationPanel.scaleMinusButton.text= RegistrationPanel.scalePlusButton.text= RegistrationPanel.scalePanel.border.title=scale