Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package de.gsi.chart;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.*;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.css.*;
import javafx.geometry.*;
import javafx.scene.CacheHint;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.paint.Paint;
import javafx.stage.Window;
import javafx.util.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.gsi.chart.axes.Axis;
import de.gsi.chart.axes.spi.AbstractAxis;
import de.gsi.chart.axes.spi.DefaultNumericAxis;
import de.gsi.chart.legend.Legend;
import de.gsi.chart.legend.spi.DefaultLegend;
import de.gsi.chart.plugins.ChartPlugin;
import de.gsi.chart.renderer.Renderer;
import de.gsi.chart.renderer.spi.LabelledMarkerRenderer;
import de.gsi.chart.ui.ChartLayoutAnimator;
import de.gsi.chart.ui.HiddenSidesPane;
import de.gsi.chart.ui.ResizableCanvas;
import de.gsi.chart.ui.ToolBarFlowPane;
import de.gsi.chart.ui.css.CssPropertyFactory;
import de.gsi.chart.ui.geometry.Corner;
import de.gsi.chart.ui.geometry.Side;
import de.gsi.chart.utils.FXUtils;
import de.gsi.dataset.DataSet;
import de.gsi.dataset.event.EventListener;
import de.gsi.dataset.utils.AssertUtils;
import de.gsi.dataset.utils.NoDuplicatesList;
import de.gsi.dataset.utils.ProcessingProfiler;
/**
* Chart designed primarily to display data traces using DataSet interfaces which are more flexible and efficient than
* the observable lists used by XYChart. Brief history: original design inspired by Oracle, extended by CERN (i.e.
* plugin concept/zoomer), modified to mitigate JavaFX performance issues and extended renderer
* concept/canvas-concept/interfaces/+more plugins by GSI. Refactored and re-write in 2018 to make it compatible with
* GPLv3 which -- in the spirit of 'Ship of Theseus' -- makes it de-facto a new development. Contributions, bug-fixes,
* and modifications are welcome. Hope you find this library useful and enjoy!
*
* @author original conceptual design by Oracle (2010, 2014)
* @author hbraeun, rstein, major refactoring, re-implementation and re-design
*/
public abstract class Chart extends HiddenSidesPane implements Observable {
private static final Logger LOGGER = LoggerFactory.getLogger(Chart.class);
private static final String CHART_CSS = Objects.requireNonNull(Chart.class.getResource("chart.css")).toExternalForm();
private static final CssPropertyFactory CSS = new CssPropertyFactory<>(Control.getClassCssMetaData());
private static final int DEFAULT_TRIGGER_DISTANCE = 50;
protected static final boolean DEBUG = false; // for more verbose debugging
protected BooleanBinding showingBinding;
protected final BooleanProperty showing = new SimpleBooleanProperty(this, "showing", false);
protected final ChangeListener super Boolean> showingListener = (ch2, o, n) -> showing.set(n);
/**
* When true any data changes will be animated.
*/
private final BooleanProperty animated = new SimpleBooleanProperty(this, "animated", true);
// TODO: Check whether 'this' or chart contents need to be added
/**
* Animator for animating stuff on the chart
*/
protected final ChartLayoutAnimator animator = new ChartLayoutAnimator(this);
/**
* When true the chart will display a legend if the chart implementation supports a legend.
*/
private final StyleableBooleanProperty legendVisible = CSS.createBooleanProperty(this, "legendVisible", true, () -> {
updateLegend(getDatasets(), getRenderers());
requestLayout();
});
// isCanvasChangeRequested is a recursion guard to update canvas only once
protected boolean isCanvasChangeRequested;
// layoutOngoing is a recursion guard to update canvas only once
protected boolean layoutOngoing;
protected final ObservableList axesList = FXCollections.observableList(new NoDuplicatesList<>());
private final Map pluginGroups = new ConcurrentHashMap<>();
private final ObservableList plugins = FXCollections.observableList(new LinkedList<>());
private final ObservableList datasets = FXCollections.observableArrayList();
protected final ObservableList allDataSets = FXCollections.observableArrayList();
protected final List listeners = new ArrayList<>();
protected final BooleanProperty autoNotification = new SimpleBooleanProperty(this, "autoNotification", true);
private final ObservableList renderers = FXCollections.observableArrayList();
{
getRenderers().addListener(this::rendererChanged);
}
protected final ResizableCanvas canvas = new ResizableCanvas();
// contains axes (left, bottom, top, right) panes & HiddenSidePane with the
// Canvas at it's centre
protected final GridPane axesAndCanvasPane = new GridPane();
protected final Group pluginsArea = Chart.createChildGroup();
protected boolean isAxesUpdate;
// containing the plugin handler/modifier
protected final ToolBarFlowPane toolBar = new ToolBarFlowPane(this);
protected final BooleanProperty toolBarPinned = new SimpleBooleanProperty(this, "toolBarPinned", false);
protected final HiddenSidesPane hiddenPane = new HiddenSidesPane();
protected final Pane plotBackground = new Pane();
protected final Pane plotForeGround = new Pane();
protected final Pane canvasForeground = new Pane();
protected final Map axesCorner = new ConcurrentHashMap<>(4);
protected final Map axesPane = new ConcurrentHashMap<>(4);
protected final Map measurementBar = new ConcurrentHashMap<>(4);
protected final Map titleLegendCorner = new ConcurrentHashMap<>(4);
protected final Map titleLegendPane = new ConcurrentHashMap<>(4);
{
for (final Corner corner : Corner.values()) {
axesCorner.put(corner, new StackPane()); // NOPMD - default init
titleLegendCorner.put(corner, new StackPane()); // NOPMD - default init
}
for (final Side side : Side.values()) {
titleLegendPane.put(side, side.isVertical() ? new ChartHBox() : new ChartVBox()); // NOPMD - default init
axesPane.put(side, side.isVertical() ? new ChartHBox() : new ChartVBox()); // NOPMD - default init
if (side == Side.CENTER_HOR || side == Side.CENTER_VER) {
axesPane.get(side).setMouseTransparent(true);
}
measurementBar.put(side, side.isVertical() ? new ChartHBox() : new ChartVBox()); // NOPMD - default
}
}
private final EventListener axisChangeListener = obs -> FXUtils.runFX(() -> axesInvalidated(obs));
protected final ListChangeListener axesChangeListenerLocal = this::axesChangedLocal;
protected final ListChangeListener axesChangeListener = this::axesChanged;
protected final ListChangeListener datasetChangeListener = this::datasetsChanged;
protected final EventListener dataSetDataListener = obs -> FXUtils.runFX(this::dataSetInvalidated);
protected final ListChangeListener pluginsChangedListener = this::pluginsChanged;
protected final ChangeListener super Window> windowPropertyListener = (ch1, oldWindow, newWindow) -> {
if (oldWindow != null) {
oldWindow.showingProperty().removeListener(showingListener);
}
if (newWindow == null) {
showing.set(false);
return;
}
newWindow.showingProperty().addListener(showingListener);
};
private final ChangeListener super Scene> scenePropertyListener = (ch, oldScene, newScene) -> {
if (oldScene == newScene) {
return;
}
if (oldScene != null) {
// remove listener
oldScene.windowProperty().removeListener(windowPropertyListener);
}
if (newScene == null) {
showing.set(false);
return;
}
// add listener
newScene.windowProperty().addListener(windowPropertyListener);
};
{
getDatasets().addListener(datasetChangeListener);
getAxes().addListener(axesChangeListener);
// update listener to propagate axes changes to chart changes
getAxes().addListener(axesChangeListenerLocal);
}
protected final Label titleLabel = new Label();
protected final StringProperty title = new StringPropertyBase() {
@Override
public Object getBean() {
return Chart.this;
}
@Override
public String getName() {
return "title";
}
@Override
protected void invalidated() {
titleLabel.setText(get());
}
};
/**
* The side of the chart where the title is displayed default Side.TOP
*/
private final StyleableObjectProperty titleSide = CSS.createObjectProperty(this, "titleSide", Side.TOP, false,
StyleConverter.getEnumConverter(Side.class), (oldVal, newVal) -> {
AssertUtils.notNull("Side must not be null", newVal);
for (final Side s : Side.values()) {
getTitleLegendPane(s).getChildren().remove(titleLabel);
}
getTitleLegendPane(newVal).getChildren().add(titleLabel);
return (newVal);
}, this::requestLayout);
/**
* The side of the chart where the title is displayed default Side.TOP
*/
private final StyleableObjectProperty measurementBarSide = CSS.createObjectProperty(this, "measurementBarSide", Side.RIGHT, false,
StyleConverter.getEnumConverter(Side.class), (oldVal, newVal) -> {
AssertUtils.notNull("Side must not be null", newVal);
return newVal;
}, this::requestLayout);
/**
* The side of the chart where the legend should be displayed default value Side.BOTTOM
*/
private final StyleableObjectProperty legendSide = CSS.createObjectProperty(this, "legendSide", Side.BOTTOM, false,
StyleConverter.getEnumConverter(Side.class), (oldVal, newVal) -> {
AssertUtils.notNull("Side must not be null", newVal);
final Legend legend = getLegend();
if (legend == null) {
return newVal;
}
for (final Side s : Side.values()) {
getTitleLegendPane(s).getChildren().remove(legend.getNode());
}
getTitleLegendPane(newVal).getChildren().add(legend.getNode());
legend.setVertical(newVal.isVertical());
return newVal;
}, this::requestLayout);
/**
* The node to display as the Legend. Subclasses can set a node here to be displayed on a side as the legend. If no
* legend is wanted then this can be set to null
*/
private final ObjectProperty