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 4a2b0cc77c9a926c53f5bfd62e44bdc32100a8ad..6b905a2fbea01c8cc8c88dd4d3ce5a21d77ecf0b 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java @@ -272,7 +272,7 @@ public class SymmetryEstimator extends MeshVisitor { return facet; } - + protected int checkAndUpdatePlanes(List<Plane> planes, ApproxSymmetryPlane newPlane, int maxVotes) { if (newPlane.getVotes() > maxVotes) { planes.clear(); @@ -283,7 +283,7 @@ public class SymmetryEstimator extends MeshVisitor { } return maxVotes; } - + protected void setSymmetryPlane(List<Plane> planes) { if (config.isAveraging() && !planes.isEmpty()) { symmetryPlane = new Plane(planes); diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSection.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSection.java new file mode 100644 index 0000000000000000000000000000000000000000..9d17a4a530733bc0f22f8dbcd179b43b3b9db2f2 --- /dev/null +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSection.java @@ -0,0 +1,48 @@ +package cz.fidentis.analyst.visitors.mesh; + +import cz.fidentis.analyst.mesh.MeshVisitor; +import cz.fidentis.analyst.mesh.core.MeshFacet; +import cz.fidentis.analyst.mesh.core.MeshTriangle; + +import javax.vecmath.Point3d; +import java.util.ArrayList; +import java.util.List; + +/** + * A visitor that calculates the cross-section of a face and a cutting plane. + * <p> + * This visitor is thread-safe. + * </p> + * + * @author Dominik Racek + */ +public class CrossSection extends MeshVisitor { + private List<Point3d> points; + private MeshFacet plane; + + /** + * Constructor + * @param plane the cutting plane + */ + public CrossSection(MeshFacet plane) { + this.plane = plane; + this.points = new ArrayList<>(); + } + + @Override + public void visitMeshFacet(MeshFacet facet) { + //TODO Dont check every triangle, find first and then check it's neighbors and new neighbors and so on + synchronized (this) { + for (MeshTriangle tri : facet) { + if (tri.checkIntersectionWithPlane(plane)) { + points.add(facet.getVertex(tri.index1).getPosition()); + } + } + } + + } + + public List<Point3d> getPoints() { + return points; + } +} diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/ControlPanelBuilder.java b/GUI/src/main/java/cz/fidentis/analyst/core/ControlPanelBuilder.java index c06df4d81c9834cda84942f620767527715b4fa3..fbc7bf32b053bdbb440b10d6cb7de457f75a3513 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/core/ControlPanelBuilder.java +++ b/GUI/src/main/java/cz/fidentis/analyst/core/ControlPanelBuilder.java @@ -1,5 +1,7 @@ package cz.fidentis.analyst.core; +import cz.fidentis.analyst.symmetry.PolylinePanel; + import java.awt.Color; import java.awt.Font; import java.awt.GridBagConstraints; @@ -729,4 +731,20 @@ public class ControlPanelBuilder { return scrollPane; } + + /** + * Adds a polyline panel with profile curves + * + * @param panel Panel to be added + */ + public void addPolylinePanel(PolylinePanel panel) { + GridBagConstraints c = new GridBagConstraints(); + c.gridwidth = GridBagConstraints.RELATIVE; + c.gridy = row; + c.gridx = col; + c.insets = new Insets(10, 10, 0, 0); + + controlPanel.add(panel, c); + } + } diff --git a/GUI/src/main/java/cz/fidentis/analyst/curvature/CurvatureAction.java b/GUI/src/main/java/cz/fidentis/analyst/curvature/CurvatureAction.java index c745c98cfa174fe6a7d062a004121c18ce69c6ef..9279a6b2f513a8d2923114dcf39b6b584da7c841 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/curvature/CurvatureAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/curvature/CurvatureAction.java @@ -21,8 +21,9 @@ public class CurvatureAction extends ControlPanelAction { */ private Curvature visitor = null; private String curvatureType = CurvaturePanel.GAUSSIAN_CURVATURE; - + private final CurvaturePanel controlPanel; + private final JTabbedPane topControlPanel; /** * Constructor. @@ -32,7 +33,19 @@ public class CurvatureAction extends ControlPanelAction { */ public CurvatureAction(Canvas canvas, JTabbedPane topControlPanel) { super(canvas, topControlPanel); + this.topControlPanel = topControlPanel; this.controlPanel = new CurvaturePanel(this); + + // 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 CurvaturePanel) { + getCanvas().getScene().setDefaultColors(); + } + }); + + topControlPanel.setSelectedComponent(controlPanel); } @Override 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 0263a00518c5916bc163d1ad75fed7176ac75102..64416253984cb183998c9bfc3f142c10c1fb55a0 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java @@ -141,6 +141,7 @@ public class DistanceAction extends ControlPanelAction { updateHausdorffDistanceInformation(); getSecondaryDrawableFace().setRenderHeatmap(isHeatmapDisplayed()); } else { + getSecondaryDrawableFace().setRenderHeatmap(false); weightedFeaturePoints.hide(); getSecondaryDrawableFace().clearHeatMapSaturation(); } 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 7f14cee8e275a139ba89f18b008486b629e2a0ed..68aec8aced6ad060ced08f1a2f90324b57517ab6 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/scene/Scene.java +++ b/GUI/src/main/java/cz/fidentis/analyst/scene/Scene.java @@ -9,6 +9,7 @@ import java.util.List; * Abstract class for ... * * @author Radek Oslejsek + * @author Dominik Racek */ public class Scene { @@ -16,6 +17,7 @@ public class Scene { private final List<DrawableFace> drawableFaces = new ArrayList<>(); private final List<DrawableFeaturePoints> drawableFeaturePoints = new ArrayList<>(); private final List<DrawablePlane> drawableSymmetryPlanes = new ArrayList<>(); + private final List<DrawablePlane> drawableCuttingPlanes = new ArrayList<>(); private final List<Drawable> otherDrawables = new ArrayList<>(); /** @@ -37,6 +39,7 @@ public class Scene { drawableFeaturePoints.add(null); } drawableSymmetryPlanes.add(null); + drawableCuttingPlanes.add(null); setDefaultColors(); } @@ -74,7 +77,10 @@ public class Scene { } drawableSymmetryPlanes.add(null); + drawableCuttingPlanes.add(null); + drawableSymmetryPlanes.add(null); + drawableCuttingPlanes.add(null); setDefaultColors(); } @@ -153,7 +159,91 @@ public class Scene { this.drawableSymmetryPlanes.set(index, sPlane); } } - + + /** + * Helper function for showing or hiding all symmetry planes + * + * @param show determines whether to hide or show the planes + */ + private void hideShowSymmetryPlanes(boolean show) { + for (int i = 0; i < drawableSymmetryPlanes.size(); ++i) { + if (drawableSymmetryPlanes.get(i) != null) { + if (show) { + drawableSymmetryPlanes.get(i).show(); + } else { + drawableSymmetryPlanes.get(i).hide(); + } + } + } + } + + /** + * Show all symmetry planes + */ + public void showSymmetryPlanes() { + hideShowSymmetryPlanes(true); + } + + /** + * Hide all symmetry planes + */ + public void hideSymmetryPlanes() { + hideShowSymmetryPlanes(false); + } + + /** + * Returns drawable cutting plane. + * + * @param index Index of the face + * @return drawable face or {@code null} + */ + public DrawablePlane getDrawableCuttingPlane(int index) { + return (index >= 0 && index < getNumFaces()) ? drawableCuttingPlanes.get(index) : null; + } + + /** + * Sets the drawable cutting plane. + * + * @param index Index of the face + * @param cPlane New cutting plane + */ + public void setDrawableCuttingPlane(int index, DrawablePlane cPlane) { + if (index >= 0 && index < getNumFaces() && cPlane != null) { + this.drawableCuttingPlanes.set(index, cPlane); + } + } + + /** + * Helper function for showing or hiding all cutting planes + * + * @param show determines whether to hide or show the planes + */ + private void hideShowCuttingPlanes(boolean show) { + for (int i = 0; i < drawableCuttingPlanes.size(); ++i) { + if (drawableCuttingPlanes.get(i) != null) { + if (show) { + drawableCuttingPlanes.get(i).show(); + } else { + drawableCuttingPlanes.get(i).hide(); + } + } + } + } + + /** + * Show all cutting planes + */ + public void showCuttingPlanes() { + hideShowCuttingPlanes(true); + } + + /** + * Hide all cutting planes + */ + public void hideCuttingPlanes() { + hideShowCuttingPlanes(false); + } + /** * Adds new drawable object to the scene. * @@ -178,6 +268,7 @@ public class Scene { ret.addAll(this.drawableFaces); ret.addAll(this.drawableFeaturePoints); ret.addAll(this.drawableSymmetryPlanes); + ret.addAll(this.drawableCuttingPlanes); ret.addAll(this.otherDrawables); while (ret.remove(null)) {} return ret; diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/PolylinePanel.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/PolylinePanel.java new file mode 100644 index 0000000000000000000000000000000000000000..720dc57650a1277bbafd73b22cf72079251bd30b --- /dev/null +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/PolylinePanel.java @@ -0,0 +1,135 @@ +package cz.fidentis.analyst.symmetry; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Stroke; +import java.awt.geom.Line2D; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import javax.swing.JPanel; +import javax.vecmath.Point3d; + + +/** + * Panel for graphing polyline profiles + * + * @author Dominik Racek + */ +public class PolylinePanel extends JPanel { + private static final int PREF_W = 500; + private static final int PREF_H = 500; + private static final int BORDER_GAP = 15; + private static final Color PRIMARY_COLOR = Color.green; + private static final Color SECONDARY_COLOR = Color.blue; + private static final Stroke GRAPH_STROKE = new BasicStroke(3f); + private List<Point3d> primary; + private List<Point3d> secondary; + + private double scale = Double.POSITIVE_INFINITY; + + /** + * Comparator for Point3d based on Y value + * + * @author Dominik Racek + */ + public class CompareY implements Comparator<Point3d> { + + /** + * Compare two Points3d objects + */ + public int compare(final Point3d a, final Point3d b) { + if (a.y < b.y) { + return -1; + } else if (a.y > b.y) { + return 1; + } else { + return 0; + } + } + } + + /** + * Constructor for one face + */ + public PolylinePanel(List<Point3d> values) { + this.primary = values; + Collections.sort(this.primary, new CompareY()); + } + + /** + * Constructor for two faces + */ + public PolylinePanel(List<Point3d> primary, List<Point3d> secondary) { + this.primary = primary; + this.secondary = secondary; + Collections.sort(this.primary, new CompareY()); + Collections.sort(this.secondary, new CompareY()); + } + + protected void drawFace(Graphics2D g2, List<Point3d> values, Color faceColor, boolean isPrimary) { + double minZ = Double.POSITIVE_INFINITY; + double maxZ = Double.NEGATIVE_INFINITY; + double minY = Double.POSITIVE_INFINITY; + double maxY = Double.NEGATIVE_INFINITY; + + for (int i = 0; i < values.size(); i++) { + if (values.get(i).z < minZ) { + minZ = values.get(i).z; + } + + if (values.get(i).z > maxZ) { + maxZ = values.get(i).z; + } + + if (values.get(i).y < minY) { + minY = values.get(i).y; + } + + if (values.get(i).y > maxY) { + maxY = values.get(i).y; + } + } + + //only calculate scale for the first face and use it for the second as well + if (isPrimary) { + scale = ((double) PREF_H - 2 * BORDER_GAP) / (maxY - minY); + } + + double offsetZ = (PREF_W * 0.7) - (maxZ * scale); + + //Draw lines + g2.setColor(faceColor); + g2.setStroke(GRAPH_STROKE); + for (int i = 0; i < values.size() - 1; i++) { + double z1 = (values.get(i).z) * scale + BORDER_GAP + offsetZ; + double y1 = (maxY - values.get(i).y) * scale + BORDER_GAP; + double z2 = (values.get(i + 1).z) * scale + BORDER_GAP + offsetZ; + double y2 = (maxY - values.get(i + 1).y) * scale + BORDER_GAP; + + g2.draw(new Line2D.Double(z1, y1, z2, y2)); + } + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D)g; + + drawFace(g2, primary, PRIMARY_COLOR, true); + + if (secondary != null) { + drawFace(g2, secondary, SECONDARY_COLOR, false); + } + + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(PREF_W, PREF_H); + } + +} \ No newline at end of file diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java new file mode 100644 index 0000000000000000000000000000000000000000..4f70c64f4dfdf503dceeede81a6a9857acf67865 --- /dev/null +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java @@ -0,0 +1,75 @@ +package cz.fidentis.analyst.symmetry; + +import cz.fidentis.analyst.canvas.Canvas; +import cz.fidentis.analyst.core.ControlPanelAction; +import cz.fidentis.analyst.visitors.mesh.CrossSection; + +import javax.swing.JTabbedPane; +import javax.swing.JToggleButton; +import java.awt.event.ActionEvent; + +/** + * Action listener for the manipulation with the cutting plane. + * + * @author Dominik Racek + */ +public class ProfilesAction extends ControlPanelAction { + + private final ProfilesPanel controlPanel; + private final JTabbedPane topControlPanel; + + /** + * Constructor. + * + * @param canvas OpenGL canvas + * @param topControlPanel Top component for placing control panels + */ + public ProfilesAction(Canvas canvas, JTabbedPane topControlPanel) { + super(canvas, topControlPanel); + this.topControlPanel = topControlPanel; + + CrossSection cs1 = new CrossSection(getScene().getDrawableCuttingPlane(0).getFacets().get(0)); + getPrimaryDrawableFace().getModel().compute(cs1); + + if (getSecondaryDrawableFace() != null) { + CrossSection cs2 = new CrossSection(getScene().getDrawableCuttingPlane(1).getFacets().get(0)); + getSecondaryDrawableFace().getModel().compute(cs2); + + controlPanel = new ProfilesPanel(this, cs1.getPoints(), cs2.getPoints()); + } else { + controlPanel = new ProfilesPanel(this, cs1.getPoints()); + } + + // 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 ProfilesPanel) { + getCanvas().getScene().setDefaultColors(); + getCanvas().getScene().showCuttingPlanes(); + } else { + getCanvas().getScene().hideCuttingPlanes(); + } + }); + getCanvas().getScene().hideCuttingPlanes(); + } + + @Override + public void actionPerformed(ActionEvent ae) { + String action = ae.getActionCommand(); + + switch (action) { + case SymmetryPanel.ACTION_COMMAND_SHOW_HIDE_PLANE: + if (((JToggleButton) ae.getSource()).isSelected()) { + getScene().showCuttingPlanes(); + } else { + getScene().hideCuttingPlanes(); + } + break; + default: + // do nothing + } + renderScene(); + } + +} diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..78c64e8e3d4d9ac8492ab35dc80824700c3be90f --- /dev/null +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java @@ -0,0 +1,140 @@ +package cz.fidentis.analyst.symmetry; + +import cz.fidentis.analyst.core.ControlPanel; +import cz.fidentis.analyst.core.ControlPanelBuilder; +import org.openide.windows.WindowManager; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; +import javax.swing.ImageIcon; +import javax.swing.JCheckBox; +import javax.swing.JOptionPane; +import javax.swing.JTextField; +import javax.vecmath.Point3d; + +/** + * Control panel for Polyline profiles and cutting planes + * + * @author Dominik Racek + */ +public class ProfilesPanel extends ControlPanel { + + /* + * GUI primitives holding the configuration state: + */ + private JTextField cuttingOffset = null; + private JCheckBox allignFaces; + private JCheckBox showCuttingPlane; + + /* + * Handled actions + */ + public static final String ACTION_COMMAND_SHOW_HIDE_PLANE = "show-hide symmetry plane"; + public static final String ACTION_COMMAND_RECOMPUTE = "recompute"; + + /* + * Mandatory design elements + */ + public static final String ICON = "profiles28x28.png"; + public static final String NAME = "Profiles"; + + /** + * Constructor for one face + */ + public ProfilesPanel(ActionListener action, List<Point3d> values) { + setName(NAME); + + ControlPanelBuilder builder = new ControlPanelBuilder(this); + + builder.addPolylinePanel(new PolylinePanel(values)); + builder.addLine(); + + buildPanel(action, builder); + } + + /** + * Constructor for two faces + */ + public ProfilesPanel(ActionListener action, List<Point3d> primary, List<Point3d> secondary) { + setName(NAME); + + ControlPanelBuilder builder = new ControlPanelBuilder(this); + + builder.addPolylinePanel(new PolylinePanel(primary, secondary)); + builder.addLine(); + + buildPanel(action, builder); + } + + private void buildPanel(ActionListener action, ControlPanelBuilder builder) { + builder.addCaptionLine("Computation options:"); + builder.addLine(); + + cuttingOffset = builder.addSliderOptionLine( + (ActionEvent e) -> { + showCuttingOffsetHelp(); + }, + "Cutting Plane Offset", + -1, + (ActionEvent e) -> { + + }); + builder.addLine(); + + allignFaces = builder.addCheckBoxOptionLine( + null, + "Allign faces", + true, + null + ); + builder.addLine(); + + builder.addCaptionLine("Computation options:"); + builder.addLine(); + + showCuttingPlane = builder.addCheckBoxOptionLine( + null, + "Show cutting plane", + true, + createListener(action, ACTION_COMMAND_SHOW_HIDE_PLANE) + ); + builder.addLine(); + + builder.addButtons( + List.of("Recompute", + "Export Profile"), + List.of( + (ActionEvent e) -> { + + }, + (ActionEvent e) -> { + + } + ) + ); + } + + @Override + public ImageIcon getIcon() { + return getStaticIcon(); + } + + /** + * Static implementation of the {@link #getIcon()} method. + * + * @return Control panel icon + */ + public static ImageIcon getStaticIcon() { + return new ImageIcon(ProfilesPanel.class.getClassLoader().getResource("/" + ICON)); + } + + private void showCuttingOffsetHelp() { + JOptionPane.showMessageDialog(WindowManager.getDefault().findTopComponent("Cutting Plane Offset"), + "TO DO", + "TO DO", + 0, + new ImageIcon(getClass().getResource("/distance.png")) + ); + } + +} 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 f48946047f820e7b836aef2f1a040ee952cef520..4ac7291021227e176f5037593dc264b4127050df 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java @@ -2,8 +2,8 @@ package cz.fidentis.analyst.symmetry; import cz.fidentis.analyst.canvas.Canvas; import cz.fidentis.analyst.core.ControlPanelAction; -import cz.fidentis.analyst.core.OutputWindow; import cz.fidentis.analyst.scene.DrawablePlane; + import java.awt.event.ActionEvent; import javax.swing.JTabbedPane; import javax.swing.JToggleButton; @@ -16,6 +16,7 @@ import javax.swing.JToggleButton; public class SymmetryAction extends ControlPanelAction { private final SymmetryPanel controlPanel; + private final JTabbedPane topControlPanel; /** * Constructor. @@ -25,50 +26,71 @@ public class SymmetryAction extends ControlPanelAction { */ public SymmetryAction(Canvas canvas, JTabbedPane topControlPanel) { super(canvas, topControlPanel); + this.topControlPanel = topControlPanel; this.controlPanel = new SymmetryPanel(this); + + // 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(); + + if (controlPanel.showSymmetryPlane.isSelected()) { + getCanvas().getScene().showSymmetryPlanes(); + } + } else { + getCanvas().getScene().hideSymmetryPlanes(); + } + }); + + recomputeSymmetryPlane(); } @Override public void actionPerformed(ActionEvent ae) { String action = ae.getActionCommand(); - DrawablePlane plane; switch (action) { case SymmetryPanel.ACTION_COMMAND_SHOW_HIDE_PANEL: hideShowPanelActionPerformed(ae, this.controlPanel); break; case SymmetryPanel.ACTION_COMMAND_SHOW_HIDE_PLANE: - plane = getCanvas().getScene().getDrawableSymmetryPlane(0); - if (plane == null) { // no plane compute so far - break; - } if (((JToggleButton) ae.getSource()).isSelected()) { - plane.show(); + getScene().showSymmetryPlanes(); } else { - plane.hide(); + getScene().hideSymmetryPlanes(); } break; case SymmetryPanel.ACTION_COMMAND_RECOMPUTE: recomputeSymmetryPlane(); break; default: - // to nothing + // do nothing } renderScene(); } private void recomputeSymmetryPlane() { - OutputWindow out = OutputWindow.measureTime(); - - SymmetryEstimator se = new SymmetryEstimator(controlPanel.getSymmetryConfig()); - getPrimaryDrawableFace().getModel().compute(se); - DrawablePlane plane = new DrawablePlane(se.getSymmetryPlaneMesh(), se.getSymmetryPlane()); - getCanvas().getScene().setDrawableSymmetryPlane(0, plane); - - out.printDuration("Computation of Hausdorff distance for models with " - + getPrimaryDrawableFace().getHumanFace().getMeshModel().getNumVertices() - + " vertices" - ); + SymmetryEstimator primaryEstimator = new SymmetryEstimator(controlPanel.getSymmetryConfig()); + getPrimaryDrawableFace().getModel().compute(primaryEstimator); + + getCanvas().getScene().setDrawableSymmetryPlane(0, + new DrawablePlane(primaryEstimator.getSymmetryPlaneMesh(), primaryEstimator.getSymmetryPlane())); + getCanvas().getScene().setDrawableCuttingPlane(0, + new DrawablePlane(primaryEstimator.getSymmetryPlaneMesh(), primaryEstimator.getSymmetryPlane())); + + if (getSecondaryDrawableFace() != null) { + SymmetryEstimator secondaryEstimator = new SymmetryEstimator(controlPanel.getSymmetryConfig()); + getSecondaryDrawableFace().getModel().compute(secondaryEstimator); + + getCanvas().getScene().setDrawableSymmetryPlane(1, + new DrawablePlane(secondaryEstimator.getSymmetryPlaneMesh(), secondaryEstimator.getSymmetryPlane())); + getCanvas().getScene().setDrawableCuttingPlane(1, + new DrawablePlane(secondaryEstimator.getSymmetryPlaneMesh(), secondaryEstimator.getSymmetryPlane())); + } + + getCanvas().getScene().hideSymmetryPlanes(); } } 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 647e4d909217937c0c14e06826dda0141cbde814..7bb0fae215be41deee022487200e2ed2477d83ce 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java @@ -10,6 +10,7 @@ import javax.swing.JCheckBox; import javax.swing.JOptionPane; import javax.swing.JTextField; import javax.swing.JToggleButton; + import org.openide.windows.WindowManager; /** @@ -52,7 +53,7 @@ public final class SymmetryPanel extends ControlPanel { private JTextField minNormAngleCosTF = null; private JTextField maxRelDistTF = null; private final JCheckBox averaging; - private final JCheckBox showSymmetryPlane; + public final JCheckBox showSymmetryPlane; /** * Constructor. @@ -61,9 +62,9 @@ public final class SymmetryPanel extends ControlPanel { */ public SymmetryPanel(ActionListener action) { this.setName(NAME); - + ControlPanelBuilder builder = new ControlPanelBuilder(this); - + builder.addCaptionLine("Computation options:"); builder.addLine(); @@ -71,7 +72,7 @@ public final class SymmetryPanel extends ControlPanel { (ActionEvent e) -> { showSignPointsHelp(); }, - "Significant points", + "Significant points", MAX_SIGNIFICANT_POINTS, (ActionEvent e) -> { tempConfig.setSignificantPointCount(ControlPanelBuilder.parseLocaleInt(signPointsTF)); @@ -83,7 +84,7 @@ public final class SymmetryPanel extends ControlPanel { (ActionEvent e) -> { showMinCurvHelp(); }, - "Min. curvature ratio", + "Min. curvature ratio", -1, (ActionEvent e) -> { tempConfig.setMinCurvRatio(ControlPanelBuilder.parseLocaleDouble(minCurvatureTF)); @@ -94,7 +95,7 @@ public final class SymmetryPanel extends ControlPanel { (ActionEvent e) -> { showMinAngleCosHelp(); }, - "Min. angle cosine", + "Min. angle cosine", -1, (ActionEvent e) -> { tempConfig.setMinAngleCos(ControlPanelBuilder.parseLocaleDouble(minAngleCosTF)); @@ -105,7 +106,7 @@ public final class SymmetryPanel extends ControlPanel { (ActionEvent e) -> { showNormalAngleHelp(); }, - "Normal angle", + "Normal angle", -1, (ActionEvent e) -> { tempConfig.setMinNormAngleCos(ControlPanelBuilder.parseLocaleDouble(minNormAngleCosTF)); @@ -116,8 +117,8 @@ public final class SymmetryPanel extends ControlPanel { (ActionEvent e) -> { showRelDistHelp(); }, - "Relative distance", - -1, + "Relative distance", + -1, (ActionEvent e) -> { tempConfig.setMaxRelDistance(ControlPanelBuilder.parseLocaleDouble(maxRelDistTF)); }); @@ -133,18 +134,18 @@ public final class SymmetryPanel extends ControlPanel { tempConfig.setAveraging(((JToggleButton) e.getSource()).isSelected()); }); builder.addLine(); - - builder.addCaptionLine("Visualization options:"); + + builder.addCaptionLine("Computation options:"); builder.addLine(); showSymmetryPlane = builder.addCheckBoxOptionLine( - null, - "Show symmetry plane", - true, + null, + "Show symmetry plane", + true, createListener(action, ACTION_COMMAND_SHOW_HIDE_PLANE) ); builder.addLine(); - + builder.addButtons( List.of("Reset changes", "Reset to defaults", @@ -171,7 +172,7 @@ public final class SymmetryPanel extends ControlPanel { } ) ); - + setTempConfig(new SymmetryConfig()); // intialize values; } diff --git a/GUI/src/main/java/cz/fidentis/analyst/toolbar/FaceToFaceToolBar.java b/GUI/src/main/java/cz/fidentis/analyst/toolbar/FaceToFaceToolBar.java index 337bfc75fc04bfa128f9244e2fe78541f47b9598..d113cc9fd7f9fcf709736526616f010111e1985c 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/toolbar/FaceToFaceToolBar.java +++ b/GUI/src/main/java/cz/fidentis/analyst/toolbar/FaceToFaceToolBar.java @@ -3,6 +3,9 @@ package cz.fidentis.analyst.toolbar; import cz.fidentis.analyst.canvas.Canvas; import cz.fidentis.analyst.distance.DistanceAction; import cz.fidentis.analyst.registration.RegistrationAction; +import cz.fidentis.analyst.symmetry.ProfilesAction; +import cz.fidentis.analyst.symmetry.SymmetryAction; + import javax.swing.JTabbedPane; /** @@ -23,6 +26,7 @@ public class FaceToFaceToolBar extends RenderingToolBar { super(canvas); addPrimaryFaceButton(); addSecondaryFaceButton(); + // (Re)render scene after all change listeners have been called // (the first added listener is called last) @@ -32,5 +36,9 @@ public class FaceToFaceToolBar extends RenderingToolBar { new DistanceAction(getCanvas(), controlPanel); // Add registration panel to the control panel new RegistrationAction(getCanvas(), controlPanel); + // Add symmetry panel to the control panel + new SymmetryAction(getCanvas(), controlPanel); + // Add profiles panel to the control panel + new ProfilesAction(getCanvas(), controlPanel); } } diff --git a/GUI/src/main/java/cz/fidentis/analyst/toolbar/RenderingModeAction.java b/GUI/src/main/java/cz/fidentis/analyst/toolbar/RenderingModeAction.java index 3d4d2c473e9c09278a65d2766a90e13c39454860..8bb8807f28631a72d02a4d373570120fb7dd9dab 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/toolbar/RenderingModeAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/toolbar/RenderingModeAction.java @@ -5,6 +5,8 @@ import cz.fidentis.analyst.canvas.Canvas; import static cz.fidentis.analyst.toolbar.RenderingToolBar.REFLECTIONS_COLOR; import cz.fidentis.analyst.scene.Drawable; import cz.fidentis.analyst.scene.DrawableFeaturePoints; +import cz.fidentis.analyst.scene.DrawablePlane; + import java.awt.Color; import java.awt.event.ActionEvent; import javax.swing.AbstractAction; @@ -71,10 +73,12 @@ public class RenderingModeAction extends AbstractAction { break; case ACTION_COMMAND_SHOW_HIDE_PRIMARY_FACE: showHideFace(0, ((JToggleButton) ae.getSource()).isSelected()); + showHideSymmetryPlane(0, ((JToggleButton) ae.getSource()).isSelected()); break; case ACTION_COMMAND_SHOW_HIDE_SECONDARY_FACES: for (int i = 1; i < canvas.getScene().getNumFaces(); i++) { showHideFace(i, ((JToggleButton) ae.getSource()).isSelected()); + showHideSymmetryPlane(i, ((JToggleButton) ae.getSource()).isSelected()); } break; case ACTION_COMMAND_SHOW_HIDE_FEATURE_POINTS: @@ -109,4 +113,17 @@ public class RenderingModeAction extends AbstractAction { } } } + + private void showHideSymmetryPlane(int i, boolean show) { + DrawablePlane plane = canvas.getScene().getDrawableSymmetryPlane(i); + if (plane == null) { + return; + } + + if (show) { + plane.show(); + } else { //hide + plane.hide(); + } + } } diff --git a/GUI/src/main/java/cz/fidentis/analyst/toolbar/SingleFaceToolBar.java b/GUI/src/main/java/cz/fidentis/analyst/toolbar/SingleFaceToolBar.java index bf42881da1c3a1f1ed2a1a8733b145ccc7220405..c971f83fb17683dd223eb6ce4b1a7d1406581e2e 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/toolbar/SingleFaceToolBar.java +++ b/GUI/src/main/java/cz/fidentis/analyst/toolbar/SingleFaceToolBar.java @@ -1,14 +1,10 @@ package cz.fidentis.analyst.toolbar; +import cz.fidentis.analyst.symmetry.ProfilesAction; import cz.fidentis.analyst.symmetry.SymmetryAction; import cz.fidentis.analyst.canvas.Canvas; -import cz.fidentis.analyst.core.ControlPanel; import cz.fidentis.analyst.curvature.CurvatureAction; -import cz.fidentis.analyst.curvature.CurvaturePanel; -import cz.fidentis.analyst.symmetry.SymmetryPanel; import javax.swing.JTabbedPane; -import javax.swing.JToggleButton; -import org.openide.util.NbBundle; /** * A toolbar extension for a single face analysis. @@ -26,26 +22,17 @@ public class SingleFaceToolBar extends RenderingToolBar { public SingleFaceToolBar(Canvas canvas, JTabbedPane controlPanel) { super(canvas); addPrimaryFaceButton(); - addSeparator(); - addSymmetryPlaneButton(controlPanel); - addCurvatureButton(controlPanel); - } - - private void addSymmetryPlaneButton(JTabbedPane controlPanel) { - JToggleButton button = new JToggleButton(); - button.addActionListener(new SymmetryAction(getCanvas(), controlPanel)); - button.setActionCommand(ControlPanel.ACTION_COMMAND_SHOW_HIDE_PANEL); - button.setIcon(SymmetryPanel.getStaticIcon()); - button.setToolTipText(NbBundle.getMessage(RenderingToolBar.class, "SingleFaceToolBar.symmetry.text")); - add(button); - } - - private void addCurvatureButton(JTabbedPane topControlPanel) { - JToggleButton button = new JToggleButton(); - button.addActionListener(new CurvatureAction(getCanvas(), topControlPanel)); - button.setActionCommand(ControlPanel.ACTION_COMMAND_SHOW_HIDE_PANEL); - button.setIcon(CurvaturePanel.getStaticIcon()); - button.setToolTipText(NbBundle.getMessage(RenderingToolBar.class, "SingleFaceToolBar.curvature.text")); - add(button); + + // (Re)render scene after all change listeners have been called + // (the first added listener is called last) + controlPanel.addChangeListener(e -> getCanvas().renderScene()); + + // Add distance panel to the control panel + new CurvatureAction(getCanvas(), controlPanel); + // Add symmetry panel to the control panel + new SymmetryAction(getCanvas(), controlPanel); + // Add profiles panel to the control panel + new ProfilesAction(getCanvas(), controlPanel); + } } diff --git a/GUI/src/main/resources/profiles28x28.png b/GUI/src/main/resources/profiles28x28.png new file mode 100644 index 0000000000000000000000000000000000000000..39bd827077e8cecc32b065f5a7f2eb0c6afefdb8 Binary files /dev/null and b/GUI/src/main/resources/profiles28x28.png differ diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshFacetImpl.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshFacetImpl.java index 5e83a7083cd0b19a5d0615799bc1275cfa4fbbf7..c733928b3799aa32bb1b4830149dfe9beedd7946 100644 --- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshFacetImpl.java +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshFacetImpl.java @@ -3,12 +3,12 @@ package cz.fidentis.analyst.mesh.core; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import javax.vecmath.Vector3d; import cz.fidentis.analyst.mesh.MeshVisitor; -import java.util.Iterator; -import java.util.NoSuchElementException; import javax.vecmath.Point3d; /** @@ -289,7 +289,5 @@ public class MeshFacetImpl implements MeshFacet { this.vertices = newVertices; return true; } - - } diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshTriangle.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshTriangle.java index 3e0328d96ec801a9583918c2cfc9fdb79aa92671..31ef4cde576946a4072c53a335c19bb397417697 100644 --- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshTriangle.java +++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshTriangle.java @@ -397,4 +397,43 @@ public class MeshTriangle implements Iterable<MeshPoint> { e.sub(p2.getPosition()); return e; } + + /** + * Checks whether the triangle is intersected by a cutting plane + * + * @param plane cutting plane + * @return true if the triangle is intersected by the plane + */ + public boolean checkIntersectionWithPlane(MeshFacet plane) { + Point3d min = getVertex1(), max = getVertex1(); + + if (getVertex2().x < min.x) { + min = getVertex2(); + } + + if (getVertex3().x < min.x) { + min = getVertex3(); + } + + if (getVertex2().x > max.x) { + max = getVertex2(); + } + + if (getVertex3().x > max.x) { + max = getVertex3(); + } + + Point3d projMin = plane.getTriangles().get(0).getProjectionToTrianglePlane(min); + Point3d projMax = plane.getTriangles().get(0).getProjectionToTrianglePlane(max); + + if (projMin == null) { + projMin = plane.getTriangles().get(1).getProjectionToTrianglePlane(min); + } + + if (projMax == null) { + projMax = plane.getTriangles().get(1).getProjectionToTrianglePlane(max); + } + + return (min.x < projMin.x && max.x > projMax.x); + } }