All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.gsi.chart.plugins.AbstractSingleValueIndicator Maven / Gradle / Ivy

/**
 * Copyright (c) 2017 European Organisation for Nuclear Research (CERN), All Rights Reserved.
 */

package de.gsi.chart.plugins;

import de.gsi.chart.axes.Axis;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Cursor;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon;

/**
 * Plugin indicating a specific X or Y value as a line drawn on the plot area,
 * with an optional {@link #textProperty() text label} describing the value.
 *
 * @author mhrabia
 */
public abstract class AbstractSingleValueIndicator extends AbstractValueIndicator {
    /**
     * The default distance between the data point coordinates and mouse cursor
     * that triggers shifting the line.
     */
    protected static final int DEFAULT_PICKING_DISTANCE = 30;
    protected static final double MIDDLE_POSITION = 0.5;
    protected static final String STYLE_CLASS_LABEL = "value-indicator-label";
    protected static final String STYLE_CLASS_LINE = "value-indicator-line";
    protected static final String STYLE_CLASS_MARKER = "value-indicator-marker";
    protected static double triangleHalfWidth = 5.0;

    /**
     * Line indicating the value.
     */
    protected final Line line = new Line();
    protected final Line pickLine = new Line();

    /**
     * small triangle marker as handler to shift the line marker
     */
    protected final Polygon triangle = new Polygon();

    /**
     * Creates a new instance of AbstractSingleValueIndicator.
     * @param axis reference axis
     *                        
     * @param value
     *            a X value to be indicated
     * @param text
     *            the text to be shown by the label. Value of
     *            {@link #textProperty()}.
     */
    protected AbstractSingleValueIndicator(Axis axis, final double value, final String text) {
        super(axis, text);
        setValue(value);

        initLine();

        initTriangle();

        editableIndicatorProperty().addListener((ch, o, n) -> updateMouseListener(n));
        updateMouseListener(isEditable());

        // Need to add them so that at initialization of the stage the CCS is
        // applied and we can calculate label's
        // width and height
        getChartChildren().addAll(line, triangle, label);
    }

    private final DoubleProperty pickingDistance = new SimpleDoubleProperty(this, "pickingDistance",
            DEFAULT_PICKING_DISTANCE) {
        @Override
        protected void invalidated() {
            if (get() <= 0) {
                throw new IllegalArgumentException("The " + getName() + " must be a positive value");
            }
        }
    };

    /**
     * Distance of the mouse cursor from the line (in pixel) that should trigger
     * the moving of the line. By default initialized to
     * {@value #DEFAULT_PICKING_DISTANCE}.
     *
     * @return the picking distance property
     */
    public final DoubleProperty pickingDistanceProperty() {
        return pickingDistance;
    }

    /**
     * Sets the value of {@link #pickingDistanceProperty()}.
     *
     * @param distance
     *            the new picking distance
     */
    public final void setPickingDistance(final double distance) {
        pickingDistanceProperty().set(distance);
    }

    /**
     * Returns the value of the {@link #pickingDistanceProperty()}.
     *
     * @return the current picking distance
     */
    public final double getPickingDistance() {
        return pickingDistanceProperty().get();
    }

    private void initLine() {
        // mouse transparent if not editable
        line.setMouseTransparent(true);
        pickLine.setPickOnBounds(true);
        pickLine.setStroke(Color.TRANSPARENT);
        pickLine.setStrokeWidth(getPickingDistance());
        pickLine.mouseTransparentProperty().bind(editableIndicatorProperty().not());
        pickLine.setOnMousePressed(mouseEvent -> {
            /*
             * Record a delta distance for the drag and drop operation. Because
             * layoutLine() sets the start/end points we have to use these here.
             * It is enough to use the start point. For X indicators, start x
             * and end x are identical and for Y indicators start y and end y
             * are identical.
             */
            dragDelta.x = pickLine.getStartX() - mouseEvent.getX();
            dragDelta.y = pickLine.getStartY() - mouseEvent.getY();
            pickLine.setCursor(Cursor.MOVE);
            mouseEvent.consume();
        });
    }

    private void initTriangle() {
        triangle.visibleProperty().bind(editableIndicatorProperty());
        triangle.mouseTransparentProperty().bind(editableIndicatorProperty().not());
        triangle.setPickOnBounds(true);
        final double a = AbstractSingleValueIndicator.triangleHalfWidth;
        triangle.getPoints().setAll(-a, -a, -a, +a, +a, +a, +a, -a);
        triangle.setOnMousePressed(mouseEvent -> {
            /*
             * Record a delta distance for the drag and drop operation. Because
             * the whole node is translated in layoutMarker we use the layout
             * position here.
             */
            dragDelta.x = triangle.getLayoutX() - mouseEvent.getX();
            dragDelta.y = triangle.getLayoutY() - mouseEvent.getY();
            triangle.setCursor(Cursor.MOVE);
            mouseEvent.consume();
        });
    }

    private void updateMouseListener(final boolean state) {
        if (state) {
            pickLine.setOnMouseReleased(mouseEvent -> pickLine.setCursor(Cursor.HAND));
            pickLine.setOnMouseEntered(mouseEvent -> pickLine.setCursor(Cursor.HAND));
            triangle.setOnMouseReleased(mouseEvent -> triangle.setCursor(Cursor.HAND));
            triangle.setOnMouseEntered(mouseEvent -> triangle.setCursor(Cursor.HAND));
            label.setOnMouseReleased(mouseEvent -> label.setCursor(Cursor.HAND));
            label.setOnMouseEntered(mouseEvent -> label.setCursor(Cursor.HAND));
        } else {
            pickLine.setOnMouseReleased(null);
            pickLine.setOnMouseEntered(null);
            triangle.setOnMouseReleased(null);
            triangle.setOnMouseEntered(null);
            label.setOnMouseReleased(null);
            label.setOnMouseEntered(null);
        }
    }

    private final DoubleProperty value = new SimpleDoubleProperty(this, "value") {
        @Override
        protected void invalidated() {
            layoutChildren();
        }
    };

    /**
     * Value indicated by this plugin.
     *
     * @return value property
     */
    public final DoubleProperty valueProperty() {
        return value;
    }

    /**
     * Returns the indicated value.
     *
     * @return indicated value
     */
    public final double getValue() {
        return valueProperty().get();
    }

    /**
     * Sets the value that should be indicated.
     *
     * @param newValue
     *            value to be indicated
     */
    public final void setValue(final double newValue) {
        valueProperty().set(newValue);
    }

    private final DoubleProperty labelPosition = new SimpleDoubleProperty(this, "labelPosition", 0.5) {
        @Override
        protected void invalidated() {
            if (get() < 0 || get() > 1) {
                throw new IllegalArgumentException("labelPosition must be in rage [0,1]");
            }
            layoutChildren();
        }
    };

    /**
     * Relative position, between 0.0 (left, bottom) and 1.0 (right, top) of the
     * description {@link #textProperty() label} in the plot area.
     * 

* Default value: 0.5 *

* * @return labelPosition property */ public final DoubleProperty labelPositionProperty() { return labelPosition; } /** * Returns the value of the {@link #labelPositionProperty()}. * * @return the relative position of the {@link #textProperty() text label} */ public final double getLabelPosition() { return labelPositionProperty().get(); } /** * Sets the new value of the {@link #labelPositionProperty()}. * * @param value * the label position, between 0.0 and 1.0 (both inclusive) */ public final void setLabelPosition(final double value) { labelPositionProperty().set(value); } /** * Sets the line coordinates. * @param startX start x coordinate * @param startY start y coordinate * @param endX stop x coordinate * @param endY stop y coordinate */ protected void layoutLine(final double startX, final double startY, final double endX, final double endY) { line.setStartX(startX); line.setStartY(startY); line.setEndX(endX); line.setEndY(endY); pickLine.setStartX(startX); pickLine.setStartY(startY); pickLine.setEndX(endX); pickLine.setEndY(endY); addChildNodeIfNotPresent(line); addChildNodeIfNotPresent(pickLine); pickLine.toBack(); } /** * Sets the marker coordinates. * @param startX start x coordinate * @param startY start y coordinate * @param endX stop x coordinate * @param endY stop y coordinate */ protected void layoutMarker(final double startX, final double startY, final double endX, final double endY) { if (!triangle.isVisible()) { return; } triangle.setTranslateX(startX); triangle.setTranslateY(startY); addChildNodeIfNotPresent(triangle); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy