Commit 898954d5 authored by Radek Ošlejšek's avatar Radek Ošlejšek
Browse files

Merge branch '258-remove-panel-builder' into 'master'

Resolve "Remove panel builder"

Closes #258

See merge request grp-fidentis/analyst2!275
parents 5f4ac5d7 70a5dcf0
Loading
Loading
Loading
Loading
+0 −1270

File deleted.

Preview size limit exceeded, changes collapsed.

+0 −60
Original line number Diff line number Diff line
package cz.fidentis.analyst.gui.elements;

import cz.fidentis.analyst.gui.builder.PanelBuilder;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Dimension;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.JLabel;

/**
 * Simple GUI element for showcase of different colors and information about
 * them.
 *
 * @author Lukas Machalek
 */
public class ColorListPanel extends JPanel {

    /**
     * Constructor
     *
     * @param colors map representing description and colors
     * @param panelTitle Title of the panel
     */
    public ColorListPanel(Map<String, Color> colors, String panelTitle) {

        PanelBuilder builder = new PanelBuilder(this);
        builder.setPanelBorder(panelTitle)
                .setLeftInset(8)
                .setTopInset(4);

        colors.forEach((description, color) -> {
            addRow(builder, color, description);
        });
    }

    /**
     * Adds row with color label and description
     *
     * @param builder builder of this panel
     * @param color color to be shown
     * @param description description of given color
     */
    private void addRow(PanelBuilder builder, Color color, String description) {
        JLabel colorLabel = new JLabel();

        colorLabel.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
        colorLabel.setOpaque(true);
        colorLabel.setBackground(color);
        colorLabel.setForeground(Color.GRAY);

        colorLabel.setPreferredSize(new Dimension(15, 15));

        builder.addLabel(colorLabel);

        builder.addLabel(description);
        builder.nextLine();
    }

}
+14 −4
Original line number Diff line number Diff line
@@ -98,14 +98,24 @@ public class DoubleSpinner extends JSpinner {
     */
    @Override
    public void setValue(Object value) {
        super.setValue(convertValue(value));
    }

    /**
     * This method converts the given value by rounding it into the number of
     * decimal digits given by {@code fractionDigits} parameter provided to
     * the constructor.
     *
     * @param value double value to be set
     * @throws IllegalArgumentException if <code>value</code> isn't allowed
     */
    public double convertValue(Object value) {
        if (value instanceof Double) {
            // round a fractio part to N decimal places
            int fraction = getFractionDecimals();
            double roudedVal = ((double) Math.round((Double)value * fraction)) / fraction;        
            super.setValue(roudedVal);
            return ((double) Math.round((Double)value * fraction)) / fraction;
        } else if (value instanceof Integer) {
            double val = (Integer) value / (double) getFractionDecimals();
            super.setValue(val);
            return (Integer) value / (double) getFractionDecimals();
        } else {
            throw new IllegalArgumentException("value");
        }
+0 −398
Original line number Diff line number Diff line
package cz.fidentis.analyst.gui.elements;

import cz.fidentis.analyst.gui.task.LoadedActionEvent;
import cz.fidentis.analyst.feature.FeaturePoint;
import cz.fidentis.analyst.feature.FeaturePointType;
import cz.fidentis.analyst.drawables.DrawableFeaturePoints;
import cz.fidentis.analyst.drawables.DrawableFpWeights;
import cz.fidentis.analyst.feature.FeaturePointType.FeaturePointCategory;
import cz.fidentis.analyst.feature.RelationToMesh.FpPositionType;
import cz.fidentis.analyst.gui.builder.PanelBuilder;
import java.awt.Color;
import java.awt.Dimension;
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.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

/**
 * GUI element to show list of feature points in control panels.
 *
 * @author Lukas Machalek
 */
public class FeaturePointListPanel extends JPanel {

    /**
     * Actions Triggered by Checkbox.
     */
    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";

    public static final String ACTION_COMMAND_SPINSLIDER_SLIDER = "slider changed";
    public static final String ACTION_COMMAND_SPINSLIDER_SPINNER = "spinnder chganged";

    /**
     * Actions trigger by Buttons.
     */
    public static final String ACTION_COMMAND_FEATURE_POINT_SELECT_ALL = "select all feature points";
    public static final String ACTION_COMMAND_FEATURE_POINT_SELECT_NONE = "select no feature points";
    public static final String ACTION_COMMAND_FEATURE_POINT_SELECT_INVERT = "invert selection of feature points";
    public static final String ACTION_COMMAND_FEATURE_POINT_SELECT_SIGNIFICANT = "Select significant feature points";

    /**
     * Actions trigger by Color buttons.
     */
    public static final String ACTION_CHANGE_TYPE_COLOR = "changes color of a selected type of feature point";

    /**
     * Lists and Map to save swing components.
     */
    private List<JButton> colorIndicators;
    private List<JCheckBox> featurePointCheckBoxes;
    private List<SpinSlider> fpSpinSliders;
    private Map<FeaturePointType, JLabel> featurePointStats;

    /**
     * Booleans representing presence of different swing components in the list.
     */
    private boolean hasColorIndicators;
    private boolean hasSpinSliders;
    private boolean hasValues;
    private boolean hasInfo;

    /**
     * Constructor.
     *
     * @param action Action listener
     * @param featurePoints List of all feature points
     */
    public void initCustom(ActionListener action, DrawableFeaturePoints featurePoints, boolean addColorIndicator, boolean addSpinSlider, boolean addValue, boolean addInfo) {
        PanelBuilder builder = new PanelBuilder(this);
        builder.setPanelBorder("Featurepoint list")
                .createSubPanel()
                .setLeftInset(4)
                .setTopInset(4);

        initLists(featurePoints);

        hasColorIndicators = addColorIndicator;
        hasSpinSliders = addSpinSlider;
        hasValues = addValue;
        hasInfo = addInfo;

        for (int i = 0; i < featurePoints.getFeaturePoints().size(); i++) {
            addRow(builder, i, action, featurePoints, hasColorIndicators, hasSpinSliders, hasValues, hasInfo);
            builder.nextLine();
        }

        builder.addScrollSubPanel(builder.getPrefferedSize().width + 60, 300).nextLine();

        createButtonPanel(action, builder);

        if (hasColorIndicators) {
            createColorPalletePanel(featurePoints, builder);
        }
    }

    private void initLists(DrawableFeaturePoints featurePoints) {
        int fpCount = featurePoints.getFeaturePoints().size();

        featurePointCheckBoxes = new ArrayList<>(fpCount);
        colorIndicators = new ArrayList<>(fpCount);
        fpSpinSliders = new ArrayList<>(fpCount);
        featurePointStats = new HashMap<>(fpCount);
    }

    private void createButtonPanel(ActionListener action, PanelBuilder builder) {
        builder.createSubPanel();

        builder.setLeftInset(8).setTopInset(4);

        JButton selectButton = (JButton) builder.addButton("Select all").getElement();
        JButton deselectButton = (JButton) builder.addButton("Deselect all").getElement();
        JButton significantButton = (JButton) builder.nextLine().addButton("Select significant").getElement();
        JButton invertButton = (JButton) builder.addButton("Invert selection").getElement();

        selectButton.addActionListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_SELECT_ALL, featurePointCheckBoxes));
        deselectButton.addActionListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_SELECT_NONE, featurePointCheckBoxes));
        significantButton.addActionListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_SELECT_SIGNIFICANT, featurePointCheckBoxes));
        invertButton.addActionListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_SELECT_INVERT, featurePointCheckBoxes));

        builder.addSubPanel()
                .nextLine();
    }

    private void createColorPalletePanel(DrawableFeaturePoints fpoints, PanelBuilder builder) {
        Map<String, Color> colors = new HashMap<>();

        colors.put("feature point is too far from the face", fpoints.getOffTheMeshColor());
        colors.put("custom feature point color", fpoints.getCustomFpColor());
        colors.put("feature point is closer than the distance threshold", fpoints.getOnTheMeshColor());
        colors.put("feature point is further than the distance threshold", fpoints.getCloseToMeshColor());

        builder.addColorListPanel(colors, "Feature point colors:");
    }

    private void addRow(PanelBuilder builder, int row, ActionListener action, DrawableFeaturePoints featurePoints, boolean addColorIndicator, boolean addSpinSlider, boolean addValue, boolean addInfo) {
        FeaturePoint featurePoint = featurePoints.getFeaturePoints().get(row);

        //Add checkbox
        JCheckBox fpBox = (JCheckBox) builder.addCheckBox(false).getElement();
        fpBox.addActionListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_SELECT, row));
        addHoverListener(fpBox, row, action);

        featurePointCheckBoxes.add(fpBox);

        //Add name Label
        JLabel name = (JLabel) builder.addLabel(featurePoint.getFeaturePointType().getName()).getElement();
        name.setToolTipText("<html><body><p style='width: 250px;'>" + featurePoint.getFeaturePointType().getInfo() + "</p></body></html>");

        //Add spin slider
        if (addSpinSlider) {
            SpinSlider spinSlider = (SpinSlider) builder.addSpinSlider().getElement();
            spinSlider.initDouble(DrawableFpWeights.FPW_DEFAULT_SIZE, 0, 100, 1);
            spinSlider.setContinousSync(false);

            spinSlider.addSliderListener(createListener(action, ACTION_COMMAND_SPINSLIDER_SLIDER, row));
            spinSlider.addSpinnerListener((ActionEvent e) -> {
                if (!fpBox.isSelected()) {
                    return; // Recompute only if the feature point is selected
                }
                action.actionPerformed(new ActionEvent(
                        e.getSource(),
                        ActionEvent.ACTION_PERFORMED,
                        ACTION_COMMAND_SPINSLIDER_SPINNER
                ));
            });

            fpSpinSliders.add(spinSlider);
        }

        //Add value label
        if (addValue) {
            featurePointStats.put(featurePoints.getFeaturePoints().get(row).getFeaturePointType(), (JLabel) builder.addLabel("").getElement());
        }

        //Add label for color indicators
        if (addColorIndicator) {
            Color color = featurePoints.getOffTheMeshColor();
            FpPositionType fpType = featurePoint.getRelationToMesh().getPositionType(featurePoints.getDistanceThreshold());

            String tooltip = "feature point is too far from the face";

            if (featurePoint.getFeaturePointType().getCategory() == FeaturePointCategory.CUSTOM) {
                tooltip = "custom feature point";
                color = featurePoints.getCustomFpColor();
            } else if (fpType == FpPositionType.ON_THE_MESH) {
                tooltip = "feature point is closer than the distance threshold";
                color = featurePoints.getOnTheMeshColor();
            } else if (fpType == FpPositionType.CLOSE_TO_MESH) {
                color = featurePoints.getCloseToMeshColor();
                tooltip = "feature point is further than the distance threshold";
            }

            JButton colorIndicator = new JButton();

            colorIndicator.setToolTipText(tooltip);

            colorIndicator.setContentAreaFilled(false);
            colorIndicator.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
            colorIndicator.setOpaque(true);
            colorIndicator.setBackground(color);
            colorIndicator.setForeground(Color.GRAY);

            colorIndicator.setPreferredSize(new Dimension(10, 10));

            colorIndicator.addActionListener(createListener(action, ACTION_CHANGE_TYPE_COLOR));

            colorIndicators.add((JButton) builder.addButton(colorIndicator).getElement());

        }

        //Add info button
        if (addInfo) {
            JButton infoButton = (JButton) builder.addButton(new javax.swing.ImageIcon(getClass().getResource("/info.png")), false)
                    .getElement();

            infoButton.addActionListener((ActionEvent e) -> {
                JOptionPane.showMessageDialog(this,
                        "<html><body><p style='width: 350px;'>" + featurePoint.getFeaturePointType().getInfo() + "</p></body></html>",
                        featurePoint.getFeaturePointType().getName(),
                        JOptionPane.INFORMATION_MESSAGE);
            });
        }

    }

    /**
     * Refreshes the panel
     *
     * @param action
     * @param featurePoints
     */
    public void refreshPanel(ActionListener action, DrawableFeaturePoints featurePoints, List<FeaturePointType> selectedFeaturePoints) {
        removeAll();
        initCustom(action, featurePoints, hasColorIndicators, hasSpinSliders, hasValues, hasInfo);
        revalidate();
        repaint();
        selectFeaturePoints(featurePoints.getFeaturePoints(), selectedFeaturePoints);
    }

    /**
     * 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 final ActionListener createListener(ActionListener action, String command) {
        return new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                action.actionPerformed(new ActionEvent(
                        e.getSource(),
                        ActionEvent.ACTION_PERFORMED,
                        command)
                );
            }
        };
    }

    /**
     * Adds hover listener to the checkbox.
     *
     * @param checkbox checkbox for feature point
     * @param index index of the feature point
     * @param action action listener
     */
    private void addHoverListener(JCheckBox checkbox, int index, ActionListener action) {
        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));
            }
        });
    }

    /**
     * (De)selects feature points
     *
     * @param featurePoints all feature points
     * @param selectedFeaturePoints selected feature points
     */
    public void selectFeaturePoints(List<FeaturePoint> featurePoints, List<FeaturePointType> selectedFeaturePoints) {
        for (int i = 0; i < featurePoints.size(); i++) {
            if (selectedFeaturePoints.contains(featurePoints.get(i).getFeaturePointType())) {
                featurePointCheckBoxes.get(i).setSelected(true);
            } else {
                featurePointCheckBoxes.get(i).setSelected(false);
            }
        }
    }

    /**
     * Updates GUI elements that display the weights of feature points used to
     * calculate the weighted Hausdorff distance.
     *
     * @param featurePointValues Map of feature point types and their weights
     */
    public void updateFeaturePointValues(Map<FeaturePointType, Double> featurePointValues) {
        featurePointStats.forEach((fpType, fpWeightLabel) -> {
            final Double fpValue = featurePointValues.get(fpType);
            fpWeightLabel.setText(fpValue == null ? "" : String.format("%.3f", fpValue));
        });
    }

    /**
     * (De)selects all feature points for the computation of the weighted
     * Hausdorff distance.
     *
     * @param selected {@code true} if all feature point checkboxes are to be
     * selected, {@code false} otherwise
     */
    public void selectAllFeaturePoints(boolean selected) {
        featurePointCheckBoxes.forEach(fpCheckBox
                -> fpCheckBox.setSelected(selected)
        );
    }

    /**
     * Inverts current selection of feature points.
     */
    public void invertAllFeaturePoints() {
        featurePointCheckBoxes.forEach(fpCheckBox
                -> fpCheckBox.setSelected(!fpCheckBox.isSelected())
        );
    }

    /**
     * (De)selects given feature point.
     *
     * @param index Index of the feature point
     * @param selected {@code true} if all feature point checkboxes are to be
     * selected, {@code false} otherwise
     */
    public void selectFeaturePoint(int index, boolean selected) {
        if (index >= 0 && index < featurePointCheckBoxes.size()) {
            featurePointCheckBoxes.get(index).setSelected(selected);
        }
    }

    /**
     * Returns current size of the sphere of index-th feature point
     *
     * @param index Index of the feature point
     * @return Sphere size, {@code NaN} on error.
     */
    public double getSpinSliderValue(int index) {
        if (index < 0 || index >= fpSpinSliders.size()) {
            return Double.NaN;
        }
        SpinSlider sl = fpSpinSliders.get(index);
        return (Double) sl.getValue();
    }

}
+163 −0
Original line number Diff line number Diff line
package cz.fidentis.analyst.gui.elements;

import cz.fidentis.analyst.feature.FeaturePoint;
import cz.fidentis.analyst.gui.task.LoadedActionEvent;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.List;

/**
 * Generic class for a list of feature points.
 *
 * @param <T> content of a row
 * @author Radek Oslejsek
 */
public abstract class FpListAbstractPanel<T extends FpListAbstractPanel.Row> extends JPanel {

    /**
     * Actions triggered during the user interaction. They should be handled by the associated action listener.
     */
    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";

    /**
     * A single raw of the list with mandatory checkpoint and the feature point.
     * This class can be extended in subclasses to add additional data to each row.
     *
     * @author Radek Oslejsek
     */
    protected class Row {
        private FeaturePoint featurePoint;
        private JCheckBox checkbox;

        /**
         * Constructor.
         *
         * @param featurePoint feature point
         */
        public Row(FeaturePoint featurePoint) {
            this.featurePoint = featurePoint;
            this.checkbox =  new JCheckBox(featurePoint.getFeaturePointType().getName());
        }

        public FeaturePoint getFeaturePoint() {
            return featurePoint;
        }

        public JCheckBox getCheckbox() {
            return checkbox;
        }

        /**
         * This method adds a row into a given panel.
         *
         * @param panel a panel storing rows
         * @param c Grid Bag layout constraint
         */
        public void addToPanel(JPanel panel, GridBagConstraints c) {
            c.anchor = GridBagConstraints.LINE_START;
            panel.add(checkbox, c);
            c.gridx++;
        }
    }

    /**
     * Action listener handling user events performed with the list of feature points.
     */
    private transient ActionListener actionListener;

    private final List<T> rows = new ArrayList<>();

    protected List<T> getRows() {
        return Collections.unmodifiableList(rows);
    }

    protected ActionListener getActionListener() {
        return actionListener;
    }

    protected void initComponents(ActionListener action, List<T> rows) {
        Objects.requireNonNull(action);

        rows.forEach(row -> row.getCheckbox().setToolTipText("<html><body><p style='width: 250px;'>" +
                row.getFeaturePoint().getFeaturePointType().getInfo() + "</p></body></html>"));

        this.actionListener = action;
        this.rows.clear();
        this.rows.addAll(rows);

        // build GUI
        removeAll();
        setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        c.insets = new Insets(0, 2, 0, 2);
        c.gridx = 0;
        c.gridy = 0;
        getRows().forEach(row -> {
            row.addToPanel(this, c);
            c.gridx = 0;
            c.gridy++;
        });

        setCheckboxActions();
        setHoverActions();
    }

    /**
     * If the feature point is (un)checked, then {@code ACTION_COMMAND_FEATURE_POINT_SELECT}
     * is triggered in the action listener.
     */
    protected void setCheckboxActions() {
        for (int i = 0; i < rows.size(); i++) {
            int index = i;
            rows.get(i).getCheckbox().addActionListener(new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    actionListener.actionPerformed(new LoadedActionEvent(
                            e.getSource(),
                            ActionEvent.ACTION_PERFORMED,
                            ACTION_COMMAND_FEATURE_POINT_SELECT,
                            index
                    ));
                }
            });
        }
    }

    /**
     * If the mouse pointer hovers over a feature point, then {@code ACTION_COMMAND_FEATURE_POINT_HOVER_IN}
     * and {@code ACTION_COMMAND_FEATURE_POINT_HOVER_OUT} are triggered in the action listener.
     */
    protected void setHoverActions() {
        for (int i = 0; i < rows.size(); i++) {
            int index = i;
            rows.get(i).getCheckbox().addMouseListener(new MouseAdapter() { // highlight spheres on mouse hover
                @Override
                public void mouseEntered(MouseEvent e) {
                    actionListener.actionPerformed(new LoadedActionEvent(
                            e.getSource(),
                            ActionEvent.ACTION_PERFORMED,
                            ACTION_COMMAND_FEATURE_POINT_HOVER_IN,
                            index));
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    actionListener.actionPerformed(new LoadedActionEvent(
                            e.getSource(),
                            ActionEvent.ACTION_PERFORMED,
                            ACTION_COMMAND_FEATURE_POINT_HOVER_OUT,
                            index));
                }
            });
        }
    }

}
Loading