Commit 56e116d1 authored by Peter Čonga's avatar Peter Čonga Committed by Radek Ošlejšek
Browse files

Distance span reflects reality even when faces are not registered.

parent 496d02f7
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()) {