Commit 6473e1c4 authored by Mário Chromík's avatar Mário Chromík Committed by Radek Ošlejšek
Browse files

Resolve "Core functionality of 3D mask"

parent 855c03a6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -77,7 +77,7 @@ public class CurveRenderingPanel extends JPanel {
    private List<CrossSection2D> secondaryCrossSectionCurves;

    private Vector3d currentCuttingPlaneNormal;
    private Box faceBoundingBox;
    protected Box faceBoundingBox;
    
//    private boolean mirrorCuts = false;
    
+75 −121
Original line number Diff line number Diff line
@@ -4,14 +4,13 @@
 */
package cz.fidentis.analyst.gui.elements;

import cz.fidentis.analyst.canvas.Canvas;
import cz.fidentis.analyst.data.face.HumanFace;
import cz.fidentis.analyst.data.shapes.SurfaceMask;
import cz.fidentis.analyst.data.shapes.SurfaceMask2D;
import cz.fidentis.analyst.gui.task.interactivemask.InteractiveMaskTaskNoParallel;
import cz.fidentis.analyst.data.shapes.Box;
import cz.fidentis.analyst.data.shapes.SurfaceMask2DImpl;
import cz.fidentis.analyst.data.shapes.SurfaceMaskLine;
import javax.swing.SwingUtilities;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
@@ -24,14 +23,10 @@ import java.awt.event.MouseEvent;
 * @author Mario Chromik
 */
public class SurfaceMaskPanel extends CurveRenderingPanel {
    /**private Canvas canvas;
    private HumanFace face;
    */

    private final SurfaceMask2D surfaceMask = new SurfaceMask2D();

    private ActionListener panelListener;

    private SurfaceMask2DImpl surfaceMask = new SurfaceMaskLine();
    private ActionListener panelListenerProject;
    private ActionListener panelListenerSelected;
    private Box boundingBox;
    /**
     * Constructor setting mouse listeners and drawing an initial mask.
     */
@@ -40,120 +35,31 @@ public class SurfaceMaskPanel extends CurveRenderingPanel {
        Adapter ml = new Adapter();
        addMouseListener(ml);
        addMouseMotionListener(new MotionAdapter());
        drawInitialRectangleSM();
    }

    /**
    /**
     * Sets canvas and face
     * @param canvas input canvas
     * @param face input face
     *
    public void setCanvasAndFace(Canvas canvas, HumanFace face) {
        this.canvas = canvas;
        this.face = face;
    }
     * Gets a surface mask
     * @return surface mask
     */

    public SurfaceMask2D getSurfaceMask() {
    public SurfaceMask2DImpl getSurfaceMask() {
        return surfaceMask;
    }

    /**
     * Draws an initial shape for surface mask
     * Sets a surface mask
     * @param mask to set
     */
    private void drawInitialRectangleSM(){
        surfaceMask.addPoint(new Point(100, 100));
        surfaceMask.addPoint(new Point(100, 200));
        surfaceMask.addPoint(new Point(200, 200));
        surfaceMask.addPoint(new Point(200, 100));
    public void setSurfaceMask(SurfaceMask2DImpl mask) {
        this.surfaceMask = mask;
        repaint();
    }

    /**
     * Draws the shape of an interactive mask
     * @param g panel graphics
     */
    private void drawMaskLines(Graphics g) {
        for (int i = 0; i < surfaceMask.getMaskPoints().size() - 1; i++) {
            g.drawLine(surfaceMask.getMaskPoints().get(i).x, surfaceMask.getMaskPoints().get(i).y,
                    surfaceMask.getMaskPoints().get(i + 1).x, surfaceMask.getMaskPoints().get(i + 1).y);
            g.drawOval(surfaceMask.getMaskPoints().get(i).x - 5,
                    surfaceMask.getMaskPoints().get(i).y - 5, 10, 10);
        }

        //Close the mask
        g.drawOval(surfaceMask.getMaskPoints().get(surfaceMask.getMaskPoints().size() - 1).x - 5,
                surfaceMask.getMaskPoints().get(surfaceMask.getMaskPoints().size() - 1).y - 5, 10, 10);

        if (surfaceMask.getMaskPoints().size() >= 3) {
            g.drawLine(surfaceMask.getMaskPoints().get(surfaceMask.getMaskPoints().size() - 1).x,
                    surfaceMask.getMaskPoints().get(surfaceMask.getMaskPoints().size() - 1).y,
                    surfaceMask.getMaskPoints().get(0).x, surfaceMask.getMaskPoints().get(0).y);
        }
    }

    /**
     * Draws curves between points
     * @param g panel graphics
     * @param controlPoints a list of control points

    private void drawCurves(Graphics g, ArrayList<Point> controlPoints) {
        int n = controlPoints.size();
        if (n >= 3) {
            for (int i = 0; i < n - 3; i += 3) {
                g.setColor(Color.RED); // Set your desired color for Bezier curves
                drawCurve(g, controlPoints.get(i).x, controlPoints.get(i).y,
                        controlPoints.get(i+1).x, controlPoints.get(i+1).y,
                        controlPoints.get(i+2).x, controlPoints.get(i+2).y);
            }
        }
    }


     * Draws a single curve
     * @param g panel graphics
     * @param x the X coordinate of the start point y1
     * @param y the Y coordinate of the start point
     * @param x1 the X coordinate of the control point
     * @param y1 the Y coordinate of the control point
     * @param x2 the X coordinate of the end point
     * @param y2 the Y coordinate of the end point

    private void drawCurve(Graphics g, int x, int y, int x1, int y1, int x2, int y2) {
        Graphics2D g2 = (Graphics2D) g;
        QuadCurve2D q = new QuadCurve2D.Float();
        q.setCurve(x, y, x1, y1, x2, y2);
        g2.draw(q);
    }



    /**
     * Projects points onto face using asynchronous thread
     /
    private void project() {
        /** Code used to intialize task and project points in parellel
        //cancel if task is already running
         task.cancel(false);
         task = new InteractiveMaskTask(canvas, pointsToProject, getPanelWidth(), getPanelHeight());
         task.execute();
        /
        InteractiveMaskTaskNoParallel task = new InteractiveMaskTaskNoParallel(canvas, surfaceMask.getPointsToProject(), getPanelWidth(), getPanelHeight());
        task.project();

    }
    */

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        //face.setSurfaceMask(new SurfaceMask());
        g.setColor(Color.BLACK);

        if (surfaceMask.getMaskPoints().size() >= 2) {
            drawMaskLines(g);
        }
        surfaceMask.draw(g);
    }

    /**
@@ -172,8 +78,20 @@ public class SurfaceMaskPanel extends CurveRenderingPanel {
        return (int)this.getSize().getHeight();
    }

    public void addActionListener(ActionListener listener) {
        this.panelListener = listener;
    /**
     * Adds action listener for projection of mask
     * @param listener action listener
     */
    public void addActionListenerProject(ActionListener listener) {
        this.panelListenerProject = listener;
    }

    /**
     * Adds action listener for selection of mask
     * @param listener action listener
     */
    public void addActionListenerSelected(ActionListener listener) {
        this.panelListenerSelected = listener;
    }

    /**
@@ -184,18 +102,25 @@ public class SurfaceMaskPanel extends CurveRenderingPanel {

        @Override
        public void mousePressed(MouseEvent e) {
            if (! surfaceMask.selectPoint(e.getPoint())) {
                surfaceMask.addPoint(e.getPoint());
                repaint();
            if (SwingUtilities.isLeftMouseButton(e)) {
                surfaceMask.addNewPoint(e.getPoint());
            } else if (SwingUtilities.isRightMouseButton(e)) {
               if (surfaceMask instanceof SurfaceMaskLine) {
                   ((SurfaceMaskLine) surfaceMask).deletePoint(e.getPoint());
               }
            }

            if (e.isShiftDown()) {
                surfaceMask.containsPoint(e.getPoint());
            }
            repaint();
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            surfaceMask.setSelectedPoint(null);
            //project();
            panelListener.actionPerformed( new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null));
            surfaceMask.setShiftPoint(null);
            panelListenerProject.actionPerformed( new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Mask updated"));
        }
    }

@@ -206,9 +131,38 @@ public class SurfaceMaskPanel extends CurveRenderingPanel {
    class MotionAdapter extends MouseMotionAdapter {
        @Override
        public void mouseDragged(MouseEvent e) {
            if (e.getPoint().x < 0 || e.getPoint().x > getPanelWidth()
                    || e.getPoint().y < 0 || e.getPoint().y > getPanelHeight()) {
                //if point is outside drawing panel unset selected and shifted point
                surfaceMask.setSelectedPoint(null);
                surfaceMask.setShiftPoint(null);
            }
            if (e.isShiftDown() && surfaceMask.getShiftPoint() != null) {
                int dx = e.getPoint().x - surfaceMask.getShiftPoint().x;
                int dy = e.getPoint().y - surfaceMask.getShiftPoint().y;
                surfaceMask.shiftMask(dx, dy);
                surfaceMask.setShiftPoint(e.getPoint());
                repaint();
            }
            if (surfaceMask.getSelectedPoint() != null) {
                surfaceMask.getSelectedPoint().setLocation(e.getPoint());
                surfaceMask.updateSelectedPoint(e.getPoint());
                repaint();
            }
        }
        @Override
        public void mouseMoved(MouseEvent e) {
            if (surfaceMask.selectPoint(e.getPoint())) {
                setCursor(new Cursor(Cursor.HAND_CURSOR));
                repaint();
                panelListenerSelected.actionPerformed( new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Point selected"));
            } else if (surfaceMask.getSelectedPoint() != null) {
                surfaceMask.setSelectedPoint(null);
                repaint();
                setCursor(Cursor.getDefaultCursor());
                panelListenerSelected.actionPerformed( new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Point selected"));
            }else {
                repaint();
                setCursor(Cursor.getDefaultCursor());
            }
        }
    }
+166 −21
Original line number Diff line number Diff line
package cz.fidentis.analyst.gui.task.interactivemask;

import cz.fidentis.analyst.canvas.Canvas;
import cz.fidentis.analyst.data.shapes.Box;
import cz.fidentis.analyst.data.shapes.CrossSection3D;
import cz.fidentis.analyst.data.shapes.SurfaceMask2D;
import cz.fidentis.analyst.data.shapes.SurfaceMask2DImpl;
import cz.fidentis.analyst.data.shapes.SurfaceMaskEllipse;
import cz.fidentis.analyst.data.shapes.SurfaceMaskLine;
import cz.fidentis.analyst.data.shapes.SurfaceMaskRectangle;
import cz.fidentis.analyst.engines.face.FaceStateServices;
import cz.fidentis.analyst.engines.interactivemask.MaskProjector;
import cz.fidentis.analyst.engines.interactivemask.MaskProjectorConfig;
@@ -10,17 +16,19 @@ import cz.fidentis.analyst.project.FacesProxy;
import cz.fidentis.analyst.rendering.Camera;

import javax.swing.*;
import javax.vecmath.Point2d;
import javax.vecmath.Vector3d;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;


/**
 *
 * Action listener for interactive mask panel
 * @author Mario Chromik
 */
public class InteractiveMaskAction extends ControlPanelAction<InteractiveMaskPanel> {

    private String mode = InteractiveMaskPanel.ACTION_DRAW;

    private SurfaceMaskHistory history = new SurfaceMaskHistory();
    /**
     * Constructor
     * A new {@code InteractiveMaskPanel} is instantiated and added to the {@code topControlPane}
@@ -33,7 +41,7 @@ public class InteractiveMaskAction extends ControlPanelAction<InteractiveMaskPan
        super(canvas, faces, topControlPane);
        InteractiveMaskPanel pl = new InteractiveMaskPanel(this);
        setControlPanel(pl);

        pl.getSurfaceMaskPanel1().setFaceBoundingBox(getPrimaryFace().getBoundingBox());
        setShowHideCode(
                () -> { // SHOW
                    getCanvas().getScene().showInteractiveMask(getCanvas().getScene().getPrimaryFaceSlot(), true);
@@ -46,17 +54,44 @@ public class InteractiveMaskAction extends ControlPanelAction<InteractiveMaskPan
        );
    }

    private void project() {
        FaceStateServices.updateOctree(getPrimaryFace(), FaceStateServices.Mode.COMPUTE_IF_ABSENT);
        SurfaceMask2D mask = getControlPanel().getSurfaceMask();
    /**
     * Gets the ratio between bounding box and the drawing panel
     * @return ratio
     */
    private double getBboxScale() {
        Box box = getPrimaryFace().getBoundingBox();
        Point2d min = CrossSection3D.flattenPoint(box.minPoint(), new Vector3d(0, 0, 1));
        Point2d max = CrossSection3D.flattenPoint(box.maxPoint(), new Vector3d(0, 0, 1));
        double scaleWidth = Math.abs((max.x - min.x) / getControlPanel().getSurfaceMaskPanel1().getPanelWidth());
        double scaleHeight = Math.abs((max.y - min.y) / getControlPanel().getSurfaceMaskPanel1().getPanelHeight());
        return Math.max(scaleWidth, scaleHeight);
    }

    /**
     * Projects a 2D represntation of the mask into 3D
     * @param mask
     */
    private void project(SurfaceMask2D mask) {
        //JOptionPane.showMessageDialog(null, "here");
        MaskProjectorConfig config = new MaskProjectorConfig(getCanvas().getWidth(),
            getCanvas().getHeight(), getControlPanel().getSurfaceMaskPanel1().getPanelWidth(),
            getControlPanel().getSurfaceMaskPanel1().getPanelHeight(),Camera.FIELD_OF_VIEW,
            getCanvas().getCamera().getPosition(), getCanvas().getCamera().getCenter(),
                getCanvas().getCamera().getUpDirection());
        MaskProjector mp = new MaskProjector(config, mask);
            getCanvas().getCamera().getUpDirection(), getBboxScale(), getControlPanel().getZoomPrecentage());
        MaskProjector mp = new MaskProjector(config, mask.getPointsToProject());

        if (getControlPanel().isPrimaryFaceEnabled() && getPrimaryFace() != null) {
            FaceStateServices.updateOctree(getPrimaryFace(), FaceStateServices.Mode.COMPUTE_IF_ABSENT);
            getPrimaryFace().getOctree().accept(mp);
            getScene().setDrawableInteractiveMask(getScene().getPrimaryFaceSlot(), mp.getResult());
        }

        if (getControlPanel().isSecondaryFaceEnabled() && getSecondaryFace() != null) {
            FaceStateServices.updateOctree(getSecondaryFace(), FaceStateServices.Mode.COMPUTE_IF_ABSENT);
            getSecondaryFace().getOctree().accept(mp);
            getScene().setDrawableInteractiveMask(getScene().getSecondaryFaceSlot(), mp.getResult());
        }

        renderScene();
    }

@@ -66,17 +101,127 @@ public class InteractiveMaskAction extends ControlPanelAction<InteractiveMaskPan
        String action = ae.getActionCommand();
        switch (action) {
            case InteractiveMaskPanel.ACTION_PROJECT:
                project();
                history.addToHistory(new SurfaceMaskMemento(getControlPanel().getSurfaceMask()));
                project(getControlPanel().getSurfaceMask());
                break;
            case InteractiveMaskPanel.ACTION_HISTORY_BACKWARD:
                history.goBack();
                getControlPanel().getSurfaceMaskPanel1().setSurfaceMask(history.getCurrent().getMask());
                project(history.getCurrent().getMask());
                break;
            case InteractiveMaskPanel.ACTION_HISTORY_FORWARD:
                history.goForward();
                getControlPanel().getSurfaceMaskPanel1().setSurfaceMask(history.getCurrent().getMask());
                project(history.getCurrent().getMask());
                break;
            case InteractiveMaskPanel.ACTION_DRAW:
                mode = InteractiveMaskPanel.ACTION_DRAW;
            case InteractiveMaskPanel.ACTION_ZOOM:
            case InteractiveMaskPanel.ACTION_POINT_SELECTED:
                project(getControlPanel().getSurfaceMask());
                break;
            case InteractiveMaskPanel.ACTION_EDIT:
                mode = InteractiveMaskPanel.ACTION_EDIT;
            case InteractiveMaskPanel.ACTION_SAMPLING:
                getControlPanel().getSurfaceMask().setSamplingStrength(getControlPanel().getSamplingStrength());
                project(getControlPanel().getSurfaceMask());
                break;
            default:
                mode = InteractiveMaskPanel.ACTION_VIEW;
        }
    }

    /**
     * A class representing a snapshot of a state of a surface mask
     * @author Mario Chromik
     */
    private class SurfaceMaskMemento {
        private SurfaceMask2DImpl mask;

        SurfaceMaskMemento(SurfaceMask2D mask) {
            if (mask == null) {
                return;
            }
            //creates a deep copy of the points defining surface mask
            if (mask instanceof SurfaceMaskEllipse) {
                this.mask = new SurfaceMaskEllipse((SurfaceMaskEllipse) mask);
            } else if (mask instanceof SurfaceMaskRectangle) {
                this.mask = new SurfaceMaskRectangle((SurfaceMaskRectangle) mask);
            } else if (mask instanceof SurfaceMaskLine) {
                this.mask = new SurfaceMaskLine((SurfaceMaskLine) mask);
            }
        }

        public SurfaceMask2DImpl getMask() {
            return mask;
        }
    }

    /**
     * A history, it is a collection of snapshots, enabling to browse through them.
     * The maximum depth of history is double the parameter HISTORY_DEPTH. If the history
     * reaches the maximum depth, the history is halved.
     * @author Mario Chromik
     */
    private class SurfaceMaskHistory {
        //TODO: it might be better to move these history classes inside SurfaceMaskPanel
        //TODO: Dynamically enable and disable buttons
        private List<SurfaceMaskMemento> history;

        private static final  int HISTORY_DEPTH = 5;
        private int currentIndex = 0;
        SurfaceMaskHistory() {
            this.history = new ArrayList<>();
        }

        /**
         * Adds a new entry in the history, if the max depth is exceeded, the history is halved, and sets
         * the current index to the last entry added.
         * @param memento memento to add
         */
        public void addToHistory(SurfaceMaskMemento memento) {

            this.history.add(memento);
            if (history.size() > HISTORY_DEPTH * 2) {
                history = history.subList(HISTORY_DEPTH, history.size());
            }
            currentIndex = history.size() - 1;
        }

        /**
         * Gets a memento with specific index
         * @param index index to select
         * @return a memento
         */
        public SurfaceMaskMemento getMemento(int index) {
            return this.history.get(index);
        }

        /**
         * Gets the current memento
         * @return the current memento
         */
        public SurfaceMaskMemento getCurrent() {
            return this.history.get(currentIndex);
        }

        /**
         * Goes back in history if possible
         * @return true if possible
         */
        public boolean goBack() {
            if (currentIndex - 1 < 0) {
                return false;
            }
            currentIndex -= 1;
            return true;
        }

        /**
         * Goes forward in the history if possible
         * @return true if possible
         */
        public boolean goForward() {
            if (currentIndex + 1 == history.size()) {
                return false;
            }
            currentIndex += 1;
            return true;
        }
    }
}
+168 −63

File changed.

Preview size limit exceeded, changes collapsed.

+215 −57

File changed.

Preview size limit exceeded, changes collapsed.

Loading