Commit 440e419d authored by Kateřina Žárská's avatar Kateřina Žárská Committed by Radek Ošlejšek
Browse files

Resolve "Edit feature points"

parent e2b13218
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ import com.google.common.eventbus.EventBus;
import cz.fidentis.analyst.Logger;
import cz.fidentis.analyst.face.events.HumanFaceEvent;
import cz.fidentis.analyst.feature.FeaturePoint;
import cz.fidentis.analyst.feature.RelationToMesh;
import cz.fidentis.analyst.feature.services.FeaturePointImportService;
import cz.fidentis.analyst.kdtree.KdTree;
import cz.fidentis.analyst.mesh.core.MeshModel;
@@ -12,6 +13,7 @@ import cz.fidentis.analyst.mesh.io.MeshObjLoader;
import cz.fidentis.analyst.octree.Octree;
import cz.fidentis.analyst.symmetry.Plane;
import cz.fidentis.analyst.visitors.face.HumanFaceVisitor;
import cz.fidentis.analyst.visitors.kdtree.KdTreeApproxDistanceToTriangles;
import cz.fidentis.analyst.visitors.mesh.BoundingBox;
import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox;
import cz.fidentis.analyst.visitors.mesh.CurvatureCalculator;
@@ -37,6 +39,7 @@ import java.util.Objects;
 *
 * @author Radek Oslejsek
 * @author Matej Kovar
 * @author Katerina Zarska
 */
public class HumanFace implements Serializable {

@@ -94,6 +97,7 @@ public class HumanFace implements Serializable {
            if (landFile != null) {
                try {
                    loadFeaturePoints(landFile.getAbsoluteFile().getParent(), landFile.getName());
                    setFeaturePointRelations();
                } catch(IOException ex) {
                    Logger.print(ex.toString());
                }
@@ -444,6 +448,29 @@ public class HumanFace implements Serializable {
        visitor.visitHumanFace(this);
    }
    
    /**
     * Sets the feature points relation to the mesh for all feature points
     */
    protected void setFeaturePointRelations() {
        for (FeaturePoint fp : featurePoints) {
            if (fp.getRelationToMesh() == null){
                fp.setRelationToMesh(computeFeaturePointRelation(fp));
            }
        }
    }
    
    /**
     * Gets the feature points relation to the mesh - distance, nearest point, feature point position type
     * @param featurePoint 
     * @return relation
     */
    protected RelationToMesh computeFeaturePointRelation(FeaturePoint featurePoint) {
        KdTree kdTree = computeKdTree(false);
        KdTreeApproxDistanceToTriangles visitor = new KdTreeApproxDistanceToTriangles(featurePoint.getPosition(), true);
        kdTree.accept(visitor);
        return new RelationToMesh(visitor.getDistance(), visitor.getNearestPoints());
    }

    @Override
    public int hashCode() {
        int hash = 7;
+2 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ import cz.fidentis.analyst.registration.RegistrationAction;
import cz.fidentis.analyst.scene.Camera;
import cz.fidentis.analyst.symmetry.ProfilesAction;
import cz.fidentis.analyst.symmetry.SymmetryAction;
import cz.fidentis.analyst.featurepoints.FeaturePointsAction;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
@@ -109,6 +110,7 @@ public class FaceTab extends TopComponent {
            new CurvatureAction(getCanvas(), controlPanel);
            new SymmetryAction(getCanvas(), controlPanel);
            new ProfilesAction(getCanvas(), controlPanel);
            new FeaturePointsAction(getCanvas(), controlPanel);
            
            // Face State Panel for primary face
            FaceStatePanel facePanel = this.createFacePanelStateForFace(primary, project);
+199 −0
Original line number Diff line number Diff line
package cz.fidentis.analyst.featurepoints;

import cz.fidentis.analyst.core.LoadedActionEvent;
import cz.fidentis.analyst.feature.FeaturePoint;
import cz.fidentis.analyst.feature.FeaturePointType;
import cz.fidentis.analyst.feature.RelationToMesh;
import cz.fidentis.analyst.scene.DrawableFeaturePoints;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;

/**
 * @author Katerina Zarska
 */
public class FeaturePointListPanel extends JPanel {
    
    public static final String ACTION_COMMAND_FEATURE_POINT_HOVER_IN = "highlight hovered FP";
    public static final String ACTION_COMMAND_FEATURE_POINT_HOVER_OUT = "set default color of hovered FP";
    public static final String ACTION_COMMAND_FEATURE_POINT_SELECT = "highlight feature point with color";
    
    private List<JLabel> colorIndicators;
    private Map<RelationToMesh, JLabel> featurePointRelations;
    private List<JCheckBox> featurePointCheckBoxes;
    
    /**
     * Constructor.
     * 
     * @param action Action listener
     * @param featurePoints List of all feature points 
     */
    public void initComponents(ActionListener action, DrawableFeaturePoints featurePoints) {
        setLayout(new GridBagLayout());
        
        featurePointRelations = new HashMap<>(featurePoints.getFeaturePoints().size());
        featurePointCheckBoxes = new ArrayList<>(featurePoints.getFeaturePoints().size());
        colorIndicators = new ArrayList<>(featurePoints.getFeaturePoints().size());
        
        for (int i = 0; i < featurePoints.getFeaturePoints().size(); i++) {
            //final FeaturePoint featurePoint = featurePoints.get(i);
            //final FeaturePointType featurePointType = featurePoint.getFeaturePointType();
            //final RelationToMesh relation = featurePoint.getRelationToMesh();
            //addRow(i, action, featurePointType, relation);
            addRow(i, action, featurePoints);
        }
    }
    
    /**
     * (De)selects feature points
     * 
     * @param selected {@code true} if all feature point checkboxes are to be selected,
     *                 {@code false} otherwise
     */
    public void selectFeaturePoints(List<FeaturePoint> featurePoints, List<FeaturePointType> selectedFeaturePoints, boolean selected) {
        for (int i = 0; i < featurePoints.size(); i++) {
            if (selectedFeaturePoints.contains(featurePoints.get(i).getFeaturePointType())) {
                featurePointCheckBoxes.get(i).setSelected(selected);
            } else {
                featurePointCheckBoxes.get(i).setSelected(!selected);
            }
        }
    }
    
    /**
     * (De)selects given feature point
     * 
     * @param index Index of the feature point
     * @param selected {@code true} if a feature point checkbox is to be selected,
     *                 {@code false} otherwise
     */
    public void selectFeaturePoint(int index, boolean selected) {
        if (index >= 0 && index < featurePointCheckBoxes.size()) {
            featurePointCheckBoxes.get(index).setSelected(selected);
        }
    }
    
    /**
     * Refreshes the panel
     * @param action
     * @param featurePoints 
     */
    public void refreshPanel(ActionListener action, DrawableFeaturePoints featurePoints, List<FeaturePointType> selectedFeaturePoints) {
        removeAll();
        initComponents(action, featurePoints);
        revalidate();
        repaint();
        selectFeaturePoints(featurePoints.getFeaturePoints(), selectedFeaturePoints, true);
    }
    
    /**
     * Creates and returns action listener that can be connected with a low-level 
     * GUI element (e.g., a button). Action event of triggered low-level element is
     * redirected to the given {@code action}.
     * The listener may also carry additional data as a payload.
     * 
     * @param action Action listener
     * @param command Control panel command
     * @param data Payload data of the action listener
     * @return Action listener
     */
    protected final ActionListener createListener(ActionListener action, String command, Object data) {
        return new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                action.actionPerformed(new LoadedActionEvent(
                        e.getSource(), 
                        ActionEvent.ACTION_PERFORMED, 
                        command,
                        data)
                ); 
            }  
        };
    }
    
    protected void addRow(int row, ActionListener action, DrawableFeaturePoints featurePoints) {//FeaturePointType featurePointType, RelationToMesh relation) {
        FeaturePoint featurePoint = featurePoints.getFeaturePoints().get(row);
        
        Color color = DrawableFeaturePoints.OFF_THE_MESH_COLOR;
        if (featurePoint.getRelationToMesh().getPositionType() == RelationToMesh.FpPositionType.ON_THE_MESH) {
            color = DrawableFeaturePoints.ON_THE_MESH_COLOR;
        } else if (featurePoint.getRelationToMesh().getPositionType() == RelationToMesh.FpPositionType.CLOSE_TO_MESH) {
            color = DrawableFeaturePoints.CLOSE_TO_MESH_COLOR;
        }

        GridBagConstraints c = new GridBagConstraints();  
        c.gridwidth = 1;
        c.gridx = 0;
        c.gridy = row;
        c.anchor = GridBagConstraints.LINE_START;
        c.fill = GridBagConstraints.NONE;
        
        GridBagConstraints c1 = new GridBagConstraints();
        c1.insets = new Insets(0, 10, 0, 10);
        c1.gridwidth = GridBagConstraints.REMAINDER;
        c1.gridx = 1;
        c1.gridy = row;
        c1.anchor = GridBagConstraints.LINE_END;
        c1.fill = GridBagConstraints.NONE;
        
        JLabel label = new JLabel("⚫");
        label.setForeground(color);
        add(label, c1);
        colorIndicators.add(label);
        
        JCheckBox checkBox = new JCheckBox();
        checkBox.setText(featurePoint.getFeaturePointType().getName());      
        add(checkBox, c);
        featurePointCheckBoxes.add(checkBox);
        
        checkBox.addActionListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_SELECT, row));
        
        final int index = row;
        checkBox.addMouseListener(new MouseAdapter() { // highlight spheres on mouse hover
            @Override
            public void mouseEntered(MouseEvent e) {
                action.actionPerformed(new LoadedActionEvent(
                        e.getSource(),
                        ActionEvent.ACTION_PERFORMED,
                        ACTION_COMMAND_FEATURE_POINT_HOVER_IN,
                        index));
            }

            @Override
            public void mouseExited(MouseEvent e) {
                action.actionPerformed(new LoadedActionEvent(
                        e.getSource(),
                        ActionEvent.ACTION_PERFORMED,
                        ACTION_COMMAND_FEATURE_POINT_HOVER_OUT,
                        index));
            }
        });
                
//        GridBagConstraints c2 = new GridBagConstraints();
//        c2.gridwidth = GridBagConstraints.REMAINDER;
//        c2.gridx = 2;
//        c2.gridy = row;
//        c2.anchor = GridBagConstraints.LINE_END;
//        c2.fill = GridBagConstraints.NONE;
//        c2.insets = new Insets(0, 0, 0, 0);
//        
//        JLabel label2 = new JLabel();
//        label2.setText(featurePoint.getRelationToMesh().getPositionTypeToString());
//        label2.setVisible(true);
//        add(label2, c2);
//        featurePointRelations.put(featurePoint.getRelationToMesh(), label2);
    }
}
+223 −0
Original line number Diff line number Diff line
package cz.fidentis.analyst.featurepoints;

import cz.fidentis.analyst.canvas.Canvas;
import cz.fidentis.analyst.core.ControlPanelAction;
import cz.fidentis.analyst.core.DoubleSpinner;
import cz.fidentis.analyst.core.LoadedActionEvent;
import cz.fidentis.analyst.face.events.HumanFaceEvent;
import cz.fidentis.analyst.face.events.HumanFaceListener;
import cz.fidentis.analyst.feature.FeaturePoint;
import cz.fidentis.analyst.feature.FeaturePointType;
import cz.fidentis.analyst.feature.RelationToMesh;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JTabbedPane;
import javax.swing.JToggleButton;

/**
 *
 * @author Katerina Zarska
 */
public class FeaturePointsAction extends ControlPanelAction implements HumanFaceListener {
    
    /*
     * GUI elements
     */
    private FeaturePointType hoveredFeaturePoint = null;
    private final FeaturePointsPanel controlPanel;
    private List<FeaturePointType> selectedFeaturePoints = new ArrayList<>();;
    
    private static final Color  FEATURE_POINT_HIGHLIGHT_COLOR = Color.MAGENTA;
    private static final Color  FEATURE_POINT_HOVER_COLOR = Color.CYAN;

    /**
     * Constructor
     * @param canvas            OpenGLCanvas
     * @param topControlPanel   Top component for placing control panels
     */
    public FeaturePointsAction(Canvas canvas, JTabbedPane topControlPanel) {
        super(canvas, topControlPanel);        
        
        controlPanel = new FeaturePointsPanel(this, getPrimaryFeaturePoints());

        // Place control panel to the topControlPanel
        topControlPanel.addTab(controlPanel.getName(), controlPanel.getIcon(), controlPanel);
        topControlPanel.addChangeListener(e -> {
            // If the FeaturePoints panel is focused...
            if (((JTabbedPane) e.getSource()).getSelectedComponent() instanceof FeaturePointsPanel) {
                getCanvas().getScene().setDefaultColors();
            }
        });
        
        // Be informed about changes in faces perfomed by other GUI elements
        getPrimaryDrawableFace().getHumanFace().registerListener(this);
        if (getSecondaryDrawableFace() != null) {
            getSecondaryDrawableFace().getHumanFace().registerListener(this);
        }
    }

    @Override
    public void actionPerformed(ActionEvent ae) {
        String action = ae.getActionCommand();
        
        switch (action) {
            case FeaturePointsPanel.ACTION_COMMAND_ADD_FEATURE_POINT:
                break;
            case FeaturePointsPanel.ACTION_COMMAND_REMOVE_FEATURE_POINT:
                workWithSelectedFp("remove");
                controlPanel.getFeaturePointListPanel().refreshPanel(this, getPrimaryFeaturePoints(), selectedFeaturePoints);
                break;
            case FeaturePointsPanel.ACTION_COMMAND_EDIT_FEATURE_POINT:
                break;
            case FeaturePointsPanel.ACTION_COMMAND_EXPORT_FEATURE_POINTS:
                break;
            case FeaturePointsPanel.ACTION_COMMAND_LOAD_FEATURE_POINTS:
                break;
            case FeaturePointListPanel.ACTION_COMMAND_FEATURE_POINT_SELECT:
                selectFeaturePoint((LoadedActionEvent) ae);
                highlightSelectedFeaturePoints();
                break;
            case FeaturePointListPanel.ACTION_COMMAND_FEATURE_POINT_HOVER_IN:
                hoverFeaturePoint((LoadedActionEvent) ae, true);
                break;
            case FeaturePointListPanel.ACTION_COMMAND_FEATURE_POINT_HOVER_OUT:
                hoverFeaturePoint((LoadedActionEvent) ae, false);
                break;
            case FeaturePointsPanel.ACTION_CHANGE_DISTANCE_THRESHOLD:
                changeDistanceThreshold(ae);
                break; 
            case FeaturePointsPanel.ACTION_PIN_ALL_FEATURE_POINTS:
                for (FeaturePoint fp : getPrimaryFeaturePoints().getFeaturePoints()) {
                    if (fp.getRelationToMesh().getDistance() != 0) {
                        pinFeaturePointToMesh(fp);
                    }
                }
                controlPanel.getFeaturePointListPanel().refreshPanel(this, getPrimaryFeaturePoints(), selectedFeaturePoints);
                break;
            case FeaturePointsPanel.ACTION_PIN_SELECTED_FEATURE_POINTS:
                workWithSelectedFp("pin");
                controlPanel.getFeaturePointListPanel().refreshPanel(this, getPrimaryFeaturePoints(), selectedFeaturePoints);
                break;
            default:
                // do nothing
        }
        renderScene();
    }
    
    /**
     * Changes the color of the face's feature point at the given index
     * and of its weighted representation when the cursor hovers over the feature point's name.
     * The index is received as the data payload of {@code actionEvent}.
     * 
     * @param actionEvent Action event with the index of the feature point as its payload data
     * @param entered {@code true} if the cursor entered the feature point,
     *                {@code false} if the cursor left the feature point
     */
    private void hoverFeaturePoint(LoadedActionEvent actionEvent, boolean entered) {
        final int index = (int) actionEvent.getData();
        
        if (entered) { // entering a feature point
            getPrimaryFeaturePoints().setColor(index, FEATURE_POINT_HOVER_COLOR);
            hoveredFeaturePoint = getTypeOfFeaturePoint(index);
        } else if (selectedFeaturePoints.contains(hoveredFeaturePoint)) { // leaving highlighted FP
            getPrimaryFeaturePoints().setColor(index, FEATURE_POINT_HIGHLIGHT_COLOR);
        } else { // leaving ordinary FP
            getPrimaryFeaturePoints().resetColorToDefault(index);
        }
    }
    
    /**
     * Returns type of the feature point at the given index.
     * 
     * @param index Index of the feature point
     * @return Type of the feature point or {@code null}
     */
    private FeaturePointType getTypeOfFeaturePoint(int index) {
        final List<FeaturePoint> featurePoints = getPrimaryFeaturePoints().getFeaturePoints();
        if (index < 0 || index >= featurePoints.size()) {
            return null;
        }
        
        return featurePoints.get(index).getFeaturePointType();
    }
    
    /**
     * Adds a point to selected feature points
     * 
     * @param actionEvent LoadedActionEvent
     */
    private void selectFeaturePoint(LoadedActionEvent actionEvent) {
        final int index = (int) actionEvent.getData();
        final FeaturePointType fpType = getTypeOfFeaturePoint(index);
        
        if (((JToggleButton) actionEvent.getSource()).isSelected()) {
            selectedFeaturePoints.add(fpType);
        } else {
            selectedFeaturePoints.remove(fpType);
        }
    }
    
    private void highlightSelectedFeaturePoints() {
        if (selectedFeaturePoints.isEmpty()) {
            return;
        }

        for (int i = 0; i < getPrimaryFeaturePoints().getFeaturePoints().size(); i++) {
            if (selectedFeaturePoints.contains(getTypeOfFeaturePoint(i))) {
                getPrimaryFeaturePoints().setColor(i, FEATURE_POINT_HIGHLIGHT_COLOR);
            } else {
                getPrimaryFeaturePoints().resetColorToDefault(i);
            }
        }
    }
    
    private void workWithSelectedFp(String action) {
        if (selectedFeaturePoints.isEmpty()) {
            return;
        }

        for (int i = getPrimaryFeaturePoints().getFeaturePoints().size()-1; i >= 0; i--) {
            if (selectedFeaturePoints.contains(getTypeOfFeaturePoint(i))) {
                switch(action) {
                    case "remove":
                        getPrimaryFeaturePoints().resetColorToDefault(i);
                        getPrimaryFeaturePoints().getFeaturePoints().remove(i);
                        break;
                    case "pin":
                        pinFeaturePointToMesh(getPrimaryFeaturePoints().getFeaturePoints().get(i));
                        break;
                }
            }
        }
    }
         
    /**
     * Changes the distance threshold for categorizing feature points into either ON_THE_MESH or CLOSE_TO_MESH
     * 
     * @param ae ActionEvent
     */
    private void changeDistanceThreshold(ActionEvent ae) {
        double sliderVal = (Double) ((DoubleSpinner) ae.getSource()).getValue();
        RelationToMesh.setDistanceThreshold(sliderVal);
        controlPanel.getFeaturePointListPanel().refreshPanel(this, getPrimaryFeaturePoints(), selectedFeaturePoints);
    }
      
    /**
     * Changes the feature point's position to the closest point on mesh
     * 
     * @param fp feature point to be changed
     */
    private void pinFeaturePointToMesh(FeaturePoint fp) {
        RelationToMesh relation = fp.getRelationToMesh();
        if (relation.getPositionType() != RelationToMesh.FpPositionType.OFF_THE_MESH) {
            fp.setPosition(relation.getNearestPoint());
            relation.setDistance(0);
        }
    }  

    @Override
    public void acceptEvent(HumanFaceEvent event) {        
    }
}
+160 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading