137
138 /**
139 * Construct a new Area Chart with the given axis and data
140 *
141 * @param xAxis The x axis to use
142 * @param yAxis The y axis to use
143 * @param data The data to use, this is the actual list used so any changes to it will be reflected in the chart
144 */
145 public AreaChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis, @NamedArg("data") ObservableList<Series<X,Y>> data) {
146 super(xAxis,yAxis);
147 setData(data);
148 }
149
150 // -------------- METHODS ------------------------------------------------------------------------------------------
151
152 private static double doubleValue(Number number) { return doubleValue(number, 0); }
153 private static double doubleValue(Number number, double nullDefault) {
154 return (number == null) ? nullDefault : number.doubleValue();
155 }
156
157 /** @inheritDoc */
158 @Override protected void updateAxisRange() {
159 final Axis<X> xa = getXAxis();
160 final Axis<Y> ya = getYAxis();
161 List<X> xData = null;
162 List<Y> yData = null;
163 if(xa.isAutoRanging()) xData = new ArrayList<X>();
164 if(ya.isAutoRanging()) yData = new ArrayList<Y>();
165 if(xData != null || yData != null) {
166 for(Series<X,Y> series : getData()) {
167 for(Data<X,Y> data: series.getData()) {
168 if(xData != null) xData.add(data.getXValue());
169 if(yData != null) yData.add(data.getYValue());
170 }
171 }
172 if(xData != null && !(xData.size() == 1 && getXAxis().toNumericValue(xData.get(0)) == 0)) {
173 xa.invalidateRange(xData);
174 }
175 if(yData != null && !(yData.size() == 1 && getYAxis().toNumericValue(yData.get(0)) == 0)) {
176 ya.invalidateRange(yData);
177 }
313 item.getCurrentX())),
314 new KeyFrame(Duration.millis(800), actionEvent -> {
315 item.setSeries(null);
316 getPlotChildren().remove(symbol);
317 removeDataItemFromDisplay(series, item);
318 },
319 new KeyValue(item.currentYProperty(),
320 item.getYValue(), Interpolator.EASE_BOTH),
321 new KeyValue(item.currentXProperty(),
322 item.getXValue(), Interpolator.EASE_BOTH))
323 );
324 }
325 } else {
326 item.setSeries(null);
327 getPlotChildren().remove(symbol);
328 removeDataItemFromDisplay(series, item);
329 }
330 //Note: better animation here, point should move from old position to new position at center point between prev and next symbols
331 }
332
333 /** @inheritDoc */
334 @Override protected void dataItemChanged(Data<X, Y> item) {
335 }
336
337 @Override protected void seriesChanged(ListChangeListener.Change<? extends Series> c) {
338 // Update style classes for all series lines and symbols
339 // Note: is there a more efficient way of doing this?
340 for (int i = 0; i < getDataSize(); i++) {
341 final Series<X,Y> s = getData().get(i);
342 Path seriesLine = (Path)((Group)s.getNode()).getChildren().get(1);
343 Path fillPath = (Path)((Group)s.getNode()).getChildren().get(0);
344 seriesLine.getStyleClass().setAll("chart-series-area-line", "series" + i, s.defaultColorStyleClass);
345 fillPath.getStyleClass().setAll("chart-series-area-fill", "series" + i, s.defaultColorStyleClass);
346 for (int j=0; j < s.getData().size(); j++) {
347 final Data<X,Y> item = s.getData().get(j);
348 final Node node = item.getNode();
349 if(node!=null) node.getStyleClass().setAll("chart-area-symbol", "series" + i, "data" + j, s.defaultColorStyleClass);
350 }
351 }
352 }
353
398 }
399 }
400 }
401 if (shouldAnimate()) animate(keyFrames.toArray(new KeyFrame[keyFrames.size()]));
402 }
403
404 @Override protected void seriesRemoved(final Series<X,Y> series) {
405 // remove series Y multiplier
406 seriesYMultiplierMap.remove(series);
407 // remove all symbol nodes
408 if (shouldAnimate()) {
409 Timeline tl = new Timeline(createSeriesRemoveTimeLine(series, 400));
410 tl.play();
411 } else {
412 getPlotChildren().remove(series.getNode());
413 for (Data<X,Y> d:series.getData()) getPlotChildren().remove(d.getNode());
414 removeSeriesFromDisplay(series);
415 }
416 }
417
418 /** @inheritDoc */
419 @Override protected void layoutPlotChildren() {
420 List<LineTo> constructedPath = new ArrayList<>(getDataSize());
421 for (int seriesIndex=0; seriesIndex < getDataSize(); seriesIndex++) {
422 Series<X, Y> series = getData().get(seriesIndex);
423 DoubleProperty seriesYAnimMultiplier = seriesYMultiplierMap.get(series);
424 double lastX = 0;
425 final ObservableList<Node> children = ((Group) series.getNode()).getChildren();
426 ObservableList<PathElement> seriesLine = ((Path) children.get(1)).getElements();
427 ObservableList<PathElement> fillPath = ((Path) children.get(0)).getElements();
428 seriesLine.clear();
429 fillPath.clear();
430 constructedPath.clear();
431 for (Iterator<Data<X, Y>> it = getDisplayedDataIterator(series); it.hasNext(); ) {
432 Data<X, Y> item = it.next();
433 double x = getXAxis().getDisplayPosition(item.getCurrentX());
434 double y = getYAxis().getDisplayPosition(
435 getYAxis().toRealValue(getYAxis().toNumericValue(item.getCurrentY()) * seriesYAnimMultiplier.getValue()));
436 constructedPath.add(new LineTo(x, y));
437 if (Double.isNaN(x) || Double.isNaN(y)) {
438 continue;
|
137
138 /**
139 * Construct a new Area Chart with the given axis and data
140 *
141 * @param xAxis The x axis to use
142 * @param yAxis The y axis to use
143 * @param data The data to use, this is the actual list used so any changes to it will be reflected in the chart
144 */
145 public AreaChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis, @NamedArg("data") ObservableList<Series<X,Y>> data) {
146 super(xAxis,yAxis);
147 setData(data);
148 }
149
150 // -------------- METHODS ------------------------------------------------------------------------------------------
151
152 private static double doubleValue(Number number) { return doubleValue(number, 0); }
153 private static double doubleValue(Number number, double nullDefault) {
154 return (number == null) ? nullDefault : number.doubleValue();
155 }
156
157 /** {@inheritDoc} */
158 @Override protected void updateAxisRange() {
159 final Axis<X> xa = getXAxis();
160 final Axis<Y> ya = getYAxis();
161 List<X> xData = null;
162 List<Y> yData = null;
163 if(xa.isAutoRanging()) xData = new ArrayList<X>();
164 if(ya.isAutoRanging()) yData = new ArrayList<Y>();
165 if(xData != null || yData != null) {
166 for(Series<X,Y> series : getData()) {
167 for(Data<X,Y> data: series.getData()) {
168 if(xData != null) xData.add(data.getXValue());
169 if(yData != null) yData.add(data.getYValue());
170 }
171 }
172 if(xData != null && !(xData.size() == 1 && getXAxis().toNumericValue(xData.get(0)) == 0)) {
173 xa.invalidateRange(xData);
174 }
175 if(yData != null && !(yData.size() == 1 && getYAxis().toNumericValue(yData.get(0)) == 0)) {
176 ya.invalidateRange(yData);
177 }
313 item.getCurrentX())),
314 new KeyFrame(Duration.millis(800), actionEvent -> {
315 item.setSeries(null);
316 getPlotChildren().remove(symbol);
317 removeDataItemFromDisplay(series, item);
318 },
319 new KeyValue(item.currentYProperty(),
320 item.getYValue(), Interpolator.EASE_BOTH),
321 new KeyValue(item.currentXProperty(),
322 item.getXValue(), Interpolator.EASE_BOTH))
323 );
324 }
325 } else {
326 item.setSeries(null);
327 getPlotChildren().remove(symbol);
328 removeDataItemFromDisplay(series, item);
329 }
330 //Note: better animation here, point should move from old position to new position at center point between prev and next symbols
331 }
332
333 /** {@inheritDoc} */
334 @Override protected void dataItemChanged(Data<X, Y> item) {
335 }
336
337 @Override protected void seriesChanged(ListChangeListener.Change<? extends Series> c) {
338 // Update style classes for all series lines and symbols
339 // Note: is there a more efficient way of doing this?
340 for (int i = 0; i < getDataSize(); i++) {
341 final Series<X,Y> s = getData().get(i);
342 Path seriesLine = (Path)((Group)s.getNode()).getChildren().get(1);
343 Path fillPath = (Path)((Group)s.getNode()).getChildren().get(0);
344 seriesLine.getStyleClass().setAll("chart-series-area-line", "series" + i, s.defaultColorStyleClass);
345 fillPath.getStyleClass().setAll("chart-series-area-fill", "series" + i, s.defaultColorStyleClass);
346 for (int j=0; j < s.getData().size(); j++) {
347 final Data<X,Y> item = s.getData().get(j);
348 final Node node = item.getNode();
349 if(node!=null) node.getStyleClass().setAll("chart-area-symbol", "series" + i, "data" + j, s.defaultColorStyleClass);
350 }
351 }
352 }
353
398 }
399 }
400 }
401 if (shouldAnimate()) animate(keyFrames.toArray(new KeyFrame[keyFrames.size()]));
402 }
403
404 @Override protected void seriesRemoved(final Series<X,Y> series) {
405 // remove series Y multiplier
406 seriesYMultiplierMap.remove(series);
407 // remove all symbol nodes
408 if (shouldAnimate()) {
409 Timeline tl = new Timeline(createSeriesRemoveTimeLine(series, 400));
410 tl.play();
411 } else {
412 getPlotChildren().remove(series.getNode());
413 for (Data<X,Y> d:series.getData()) getPlotChildren().remove(d.getNode());
414 removeSeriesFromDisplay(series);
415 }
416 }
417
418 /** {@inheritDoc} */
419 @Override protected void layoutPlotChildren() {
420 List<LineTo> constructedPath = new ArrayList<>(getDataSize());
421 for (int seriesIndex=0; seriesIndex < getDataSize(); seriesIndex++) {
422 Series<X, Y> series = getData().get(seriesIndex);
423 DoubleProperty seriesYAnimMultiplier = seriesYMultiplierMap.get(series);
424 double lastX = 0;
425 final ObservableList<Node> children = ((Group) series.getNode()).getChildren();
426 ObservableList<PathElement> seriesLine = ((Path) children.get(1)).getElements();
427 ObservableList<PathElement> fillPath = ((Path) children.get(0)).getElements();
428 seriesLine.clear();
429 fillPath.clear();
430 constructedPath.clear();
431 for (Iterator<Data<X, Y>> it = getDisplayedDataIterator(series); it.hasNext(); ) {
432 Data<X, Y> item = it.next();
433 double x = getXAxis().getDisplayPosition(item.getCurrentX());
434 double y = getYAxis().getDisplayPosition(
435 getYAxis().toRealValue(getYAxis().toNumericValue(item.getCurrentY()) * seriesYAnimMultiplier.getValue()));
436 constructedPath.add(new LineTo(x, y));
437 if (Double.isNaN(x) || Double.isNaN(y)) {
438 continue;
|