![JAR search and dependency download from the Maven repository](/logo.png)
de.gsi.chart.plugins.AbstractSingleValueIndicator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of chartfx-chart Show documentation
Show all versions of chartfx-chart Show documentation
This charting library ${project.artifactId}- is an extension
in the spirit of Oracle's XYChart and performance/time-proven JDataViewer charting functionalities.
Emphasis was put on plotting performance for both large number of data points and real-time displays,
as well as scientific accuracies leading to error bar/surface plots, and other scientific plotting
features (parameter measurements, fitting, multiple axes, zoom, ...).
The newest version!
/*
* Copyright (c) 2017 European Organisation for Nuclear Research (CERN), All Rights Reserved.
*/
package de.gsi.chart.plugins;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
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;
import de.gsi.chart.axes.Axis;
import de.gsi.dataset.event.EventListener;
import de.gsi.dataset.event.EventSource;
import de.gsi.dataset.event.UpdateEvent;
/**
* 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
implements EventSource, ValueIndicator {
/**
* 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;
private final transient AtomicBoolean autoNotification = new AtomicBoolean(true);
private final transient List updateListeners = Collections.synchronizedList(new LinkedList<>());
private boolean autoRemove = false;
/**
* 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();
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");
}
}
};
private final DoubleProperty value = new SimpleDoubleProperty(this, "value") {
@Override
protected void invalidated() {
layoutChildren();
}
};
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();
}
};
/**
* 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());
// remove triangle from chart foregrond when the chart is removed
chartProperty().addListener((p, o, n) -> {
if (o != null) {
o.getPlotForeground().getChildren().remove(triangle);
}
});
// 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, label);
this.value.addListener(
(ch, o, n) -> invokeListener(new UpdateEvent(this, "value changed to " + n + " for axis " + axis)));
}
@Override
public AtomicBoolean autoNotification() {
return autoNotification;
}
/**
* Returns the value of the {@link #labelPositionProperty()}.
*
* @return the relative position of the {@link #textProperty() text label}
*/
public final double getLabelPosition() {
return labelPositionProperty().get();
}
/**
* Returns the value of the {@link #pickingDistanceProperty()}.
*
* @return the current picking distance
*/
public final double getPickingDistance() {
return pickingDistanceProperty().get();
}
/**
* Returns the indicated value.
*
* @return indicated value
*/
@Override
public final double getValue() {
return valueProperty().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 -> {
if (mouseEvent.isPrimaryButtonDown()) {
/*
* 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);
triangle.setManaged(false); // prevent the triangle from relayouting the whole chart
triangle.setOpacity(0.7);
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();
});
}
/**
* @return {@code true} indicator should be removed if there is no listener attached to it
*/
public boolean isAutoRemove() {
return autoRemove;
}
/**
* 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;
}
/**
* 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(pickLine);
addChildNodeIfNotPresent(line);
}
/**
* 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);
triangle.toFront();
// triangle has to be put onto the plot foreground to be able to put it on top of axes
// is removed when the chart is changed
// addChildNodeIfNotPresent(triangle);
if (!getChart().getPlotForeground().getChildren().contains(triangle)) {
getChart().getPlotForeground().getChildren().add(triangle);
}
}
/**
* 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;
}
/**
* @param autoRemove {@code true} indicator should be removed if there is no listener attached to it
*/
public void setAutoRemove(boolean autoRemove) {
this.autoRemove = autoRemove;
}
/**
* 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 value of {@link #pickingDistanceProperty()}.
*
* @param distance the new picking distance
*/
public final void setPickingDistance(final double distance) {
pickingDistanceProperty().set(distance);
}
/**
* Sets the value that should be indicated.
*
* @param newValue value to be indicated
*/
@Override
public final void setValue(final double newValue) {
valueProperty().set(newValue);
}
@Override
public List updateEventListener() {
return updateListeners;
}
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);
}
}
/**
* Value indicated by this plugin.
*
* @return value property
*/
@Override
public final DoubleProperty valueProperty() {
return value;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy