1 /* 2 * Copyright (c) 2008, 2015, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package ensemble.samples.charts.line.stock; 33 34 35 import javafx.animation.Animation; 36 import javafx.animation.KeyFrame; 37 import javafx.animation.Timeline; 38 import javafx.application.Application; 39 import javafx.event.ActionEvent; 40 import javafx.event.EventHandler; 41 import javafx.scene.Parent; 42 import javafx.scene.Scene; 43 import javafx.scene.chart.LineChart; 44 import javafx.scene.chart.NumberAxis; 45 import javafx.scene.chart.XYChart; 46 import javafx.stage.Stage; 47 import javafx.util.Duration; 48 49 50 /** 51 * A simulated stock line chart. 52 * 53 * @sampleName Stock Line Chart 54 * @preview preview.png 55 * @see javafx.scene.chart.LineChart 56 * @see javafx.scene.chart.NumberAxis 57 * @docUrl https://docs.oracle.com/javafx/2/charts/jfxpub-charts.htm Using JavaFX Charts Tutorial 58 */ 59 public class StockLineChartApp extends Application { 60 61 private LineChart<Number, Number> chart; 62 private XYChart.Series<Number, Number> hourDataSeries; 63 private XYChart.Series<Number, Number> minuteDataSeries; 64 private NumberAxis xAxis; 65 private Timeline animation; 66 private double hours = 0; 67 private double minutes = 0; 68 private double timeInHours = 0; 69 private double prevY = 10; 70 private double y = 10; 71 72 public StockLineChartApp() { 73 // create timeline to add new data every 60th of second 74 animation = new Timeline(); 75 animation.getKeyFrames() 76 .add(new KeyFrame(Duration.millis(1000 / 60), (ActionEvent actionEvent) -> { 77 // 6 minutes data per frame 78 for (int count = 0; count < 6; count++) { 79 nextTime(); 80 plotTime(); 81 } 82 })); 83 animation.setCycleCount(Animation.INDEFINITE); 84 } 85 86 public Parent createContent() { 87 xAxis = new NumberAxis(0, 24, 3); 88 final NumberAxis yAxis = new NumberAxis(0, 100, 10); 89 chart = new LineChart<>(xAxis, yAxis); 90 // setup chart 91 chart.getStylesheets().add(StockLineChartApp.class.getResource("StockLineChart.css").toExternalForm()); 92 chart.setCreateSymbols(false); 93 chart.setAnimated(false); 94 chart.setLegendVisible(false); 95 chart.setTitle("ACME Company Stock"); 96 xAxis.setLabel("Time"); 97 xAxis.setForceZeroInRange(false); 98 yAxis.setLabel("Share Price"); 99 yAxis 100 .setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis, "$", null)); 101 // add starting data 102 hourDataSeries = new XYChart.Series<>(); 103 hourDataSeries.setName("Hourly Data"); 104 minuteDataSeries = new XYChart.Series<>(); 105 minuteDataSeries.setName("Minute Data"); 106 // create some starting data 107 hourDataSeries.getData() 108 .add(new XYChart.Data<Number, Number>(timeInHours, prevY)); 109 minuteDataSeries.getData() 110 .add(new XYChart.Data<Number, Number>(timeInHours, prevY)); 111 for (double m = 0; m < (60); m++) { 112 nextTime(); 113 plotTime(); 114 } 115 chart.getData().add(minuteDataSeries); 116 chart.getData().add(hourDataSeries); 117 return chart; 118 } 119 120 private void nextTime() { 121 if (minutes == 59) { 122 hours++; 123 minutes = 0; 124 } else { 125 minutes++; 126 } 127 timeInHours = hours + ((1d / 60d) * minutes); 128 } 129 130 private void plotTime() { 131 if ((timeInHours % 1) == 0) { 132 // change of hour 133 double oldY = y; 134 y = prevY - 10 + (Math.random() * 20); 135 prevY = oldY; 136 while (y < 10 || y > 90) { 137 y = y - 10 + (Math.random() * 20); 138 } 139 hourDataSeries.getData() 140 .add(new XYChart.Data<Number, Number>(timeInHours, prevY)); 141 // after 25hours delete old data 142 if (timeInHours > 25) { 143 hourDataSeries.getData().remove(0); 144 } 145 // every hour after 24 move range 1 hour 146 if (timeInHours > 24) { 147 xAxis.setLowerBound(xAxis.getLowerBound() + 1); 148 xAxis.setUpperBound(xAxis.getUpperBound() + 1); 149 } 150 } 151 double min = (timeInHours % 1); 152 double randomPickVariance = Math.random(); 153 if (randomPickVariance < 0.3) { 154 double minY = prevY + ((y - prevY) * min) - 4 + (Math.random() * 8); 155 minuteDataSeries.getData().add(new XYChart.Data<Number, Number>(timeInHours, minY)); 156 } else if (randomPickVariance < 0.7) { 157 double minY = prevY + ((y - prevY) * min) - 6 + (Math.random() * 12); 158 minuteDataSeries.getData().add(new XYChart.Data<Number, Number>(timeInHours, minY)); 159 } else if (randomPickVariance < 0.95) { 160 double minY = prevY + ((y - prevY) * min) - 10 + (Math.random() * 20); 161 minuteDataSeries.getData().add(new XYChart.Data<Number, Number>(timeInHours, minY)); 162 } else { 163 double minY = prevY + ((y - prevY) * min) - 15 + (Math.random() * 30); 164 minuteDataSeries.getData().add(new XYChart.Data<Number, Number>(timeInHours, minY)); 165 } 166 // after 25hours delete old data 167 if (timeInHours > 25) { 168 minuteDataSeries.getData().remove(0); 169 } 170 } 171 172 public void play() { 173 animation.play(); 174 } 175 176 @Override 177 public void stop() { 178 animation.pause(); 179 } 180 181 @Override public void start(Stage primaryStage) throws Exception { 182 primaryStage.setScene(new Scene(createContent())); 183 primaryStage.show(); 184 play(); 185 } 186 187 /** 188 * Java main for when running without JavaFX launcher 189 * @param args command line arguments 190 */ 191 public static void main(String[] args) { 192 launch(args); 193 } 194 }