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 index 9d17a4a530733bc0f22f8dbcd179b43b3b9db2f2..372081f6965e898a409d88b8962c69724026613d 100644 --- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSection.java +++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSection.java @@ -33,9 +33,20 @@ public class CrossSection extends MeshVisitor { 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) { + Point3d last = null; for (MeshTriangle tri : facet) { if (tri.checkIntersectionWithPlane(plane)) { - points.add(facet.getVertex(tri.index1).getPosition()); + Point3d p = facet.getVertex(tri.index1).getPosition(); + + // Check for duplicates + if (last != null) { + if (last == p) { + continue; + } + } + + points.add(p); + last = p; } } } 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 4f70c64f4dfdf503dceeede81a6a9857acf67865..79f57fccecb381b0beddad2a9976d5a483d922bb 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java @@ -3,10 +3,17 @@ 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 org.openide.filesystems.FileChooserBuilder; import javax.swing.JTabbedPane; import javax.swing.JToggleButton; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.vecmath.Point3d; import java.awt.event.ActionEvent; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; /** * Action listener for the manipulation with the cutting plane. @@ -15,9 +22,18 @@ import java.awt.event.ActionEvent; */ public class ProfilesAction extends ControlPanelAction { + /* + * GUI elements + */ private final ProfilesPanel controlPanel; private final JTabbedPane topControlPanel; + /* + * Calculated profiles + */ + private List<Point3d> primaryPoints; + private List<Point3d> secondaryPoints; + /** * Constructor. * @@ -30,14 +46,16 @@ public class ProfilesAction extends ControlPanelAction { CrossSection cs1 = new CrossSection(getScene().getDrawableCuttingPlane(0).getFacets().get(0)); getPrimaryDrawableFace().getModel().compute(cs1); + this.primaryPoints = cs1.getPoints(); if (getSecondaryDrawableFace() != null) { CrossSection cs2 = new CrossSection(getScene().getDrawableCuttingPlane(1).getFacets().get(0)); getSecondaryDrawableFace().getModel().compute(cs2); + this.secondaryPoints = cs2.getPoints(); - controlPanel = new ProfilesPanel(this, cs1.getPoints(), cs2.getPoints()); + controlPanel = new ProfilesPanel(this, this.primaryPoints, this.secondaryPoints); } else { - controlPanel = new ProfilesPanel(this, cs1.getPoints()); + controlPanel = new ProfilesPanel(this, this.primaryPoints); } // Place control panel to the topControlPanel @@ -54,18 +72,60 @@ public class ProfilesAction extends ControlPanelAction { getCanvas().getScene().hideCuttingPlanes(); } + private void exportProfile(List<Point3d> points, String title) { + File file = new FileChooserBuilder(ProfilesAction.class) + .setTitle(title) + .setDefaultWorkingDirectory(new File(System.getProperty("user.home"))) + .setFilesOnly(true) + .setFileFilter(new FileNameExtensionFilter("csv files (*.csv)", "csv")) + .setAcceptAllFileFilterUsed(true) + .showSaveDialog(); + + if (file == null) { + return; + } + + // If chosen file exists, use the exact file path + // If chosen file does not exist and does not have an extension, add it + if (!file.exists()) { + if (!file.getAbsolutePath().endsWith(".csv")) { + file = new File(file.getAbsolutePath() + ".csv"); + } + } + + try { + file.createNewFile(); + + PrintWriter writer = new PrintWriter(file.getAbsoluteFile(), "UTF-8"); + writer.println("N,X-Coordinate,Z-Coordinate"); + for (int i = 0; i < points.size(); ++i) { + writer.println((i+1) + "," + points.get(i).x + "," + points.get(i).z); + } + + writer.close(); + } catch (IOException ex) { + System.out.println("ERROR writing to a file: " + ex); + } + } + @Override public void actionPerformed(ActionEvent ae) { String action = ae.getActionCommand(); switch (action) { - case SymmetryPanel.ACTION_COMMAND_SHOW_HIDE_PLANE: + case ProfilesPanel.ACTION_COMMAND_SHOW_HIDE_PLANE: if (((JToggleButton) ae.getSource()).isSelected()) { getScene().showCuttingPlanes(); } else { getScene().hideCuttingPlanes(); } break; + case ProfilesPanel.ACTION_COMMAND_EXPORT: + exportProfile(this.primaryPoints, "Export primary face profile to file"); + if (getSecondaryDrawableFace() != null) { + exportProfile(this.secondaryPoints, "Export secondary face profile to file"); + } + break; default: // do nothing } diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java index 78c64e8e3d4d9ac8492ab35dc80824700c3be90f..b44821a7477c1529f98e7ef1d9bd0e07e0a63c06 100644 --- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java +++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java @@ -31,6 +31,7 @@ public class ProfilesPanel extends ControlPanel { */ public static final String ACTION_COMMAND_SHOW_HIDE_PLANE = "show-hide symmetry plane"; public static final String ACTION_COMMAND_RECOMPUTE = "recompute"; + public static final String ACTION_COMMAND_EXPORT = "export"; /* * Mandatory design elements @@ -102,13 +103,17 @@ public class ProfilesPanel extends ControlPanel { builder.addButtons( List.of("Recompute", - "Export Profile"), + "Export Profiles"), List.of( (ActionEvent e) -> { }, (ActionEvent e) -> { - + action.actionPerformed(new ActionEvent( + e.getSource(), + ActionEvent.ACTION_PERFORMED, + ACTION_COMMAND_EXPORT + )); } ) );