1 /* 2 * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.scene.chart; 27 28 import javafx.animation.FadeTransition; 29 import javafx.animation.ParallelTransition; 30 import javafx.application.Platform; 31 import javafx.beans.NamedArg; 32 import javafx.collections.FXCollections; 33 import javafx.collections.ObservableList; 34 import javafx.scene.AccessibleRole; 35 import javafx.scene.Node; 36 import javafx.scene.layout.StackPane; 37 import javafx.util.Duration; 38 39 import com.sun.javafx.charts.Legend.LegendItem; 40 41 import java.util.Iterator; 42 43 /** 44 * Chart type that plots symbols for the data points in a series. 45 * @since JavaFX 2.0 46 */ 47 public class ScatterChart<X,Y> extends XYChart<X,Y> { 48 49 // -------------- CONSTRUCTORS ---------------------------------------------- 50 51 /** 52 * Construct a new ScatterChart with the given axis and data. 53 * 54 * @param xAxis The x axis to use 55 * @param yAxis The y axis to use 56 */ 57 public ScatterChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis) { 58 this(xAxis, yAxis, FXCollections.<Series<X, Y>>observableArrayList()); 59 } 60 61 /** 62 * Construct a new ScatterChart with the given axis and data. 63 * 64 * @param xAxis The x axis to use 65 * @param yAxis The y axis to use 66 * @param data The data to use, this is the actual list used so any changes to it will be reflected in the chart 67 */ 68 public ScatterChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis, @NamedArg("data") ObservableList<Series<X,Y>> data) { 69 super(xAxis,yAxis); 70 setData(data); 71 } 72 73 // -------------- METHODS ------------------------------------------------------------------------------------------ 74 75 /** @inheritDoc */ 76 @Override protected void dataItemAdded(Series<X,Y> series, int itemIndex, Data<X,Y> item) { 77 Node symbol = item.getNode(); 78 // check if symbol has already been created 79 if (symbol == null) { 80 symbol = new StackPane(); 81 symbol.setAccessibleRole(AccessibleRole.TEXT); 82 symbol.setAccessibleRoleDescription("Point"); 83 symbol.focusTraversableProperty().bind(Platform.accessibilityActiveProperty()); 84 item.setNode(symbol); 85 } 86 // set symbol styles 87 symbol.getStyleClass().setAll("chart-symbol", "series" + getData().indexOf(series), "data" + itemIndex, 88 series.defaultColorStyleClass); 89 // add and fade in new symbol if animated 90 if (shouldAnimate()) { 91 symbol.setOpacity(0); 92 getPlotChildren().add(symbol); 93 FadeTransition ft = new FadeTransition(Duration.millis(500),symbol); 94 ft.setToValue(1); 95 ft.play(); 96 } else { 97 getPlotChildren().add(symbol); 98 } 99 } 100 101 /** @inheritDoc */ 102 @Override protected void dataItemRemoved(final Data<X,Y> item, final Series<X,Y> series) { 103 final Node symbol = item.getNode(); 104 105 if (symbol != null) { 106 symbol.focusTraversableProperty().unbind(); 107 } 108 109 if (shouldAnimate()) { 110 // fade out old symbol 111 FadeTransition ft = new FadeTransition(Duration.millis(500),symbol); 112 ft.setToValue(0); 113 ft.setOnFinished(actionEvent -> { 114 getPlotChildren().remove(symbol); 115 removeDataItemFromDisplay(series, item); 116 symbol.setOpacity(1.0); 117 }); 118 ft.play(); 119 } else { 120 getPlotChildren().remove(symbol); 121 removeDataItemFromDisplay(series, item); 122 } 123 } 124 125 /** @inheritDoc */ 126 @Override protected void dataItemChanged(Data<X, Y> item) { 127 } 128 129 /** @inheritDoc */ 130 @Override protected void seriesAdded(Series<X,Y> series, int seriesIndex) { 131 // handle any data already in series 132 for (int j=0; j<series.getData().size(); j++) { 133 dataItemAdded(series,j,series.getData().get(j)); 134 } 135 } 136 137 /** @inheritDoc */ 138 @Override protected void seriesRemoved(final Series<X,Y> series) { 139 // remove all symbol nodes 140 if (shouldAnimate()) { 141 ParallelTransition pt = new ParallelTransition(); 142 pt.setOnFinished(event -> { 143 removeSeriesFromDisplay(series); 144 }); 145 for (final Data<X,Y> d : series.getData()) { 146 final Node symbol = d.getNode(); 147 // fade out old symbol 148 FadeTransition ft = new FadeTransition(Duration.millis(500),symbol); 149 ft.setToValue(0); 150 ft.setOnFinished(actionEvent -> { 151 getPlotChildren().remove(symbol); 152 symbol.setOpacity(1.0); 153 }); 154 pt.getChildren().add(ft); 155 } 156 pt.play(); 157 } else { 158 for (final Data<X,Y> d : series.getData()) { 159 final Node symbol = d.getNode(); 160 getPlotChildren().remove(symbol); 161 } 162 removeSeriesFromDisplay(series); 163 } 164 } 165 166 /** @inheritDoc */ 167 @Override protected void layoutPlotChildren() { 168 // update symbol positions 169 for (int seriesIndex=0; seriesIndex < getDataSize(); seriesIndex++) { 170 Series<X,Y> series = getData().get(seriesIndex); 171 for (Iterator<Data<X, Y>> it = getDisplayedDataIterator(series); it.hasNext(); ) { 172 Data<X, Y> item = it.next(); 173 double x = getXAxis().getDisplayPosition(item.getCurrentX()); 174 double y = getYAxis().getDisplayPosition(item.getCurrentY()); 175 if (Double.isNaN(x) || Double.isNaN(y)) { 176 continue; 177 } 178 Node symbol = item.getNode(); 179 if (symbol != null) { 180 final double w = symbol.prefWidth(-1); 181 final double h = symbol.prefHeight(-1); 182 symbol.resizeRelocate(x-(w/2), y-(h/2),w,h); 183 } 184 } 185 } 186 } 187 188 @Override 189 LegendItem createLegendItemForSeries(Series<X, Y> series, int seriesIndex) { 190 LegendItem legendItem = new LegendItem(series.getName()); 191 Node node = series.getData().isEmpty() ? null : series.getData().get(0).getNode(); 192 if (node != null) { 193 legendItem.getSymbol().getStyleClass().addAll(node.getStyleClass()); 194 } 195 return legendItem; 196 } 197 }