Loading GUI/src/main/java/cz/fidentis/analyst/gui/task/batch/symmetry/BatchCurveRenderingPanel.java +0 −4 Original line number Diff line number Diff line Loading @@ -202,10 +202,6 @@ public class BatchCurveRenderingPanel extends JPanel { g2.setColor(Color.GRAY); for (Line2D line : normalVectors) { double length = Math.sqrt(Math.pow(line.getX1() - line.getX2(), 2) + Math.pow(line.getY1() - line.getY2(), 2)); if (length > 28) { continue; } double x1 = line.getX1() * scale + offsetX; double y1 = line.getY1() * scale + offsetY; // This calculation is required to keep the normal vector sizes Loading GUI/src/main/java/cz/fidentis/analyst/gui/task/batch/symmetry/BatchCuttingPlanesAction.java +53 −110 Original line number Diff line number Diff line Loading @@ -5,35 +5,40 @@ package cz.fidentis.analyst.gui.task.batch.symmetry; import cz.fidentis.analyst.canvas.Canvas; import cz.fidentis.analyst.drawables.DrawableCuttingPlane; import cz.fidentis.analyst.gui.task.ControlPanelAction; import cz.fidentis.analyst.events.HumanFaceEvent; import cz.fidentis.analyst.events.HumanFaceListener; import cz.fidentis.analyst.events.HumanFaceTransformedEvent; import cz.fidentis.analyst.drawables.DrawableCuttingPlane; import cz.fidentis.analyst.face.HumanFace; import cz.fidentis.analyst.gui.elements.PlaneManipulationPanel; import cz.fidentis.analyst.gui.task.ControlPanelAction; import cz.fidentis.analyst.gui.task.batch.FacesProxyDecorator; import cz.fidentis.analyst.shapes.Curve2d; import cz.fidentis.analyst.symmetry.CuttingPlanesUtils; import org.json.simple.JSONObject; import cz.fidentis.analyst.symmetry.CuttingPlanesUtils.CuttingPlaneConfig; import org.openide.filesystems.FileChooserBuilder; import javax.swing.*; import javax.swing.JTabbedPane; import javax.swing.filechooser.FileNameExtensionFilter; import javax.vecmath.Point2d; import javax.vecmath.Vector3d; import java.awt.event.ActionEvent; import java.awt.geom.Line2D; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.function.DoubleFunction; import java.util.stream.Collectors; import javax.vecmath.Point2d; import javax.vecmath.Vector3d; import org.json.simple.JSONObject; import static cz.fidentis.analyst.symmetry.CuttingPlanesUtils.*; import java.awt.geom.Line2D; import java.util.Comparator; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; /** * Action listener for manipulation with cutting planes. Loading Loading @@ -69,13 +74,17 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla * A new {@code ProfilesPanel} is instantiated and added to the {@code topControlPane} * * @param canvas OpenGL canvas * @param batchFacesProxy A proxy of all faces * @param faces Faces processed by current analytical task * @param topControlPane A top component when a new control panel is placed */ public BatchCuttingPlanesAction(Canvas canvas, FacesProxyDecorator batchFacesProxy, JTabbedPane topControlPane) { super(canvas, batchFacesProxy.getFacesProxy(), topControlPane); setControlPanel(new BatchCuttingPlanesPanel(this, batchFacesProxy)); /* if (getSecondaryDrawableFace() == null) { getControlPanel().hideHausdorffDistance(); }*/ getControlPanel().getCurveRenderingPanel().setFaceBoundingBox(getPrimaryFace().getBoundingBox()); createDefaultPlanes(); Loading @@ -87,6 +96,8 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla getScene().clearCuttingPlanes(); createDefaultPlanes(); getControlPanel().getCurveRenderingPanel().setFaceBoundingBox(getPrimaryFace().getBoundingBox()); //computeHausdorffDistances(getControlPanel().getCurveRenderingPanel().get2DAverageCurves(), //getControlPanel().getCurveRenderingPanel().get2DAverageCurves()); humanFaceChanged = false; } if (getControlPanel().getFacesProxyDecorator().haveAvgFace() && (lastAvgFace == null || !lastAvgFace.equals(getControlPanel().getFacesProxyDecorator().getAvgFace()))) { Loading Loading @@ -123,10 +134,16 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla exportConfiguration(); exportCurve(getControlPanel().getCurveRenderingPanel().get2DCurves().get(0), "Export primary face cross-section curve to file"); // if (getControlPanel().isMirrorCutsChecked()) { // exportProfile(this.primaryMirrorCurve, "Export primary face mirror profile to file"); // } if (getSecondaryDrawableFace() != null) { exportCurve(getControlPanel().getCurveRenderingPanel().get2DCurves().get(0), "Export secondary face cross-section curve to file"); // if (getControlPanel().isMirrorCutsChecked()) { // exportProfile(this.secondaryMirrorCurve, "Export secondary face mirror profile to file"); // } } break; Loading @@ -149,6 +166,8 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla if (getSecondaryDrawableFace() == null) { return; } //computeHausdorffDistances(getControlPanel().getCurveRenderingPanel().get2DAverageCurves(), // getControlPanel().getCurveRenderingPanel().get2DAverageCurves()); break; case BatchCuttingPlanesPanel.ACTION_INITIALIZE_MANUPULATION_PANEL: createPlane(); Loading @@ -158,10 +177,6 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla break; case BatchCuttingPlanesPanel.ACTION_COMMAND_SHOW_SELECTED_FACE: showSelectedFace(); if (getControlPanel().getSelectedFace() != null) { getControlPanel().getCurveRenderingPanel().setFaceBoundingBox(getControlPanel().getFacesProxyDecorator().haveAvgFace() ? getControlPanel().getFacesProxyDecorator().getAvgFace().getBoundingBox() : getControlPanel().getSelectedFace().getBoundingBox()); getControlPanel().getCurveRenderingPanel().setCurrentCuttingPlane(getControlPanel().getCurveRenderingPanel().getCurrentCuttingPlane()); } computeCrossSections(getCuttingPlanes(getControlPanel().getCurveRenderingPanel().getCurrentCuttingPlane())); break; default: Loading Loading @@ -229,7 +244,7 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla try { file.createNewFile(); try (PrintWriter writer = new PrintWriter(file.getAbsoluteFile(), StandardCharsets.UTF_8)) { try (PrintWriter writer = new PrintWriter(file.getAbsoluteFile(), "UTF-8")) { writer.print(configuration.toJSONString()); } } catch (IOException ex) { Loading @@ -247,7 +262,7 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla try { file.createNewFile(); try (PrintWriter writer = new PrintWriter(file.getAbsoluteFile(), StandardCharsets.UTF_8)) { try (PrintWriter writer = new PrintWriter(file.getAbsoluteFile(), "UTF-8")) { List<Point2d> points = curve.getCurveSegments().stream() .flatMap(Collection::stream) .toList(); Loading Loading @@ -314,17 +329,15 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla List<List<Curve2d>> curvesOfSelectedFace = CuttingPlanesUtils.computeCrossSections(crossSectionPlanes, getControlPanel().getSelectedFace(), null, FEATURE_POINTS_CLOSENESS); List<List<Curve2d>> curvesOfFaces = new ArrayList<>(); List<HumanFace> faceList = new ArrayList<>(); for (int i = 0; i < getControlPanel().getFacesProxyDecorator().getNumFaces(); i++) { curvesOfFaces.add(CuttingPlanesUtils.computeCrossSections(crossSectionPlanes, getControlPanel().getFacesProxyDecorator().getFace(i), null, FEATURE_POINTS_CLOSENESS).get(0)); faceList.add(getControlPanel().getFacesProxyDecorator().getFace(i)); } getControlPanel().getCurveRenderingPanel().setCurves(calculateDeviation(curvesOfSelectedFace.get(0), curvesOfFaces, faceList)); getControlPanel().getCurveRenderingPanel().setCurves(findNearestNeighbors(curvesOfSelectedFace.get(0), curvesOfFaces)); } private List<Curve2d> calculateDeviation(List<Curve2d> avg, List<List<Curve2d>> faceCurves, List<HumanFace> faceList) { private List<Curve2d> findNearestNeighbors(List<Curve2d> avg, List<List<Curve2d>> faces) { List<Curve2d> reducedCurves = new ArrayList<>(); for (int i = 0; i < avg.size(); i++) { if (avg.get(i) == null) { Loading @@ -333,7 +346,7 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla Curve2d avgCurve = avg.get(i).subSample(sampling); reducedCurves.add(avgCurve); List<Curve2d> curves = new ArrayList<>(); for (List<Curve2d> curvesOfFace : faceCurves) { for (List<Curve2d> curvesOfFace : faces) { curves.add(curvesOfFace.get(i)); } Loading @@ -343,36 +356,27 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla int curCounter = counter.getAndIncrement(); if (curCounter < avgCurveSegment.size() - 1) { // Find nearest neighbors in parallel List<Point2d> nearestNeighbors; List<Point2d> nearestNeighbors = curves.parallelStream() .map(curve -> curve.getCurveSegments().stream() .flatMap(Collection::stream) .min(Comparator.comparingDouble(point -> Math.sqrt(Math.pow((point.x - avgPoint.x), 2) + Math.pow((point.y - avgPoint.y), 2)))) .orElse(null)) .filter(Objects::nonNull) .collect(Collectors.toList()); Line2D normal = avgCurve.getNormals().get(curCounter); double maxDistance; double minDistance; if (getControlPanel().getFacesProxyDecorator().haveAvgFace()) { nearestNeighbors = findNearestNeighbors(curves, avgPoint); double[] distances = calculateMinMaxDistances(nearestNeighbors, avgPoint, normal, Arrays.asList(1.0, 0.0, 0.0)); maxDistance = distances[0]; minDistance = distances[1]; } else { BoundingBox2D avgBox2D = getBoundingBox2D(getControlPanel().getCurveRenderingPanel().getPreferredSize().width, getControlPanel().getCurveRenderingPanel().getPreferredSize().height, getControlPanel().getSelectedFace().getBoundingBox(), getControlPanel().getCurveRenderingPanel().getCurrentCuttingPlane()); double selScale = avgBox2D.scale(); double selOffsetX = avgBox2D.offsetX(); double selOffsetY = avgBox2D.offsetY(); nearestNeighbors = findNearestNeighbors(curves, avgPoint, faceList, Arrays.asList(selScale, selOffsetX, selOffsetY)); double maxDistance = Double.MIN_VALUE; double minDistance = Double.MAX_VALUE; for (Point2d p : nearestNeighbors) { double dx = p.x - avgPoint.x; double dy = p.y - avgPoint.y; double relativeDistance = dx * normal.getX2() + dy * normal.getY2(); double[] distances = calculateMinMaxDistances(nearestNeighbors, avgPoint, normal, Arrays.asList(selScale, selOffsetX, selOffsetY)); maxDistance = distances[0]; minDistance = distances[1]; maxDistance = relativeDistance > maxDistance ? relativeDistance : maxDistance; minDistance = relativeDistance < minDistance ? relativeDistance : minDistance; } normal.setLine( normal.getX1() + (normal.getX2() * minDistance), normal.getY1() + (normal.getY2() * minDistance), Loading @@ -388,71 +392,10 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla return reducedCurves; } private List<Point2d> findNearestNeighbors(List<Curve2d> curves, Point2d avgPoint) { return curves.parallelStream() .map(curve -> curve.getCurveSegments().stream() .flatMap(Collection::stream) .min(Comparator.comparingDouble(point -> Math.pow((point.x - avgPoint.x), 2) + Math.pow((point.y - avgPoint.y), 2))) .orElse(null)) .filter(Objects::nonNull) .collect(Collectors.toList()); } private List<Point2d> findNearestNeighbors(List<Curve2d> curves, Point2d avgPoint, List<HumanFace> faceList, List<Double> scaleOffset) { List<Point2d> nearestNeighbors = new ArrayList<>(); double selScale = scaleOffset.get(0); double selOffsetX = scaleOffset.get(1); double selOffsetY = scaleOffset.get(2); Point2d transformedAvgPoint = new Point2d(avgPoint.x * selScale + selOffsetX, avgPoint.y * selScale + selOffsetY); for (int faceIndex = 0; faceIndex < getControlPanel().getFacesProxyDecorator().getNumFaces(); faceIndex++) { BoundingBox2D box2D = getBoundingBox2D(getControlPanel().getCurveRenderingPanel().getPreferredSize().width, getControlPanel().getCurveRenderingPanel().getPreferredSize().height, faceList.get(faceIndex).getBoundingBox(), getControlPanel().getCurveRenderingPanel().getCurrentCuttingPlane()); double scale = box2D.scale(); double offsetX = box2D.offsetX(); double offsetY = box2D.offsetY(); Curve2d curve = curves.get(faceIndex); Point2d nearestPoint = curve.getCurveSegments().parallelStream() .flatMap(Collection::stream) .min(Comparator.comparingDouble(point -> { double dx = (point.x * scale + offsetX) - transformedAvgPoint.x; double dy = (point.y * scale + offsetY) - transformedAvgPoint.y; return dx * dx + dy * dy; // Compare squared distances })) .orElse(null); if (nearestPoint != null) { nearestNeighbors.add(new Point2d(nearestPoint.x * scale + offsetX, nearestPoint.y * scale + offsetY)); } } return nearestNeighbors; } private double[] calculateMinMaxDistances(List<Point2d> nearestNeighbors, Point2d avgPoint, Line2D normal, List<Double> scaleOffset) { double selScale = scaleOffset.get(0); double selOffsetX = scaleOffset.get(1); double selOffsetY = scaleOffset.get(2); double maxDistance = Double.MIN_VALUE; double minDistance = Double.MAX_VALUE; for (Point2d p : nearestNeighbors) { double dx = p.x - (avgPoint.x * selScale + selOffsetX); double dy = p.y - (avgPoint.y * selScale + selOffsetY); double relativeDistance = dx * normal.getX2() + dy * normal.getY2(); maxDistance = relativeDistance > maxDistance ? relativeDistance : maxDistance; minDistance = relativeDistance < minDistance ? relativeDistance : minDistance; } maxDistance /= selScale; minDistance /= selScale; return new double[] {maxDistance, minDistance}; } /** * Computes Hausdorff distances of all visible curves. * @param primaryCurves cross-sections with primary face * @param secondaryCurves cross-sections with secondary face * @param primaryCurves cross sections with primary face * @param secondaryCurves cross sections with secondary face */ protected void computeHausdorffDistances(List<Curve2d> primaryCurves, List<Curve2d> secondaryCurves) { if (secondaryCurves.isEmpty()) { Loading Loading
GUI/src/main/java/cz/fidentis/analyst/gui/task/batch/symmetry/BatchCurveRenderingPanel.java +0 −4 Original line number Diff line number Diff line Loading @@ -202,10 +202,6 @@ public class BatchCurveRenderingPanel extends JPanel { g2.setColor(Color.GRAY); for (Line2D line : normalVectors) { double length = Math.sqrt(Math.pow(line.getX1() - line.getX2(), 2) + Math.pow(line.getY1() - line.getY2(), 2)); if (length > 28) { continue; } double x1 = line.getX1() * scale + offsetX; double y1 = line.getY1() * scale + offsetY; // This calculation is required to keep the normal vector sizes Loading
GUI/src/main/java/cz/fidentis/analyst/gui/task/batch/symmetry/BatchCuttingPlanesAction.java +53 −110 Original line number Diff line number Diff line Loading @@ -5,35 +5,40 @@ package cz.fidentis.analyst.gui.task.batch.symmetry; import cz.fidentis.analyst.canvas.Canvas; import cz.fidentis.analyst.drawables.DrawableCuttingPlane; import cz.fidentis.analyst.gui.task.ControlPanelAction; import cz.fidentis.analyst.events.HumanFaceEvent; import cz.fidentis.analyst.events.HumanFaceListener; import cz.fidentis.analyst.events.HumanFaceTransformedEvent; import cz.fidentis.analyst.drawables.DrawableCuttingPlane; import cz.fidentis.analyst.face.HumanFace; import cz.fidentis.analyst.gui.elements.PlaneManipulationPanel; import cz.fidentis.analyst.gui.task.ControlPanelAction; import cz.fidentis.analyst.gui.task.batch.FacesProxyDecorator; import cz.fidentis.analyst.shapes.Curve2d; import cz.fidentis.analyst.symmetry.CuttingPlanesUtils; import org.json.simple.JSONObject; import cz.fidentis.analyst.symmetry.CuttingPlanesUtils.CuttingPlaneConfig; import org.openide.filesystems.FileChooserBuilder; import javax.swing.*; import javax.swing.JTabbedPane; import javax.swing.filechooser.FileNameExtensionFilter; import javax.vecmath.Point2d; import javax.vecmath.Vector3d; import java.awt.event.ActionEvent; import java.awt.geom.Line2D; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.function.DoubleFunction; import java.util.stream.Collectors; import javax.vecmath.Point2d; import javax.vecmath.Vector3d; import org.json.simple.JSONObject; import static cz.fidentis.analyst.symmetry.CuttingPlanesUtils.*; import java.awt.geom.Line2D; import java.util.Comparator; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; /** * Action listener for manipulation with cutting planes. Loading Loading @@ -69,13 +74,17 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla * A new {@code ProfilesPanel} is instantiated and added to the {@code topControlPane} * * @param canvas OpenGL canvas * @param batchFacesProxy A proxy of all faces * @param faces Faces processed by current analytical task * @param topControlPane A top component when a new control panel is placed */ public BatchCuttingPlanesAction(Canvas canvas, FacesProxyDecorator batchFacesProxy, JTabbedPane topControlPane) { super(canvas, batchFacesProxy.getFacesProxy(), topControlPane); setControlPanel(new BatchCuttingPlanesPanel(this, batchFacesProxy)); /* if (getSecondaryDrawableFace() == null) { getControlPanel().hideHausdorffDistance(); }*/ getControlPanel().getCurveRenderingPanel().setFaceBoundingBox(getPrimaryFace().getBoundingBox()); createDefaultPlanes(); Loading @@ -87,6 +96,8 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla getScene().clearCuttingPlanes(); createDefaultPlanes(); getControlPanel().getCurveRenderingPanel().setFaceBoundingBox(getPrimaryFace().getBoundingBox()); //computeHausdorffDistances(getControlPanel().getCurveRenderingPanel().get2DAverageCurves(), //getControlPanel().getCurveRenderingPanel().get2DAverageCurves()); humanFaceChanged = false; } if (getControlPanel().getFacesProxyDecorator().haveAvgFace() && (lastAvgFace == null || !lastAvgFace.equals(getControlPanel().getFacesProxyDecorator().getAvgFace()))) { Loading Loading @@ -123,10 +134,16 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla exportConfiguration(); exportCurve(getControlPanel().getCurveRenderingPanel().get2DCurves().get(0), "Export primary face cross-section curve to file"); // if (getControlPanel().isMirrorCutsChecked()) { // exportProfile(this.primaryMirrorCurve, "Export primary face mirror profile to file"); // } if (getSecondaryDrawableFace() != null) { exportCurve(getControlPanel().getCurveRenderingPanel().get2DCurves().get(0), "Export secondary face cross-section curve to file"); // if (getControlPanel().isMirrorCutsChecked()) { // exportProfile(this.secondaryMirrorCurve, "Export secondary face mirror profile to file"); // } } break; Loading @@ -149,6 +166,8 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla if (getSecondaryDrawableFace() == null) { return; } //computeHausdorffDistances(getControlPanel().getCurveRenderingPanel().get2DAverageCurves(), // getControlPanel().getCurveRenderingPanel().get2DAverageCurves()); break; case BatchCuttingPlanesPanel.ACTION_INITIALIZE_MANUPULATION_PANEL: createPlane(); Loading @@ -158,10 +177,6 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla break; case BatchCuttingPlanesPanel.ACTION_COMMAND_SHOW_SELECTED_FACE: showSelectedFace(); if (getControlPanel().getSelectedFace() != null) { getControlPanel().getCurveRenderingPanel().setFaceBoundingBox(getControlPanel().getFacesProxyDecorator().haveAvgFace() ? getControlPanel().getFacesProxyDecorator().getAvgFace().getBoundingBox() : getControlPanel().getSelectedFace().getBoundingBox()); getControlPanel().getCurveRenderingPanel().setCurrentCuttingPlane(getControlPanel().getCurveRenderingPanel().getCurrentCuttingPlane()); } computeCrossSections(getCuttingPlanes(getControlPanel().getCurveRenderingPanel().getCurrentCuttingPlane())); break; default: Loading Loading @@ -229,7 +244,7 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla try { file.createNewFile(); try (PrintWriter writer = new PrintWriter(file.getAbsoluteFile(), StandardCharsets.UTF_8)) { try (PrintWriter writer = new PrintWriter(file.getAbsoluteFile(), "UTF-8")) { writer.print(configuration.toJSONString()); } } catch (IOException ex) { Loading @@ -247,7 +262,7 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla try { file.createNewFile(); try (PrintWriter writer = new PrintWriter(file.getAbsoluteFile(), StandardCharsets.UTF_8)) { try (PrintWriter writer = new PrintWriter(file.getAbsoluteFile(), "UTF-8")) { List<Point2d> points = curve.getCurveSegments().stream() .flatMap(Collection::stream) .toList(); Loading Loading @@ -314,17 +329,15 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla List<List<Curve2d>> curvesOfSelectedFace = CuttingPlanesUtils.computeCrossSections(crossSectionPlanes, getControlPanel().getSelectedFace(), null, FEATURE_POINTS_CLOSENESS); List<List<Curve2d>> curvesOfFaces = new ArrayList<>(); List<HumanFace> faceList = new ArrayList<>(); for (int i = 0; i < getControlPanel().getFacesProxyDecorator().getNumFaces(); i++) { curvesOfFaces.add(CuttingPlanesUtils.computeCrossSections(crossSectionPlanes, getControlPanel().getFacesProxyDecorator().getFace(i), null, FEATURE_POINTS_CLOSENESS).get(0)); faceList.add(getControlPanel().getFacesProxyDecorator().getFace(i)); } getControlPanel().getCurveRenderingPanel().setCurves(calculateDeviation(curvesOfSelectedFace.get(0), curvesOfFaces, faceList)); getControlPanel().getCurveRenderingPanel().setCurves(findNearestNeighbors(curvesOfSelectedFace.get(0), curvesOfFaces)); } private List<Curve2d> calculateDeviation(List<Curve2d> avg, List<List<Curve2d>> faceCurves, List<HumanFace> faceList) { private List<Curve2d> findNearestNeighbors(List<Curve2d> avg, List<List<Curve2d>> faces) { List<Curve2d> reducedCurves = new ArrayList<>(); for (int i = 0; i < avg.size(); i++) { if (avg.get(i) == null) { Loading @@ -333,7 +346,7 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla Curve2d avgCurve = avg.get(i).subSample(sampling); reducedCurves.add(avgCurve); List<Curve2d> curves = new ArrayList<>(); for (List<Curve2d> curvesOfFace : faceCurves) { for (List<Curve2d> curvesOfFace : faces) { curves.add(curvesOfFace.get(i)); } Loading @@ -343,36 +356,27 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla int curCounter = counter.getAndIncrement(); if (curCounter < avgCurveSegment.size() - 1) { // Find nearest neighbors in parallel List<Point2d> nearestNeighbors; List<Point2d> nearestNeighbors = curves.parallelStream() .map(curve -> curve.getCurveSegments().stream() .flatMap(Collection::stream) .min(Comparator.comparingDouble(point -> Math.sqrt(Math.pow((point.x - avgPoint.x), 2) + Math.pow((point.y - avgPoint.y), 2)))) .orElse(null)) .filter(Objects::nonNull) .collect(Collectors.toList()); Line2D normal = avgCurve.getNormals().get(curCounter); double maxDistance; double minDistance; if (getControlPanel().getFacesProxyDecorator().haveAvgFace()) { nearestNeighbors = findNearestNeighbors(curves, avgPoint); double[] distances = calculateMinMaxDistances(nearestNeighbors, avgPoint, normal, Arrays.asList(1.0, 0.0, 0.0)); maxDistance = distances[0]; minDistance = distances[1]; } else { BoundingBox2D avgBox2D = getBoundingBox2D(getControlPanel().getCurveRenderingPanel().getPreferredSize().width, getControlPanel().getCurveRenderingPanel().getPreferredSize().height, getControlPanel().getSelectedFace().getBoundingBox(), getControlPanel().getCurveRenderingPanel().getCurrentCuttingPlane()); double selScale = avgBox2D.scale(); double selOffsetX = avgBox2D.offsetX(); double selOffsetY = avgBox2D.offsetY(); nearestNeighbors = findNearestNeighbors(curves, avgPoint, faceList, Arrays.asList(selScale, selOffsetX, selOffsetY)); double maxDistance = Double.MIN_VALUE; double minDistance = Double.MAX_VALUE; for (Point2d p : nearestNeighbors) { double dx = p.x - avgPoint.x; double dy = p.y - avgPoint.y; double relativeDistance = dx * normal.getX2() + dy * normal.getY2(); double[] distances = calculateMinMaxDistances(nearestNeighbors, avgPoint, normal, Arrays.asList(selScale, selOffsetX, selOffsetY)); maxDistance = distances[0]; minDistance = distances[1]; maxDistance = relativeDistance > maxDistance ? relativeDistance : maxDistance; minDistance = relativeDistance < minDistance ? relativeDistance : minDistance; } normal.setLine( normal.getX1() + (normal.getX2() * minDistance), normal.getY1() + (normal.getY2() * minDistance), Loading @@ -388,71 +392,10 @@ public class BatchCuttingPlanesAction extends ControlPanelAction<BatchCuttingPla return reducedCurves; } private List<Point2d> findNearestNeighbors(List<Curve2d> curves, Point2d avgPoint) { return curves.parallelStream() .map(curve -> curve.getCurveSegments().stream() .flatMap(Collection::stream) .min(Comparator.comparingDouble(point -> Math.pow((point.x - avgPoint.x), 2) + Math.pow((point.y - avgPoint.y), 2))) .orElse(null)) .filter(Objects::nonNull) .collect(Collectors.toList()); } private List<Point2d> findNearestNeighbors(List<Curve2d> curves, Point2d avgPoint, List<HumanFace> faceList, List<Double> scaleOffset) { List<Point2d> nearestNeighbors = new ArrayList<>(); double selScale = scaleOffset.get(0); double selOffsetX = scaleOffset.get(1); double selOffsetY = scaleOffset.get(2); Point2d transformedAvgPoint = new Point2d(avgPoint.x * selScale + selOffsetX, avgPoint.y * selScale + selOffsetY); for (int faceIndex = 0; faceIndex < getControlPanel().getFacesProxyDecorator().getNumFaces(); faceIndex++) { BoundingBox2D box2D = getBoundingBox2D(getControlPanel().getCurveRenderingPanel().getPreferredSize().width, getControlPanel().getCurveRenderingPanel().getPreferredSize().height, faceList.get(faceIndex).getBoundingBox(), getControlPanel().getCurveRenderingPanel().getCurrentCuttingPlane()); double scale = box2D.scale(); double offsetX = box2D.offsetX(); double offsetY = box2D.offsetY(); Curve2d curve = curves.get(faceIndex); Point2d nearestPoint = curve.getCurveSegments().parallelStream() .flatMap(Collection::stream) .min(Comparator.comparingDouble(point -> { double dx = (point.x * scale + offsetX) - transformedAvgPoint.x; double dy = (point.y * scale + offsetY) - transformedAvgPoint.y; return dx * dx + dy * dy; // Compare squared distances })) .orElse(null); if (nearestPoint != null) { nearestNeighbors.add(new Point2d(nearestPoint.x * scale + offsetX, nearestPoint.y * scale + offsetY)); } } return nearestNeighbors; } private double[] calculateMinMaxDistances(List<Point2d> nearestNeighbors, Point2d avgPoint, Line2D normal, List<Double> scaleOffset) { double selScale = scaleOffset.get(0); double selOffsetX = scaleOffset.get(1); double selOffsetY = scaleOffset.get(2); double maxDistance = Double.MIN_VALUE; double minDistance = Double.MAX_VALUE; for (Point2d p : nearestNeighbors) { double dx = p.x - (avgPoint.x * selScale + selOffsetX); double dy = p.y - (avgPoint.y * selScale + selOffsetY); double relativeDistance = dx * normal.getX2() + dy * normal.getY2(); maxDistance = relativeDistance > maxDistance ? relativeDistance : maxDistance; minDistance = relativeDistance < minDistance ? relativeDistance : minDistance; } maxDistance /= selScale; minDistance /= selScale; return new double[] {maxDistance, minDistance}; } /** * Computes Hausdorff distances of all visible curves. * @param primaryCurves cross-sections with primary face * @param secondaryCurves cross-sections with secondary face * @param primaryCurves cross sections with primary face * @param secondaryCurves cross sections with secondary face */ protected void computeHausdorffDistances(List<Curve2d> primaryCurves, List<Curve2d> secondaryCurves) { if (secondaryCurves.isEmpty()) { Loading