de.gsi.chart.renderer.spi.MountainRangeRenderer 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, ...).
package de.gsi.chart.renderer.spi;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.List;
import java.util.WeakHashMap;
import de.gsi.chart.Chart;
import de.gsi.chart.XYChart;
import de.gsi.chart.axes.Axis;
import de.gsi.chart.renderer.ErrorStyle;
import de.gsi.chart.renderer.Renderer;
import de.gsi.dataset.DataSet;
import de.gsi.dataset.DataSet3D;
import de.gsi.dataset.DataSetError;
import de.gsi.dataset.event.EventListener;
import de.gsi.dataset.utils.AssertUtils;
import de.gsi.dataset.utils.ProcessingProfiler;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.canvas.GraphicsContext;
/**
* @author rstein
*/
public class MountainRangeRenderer extends ErrorDataSetRenderer implements Renderer {
//private static final Logger LOGGER = LoggerFactory.getLogger(MountainRangeRenderer.class);
protected DoubleProperty mountainRangeOffset = new SimpleDoubleProperty(this, "mountainRangeOffset", 0.5);
private final ObservableList renderers = FXCollections.observableArrayList();
final ObservableList empty = FXCollections.observableArrayList();
private final WeakHashMap xWeakIndexMap = new WeakHashMap<>();
private final WeakHashMap yWeakIndexMap = new WeakHashMap<>();
private double zRangeMin = 0.0;
private double zRangeMax = 0.0;
private double mountainRaingeExtra = 0.0;
public MountainRangeRenderer(final double mountainRangeOffset) {
this();
setMountainRangeOffset(mountainRangeOffset);
}
public MountainRangeRenderer() {
super();
// renderers.add(this);
setDrawMarker(false);
setDrawBars(false);
setErrorType(ErrorStyle.NONE);
xWeakIndexMap.clear();
yWeakIndexMap.clear();
}
@Override
public void render(final GraphicsContext gc, final Chart chart, final int dataSetOffset,
final ObservableList datasets) {
final long start = ProcessingProfiler.getTimeStamp();
if (!(chart instanceof XYChart)) {
throw new InvalidParameterException(
"must be derivative of XYChart for renderer - " + this.getClass().getSimpleName());
}
final XYChart xyChart = (XYChart) chart;
if (!(xyChart.getYAxis() instanceof Axis)) {
throw new InvalidParameterException("y Axis not a Axis derivative, yAxis = " + xyChart.getYAxis());
}
final Axis yAxis = xyChart.getYAxis();
// make local copy and add renderer specific data sets
final List localDataSetList = new ArrayList<>(datasets);
localDataSetList.addAll(getDatasets());
// render in reverse order
for (int dataSetIndex = localDataSetList.size() - 1; dataSetIndex >= 0; dataSetIndex--) {
final DataSet dataSet = localDataSetList.get(dataSetIndex);
// detect and fish-out DataSet3D, ignore others
if (dataSet instanceof DataSet3D) {
dataSet.lock();
final DataSet3D mData = (DataSet3D) dataSet;
xWeakIndexMap.clear();
yWeakIndexMap.clear();
yAxis.setAutoGrowRanging(true);
zRangeMin = mData.getZRange().getMin();
zRangeMax = mData.getZRange().getMax();
mountainRaingeExtra = MountainRangeRenderer.this.getMountainRangeOffset();
yAxis.setLowerBound(zRangeMin);
yAxis.setUpperBound(zRangeMax * (1.0 + mountainRaingeExtra));
yAxis.forceRedraw();
final int yCountMax = mData.getYDataCount();
checkAndRecreateRenderer(yCountMax);
for (int index = yCountMax - 1; index > 0; index--) {
final MountainRangeRenderer.Demux3dTo2dDataSet dataSet2D = new Demux3dTo2dDataSet(mData, index);
renderers.get(index).getDatasets().setAll(dataSet2D);
renderers.get(index).render(gc, chart, 0, empty);
}
dataSet.unlock();
}
}
// super.render(gc, chart, empty);
ProcessingProfiler.getTimeDiff(start);
}
private void checkAndRecreateRenderer(final int nRenderer) {
if (renderers.size() == nRenderer) {
// all OK
return;
}
if (nRenderer > renderers.size()) {
for (int i = renderers.size(); i < nRenderer; i++) {
final ErrorDataSetRenderer newRenderer = new ErrorDataSetRenderer();
newRenderer.bind(this);
// do not show history sets in legend (single exception to
// binding)
newRenderer.showInLegendProperty().unbind();
newRenderer.setShowInLegend(false);
renderers.add(newRenderer);
}
return;
}
// require less renderer -> remove first until we have the right number
// needed
while (nRenderer < renderers.size()) {
renderers.remove(0);
}
}
/**
* Returns the mountainRangeOffset
.
*
* @return the mountainRangeOffset
, i.e. vertical offset between subsequent data sets
*/
public final double getMountainRangeOffset() {
return mountainRangeOffset.get();
}
/**
* Sets the dashSize
to the specified value. The dash is the horizontal line painted at the ends of the
* vertical line. It is not painted if set to 0.
*
* @param mountainRangeOffset tmountainRangeOffset
, i.e. vertical offset between subsequent data sets
* @return itself (fluent design)
*/
public final MountainRangeRenderer setMountainRangeOffset(final double mountainRangeOffset) {
AssertUtils.gtEqThanZero("mountainRangeOffset", mountainRangeOffset);
this.mountainRangeOffset.setValue(mountainRangeOffset);
return this;
}
public final DoubleProperty mountainRangeOffsetProperty() {
return mountainRangeOffset;
}
private class Demux3dTo2dDataSet implements DataSetError {
private final DataSet3D dataSet;
private final int yIndex;
private final int yMax;
private double yShift;
private List updateListener = new ArrayList<>();
public Demux3dTo2dDataSet(final DataSet3D sourceDataSet, final int selectedYIndex) {
super();
dataSet = sourceDataSet;
yIndex = selectedYIndex;
yMax = dataSet.getYDataCount();
yShift = 0.0; // just temporarily, will be recomputed
getYMax(); // #NOPMD locally needed to initialise, cannot be
// overwritten by user
}
@Override
public List updateEventListener() {
return updateListener;
}
@Override
public String getName() {
return dataSet.getName() + ":slice#" + yIndex;
}
@Override
public DataSet lock() {
// empty implementation since the superordinate DataSet3D lock is
// being held/protecting this data set
return this;
}
@Override
public DataSet unlock() {
// empty implementation since the superordinate DataSet3D lock is
// being held/protecting this data set
return this;
}
@Override
public DataSet setAutoNotifaction(final boolean flag) {
return dataSet.setAutoNotifaction(flag);
}
@Override
public boolean isAutoNotification() {
return dataSet.isAutoNotification();
}
@Override
public int getDataCount() {
return dataSet.getXDataCount();
}
@Override
public int getDataCount(final double xmin, final double xmax) {
return dataSet.getDataCount(xmin, xmax);
}
@Override
public double getX(final int i) {
return dataSet.getX(i);
}
@Override
public double getY(final int i) {
return dataSet.getZ(i, yIndex) + yShift;
}
@Override
public Double getUndefValue() {
return dataSet.getUndefValue();
}
@Override
public int getXIndex(final double x) {
// added computation of hash since this is recomputed quite often
// (and the same) for each slice
Integer ret = xWeakIndexMap.get(x);
if (ret == null) {
ret = dataSet.getXIndex(x);
xWeakIndexMap.put(x, ret);
}
return ret;
}
@Override
public int getYIndex(final double y) {
// added computation of hash since this is recomputed quite often
// (and the same) for each slice
Integer ret = yWeakIndexMap.get(y);
if (ret == null) {
ret = dataSet.getYIndex(y);
yWeakIndexMap.put(y, ret);
}
return ret;
}
@Override
public double getXMin() {
return dataSet.getXMin();
}
@Override
public double getXMax() {
return dataSet.getXMax();
}
@Override
public double getYMin() {
return dataSet.getZRange().getMin();
}
@Override
public double getYMax() {
yShift = mountainRaingeExtra * zRangeMax * yIndex / yMax;
return zRangeMax * (1 + mountainRaingeExtra);
}
@Override
public String getDataLabel(final int index) {
return dataSet.getDataLabel(index);
}
@Override
public String getStyle() {
return dataSet.getStyle();
}
@Override
public DataSet setStyle(final String style) {
return dataSet.setStyle(style);
}
@Override
public ErrorType getErrorType() {
return ErrorType.Y;
}
@Override
public double getXErrorNegative(final int index) {
return 0;
}
@Override
public double getXErrorPositive(final int index) {
return 0;
}
@Override
public double getYErrorNegative(final int index) {
return 0;
}
@Override
public double getYErrorPositive(final int index) {
return 0;
}
@Override
public String getStyle(final int index) {
return null;
}
}
}