Commit 578d4146 authored by Radek Ošlejšek's avatar Radek Ošlejšek
Browse files

Merge branch 'revert-0c46a688' into 'master'

Revert 0c46a688

See merge request grp-fidentis/analyst2!316
parents 496d02f7 56e116d1
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -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
+53 −110
Original line number Diff line number Diff line
@@ -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.
@@ -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();
@@ -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()))) {
@@ -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;
                
@@ -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();
@@ -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:
@@ -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) {
@@ -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();
@@ -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) {
@@ -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));
            }

@@ -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),
@@ -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()) {