248 lines
8.8 KiB
Java
248 lines
8.8 KiB
Java
/*
|
|
Reversound is used to get the music sheet of a piece from a music file.
|
|
Copyright (C) 2014 Gabriel AUGENDRE
|
|
Copyright (C) 2014 Gabriel DIENY
|
|
Copyright (C) 2014 Arthur GAUCHER
|
|
Copyright (C) 2014 Gabriel LEPETIT-AIMON
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package gui.graphs;
|
|
|
|
/*
|
|
* Copyright (c) 2008, 2013 Oracle and/or its affiliates.
|
|
* All rights reserved. Use is subject to license terms.
|
|
*
|
|
* This file is available and licensed under the following license:
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the distribution.
|
|
* - Neither the name of Oracle Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
import javafx.collections.ObservableList;
|
|
import javafx.scene.Node;
|
|
import javafx.scene.chart.Axis;
|
|
import javafx.scene.chart.CategoryAxis;
|
|
import javafx.scene.chart.XYChart;
|
|
import javafx.scene.shape.Path;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Un graphe XY customisé pour afficher des barres de longueur varriable et de
|
|
* coordonnées variables.
|
|
*
|
|
* Adapté de CandleStickChart.java dans la démo Ensemble8 de javafx.
|
|
* Originellement définit comme : "A CandleChart is a style of candle-chart used primarily
|
|
* to describe price movements of a security, derivative,
|
|
* or currency over time.
|
|
* The Data Y value is used for the opening price and then the close, high and
|
|
* low values are stored in the Data's extra value property using a
|
|
* CandleExtraValues object."
|
|
*
|
|
* Modifié pour dessiner des barres horizontales plutôt que verticales et pour
|
|
* accepter un String comme donnée en Y
|
|
*/
|
|
public class TraceChart<X, String> extends XYChart<Number, String> {
|
|
|
|
// -------------- CONSTRUCTORS ----------------------------------------------
|
|
/**
|
|
* Construct a new CandleStickChart with the given axis.
|
|
*/
|
|
public TraceChart(Axis<Number> xAxis, Axis<String> yAxis) {
|
|
super(xAxis, yAxis);
|
|
setAnimated(false);
|
|
xAxis.setAnimated(false);
|
|
yAxis.setAnimated(false);
|
|
}
|
|
|
|
/**
|
|
* Construct a new TraceChart with the given axis and data.
|
|
*/
|
|
public TraceChart(Axis<Number> xAxis, Axis<String> yAxis, ObservableList<Series<Number, String>> data) {
|
|
this(xAxis, yAxis);
|
|
setData(data);
|
|
}
|
|
|
|
// -------------- METHODS ------------------------------------------------------------------------------------------
|
|
/** Called to update and layout the content for the plot */
|
|
@Override protected void layoutPlotChildren() {
|
|
// we have nothing to layout if no data is present
|
|
if (getData() == null) {
|
|
return;
|
|
}
|
|
// update candle positions
|
|
for (int seriesIndex = 0; seriesIndex < getData().size(); seriesIndex++) {
|
|
XYChart.Series<Number, String> series = getData().get(seriesIndex);
|
|
Iterator<Data<Number, String>> iter = getDisplayedDataIterator(series);
|
|
Path seriesPath = null;
|
|
if (series.getNode() instanceof Path) {
|
|
seriesPath = (Path) series.getNode();
|
|
seriesPath.getElements().clear();
|
|
}
|
|
while (iter.hasNext()) {
|
|
XYChart.Data<Number, String> item = iter.next();
|
|
double x = getXAxis().getDisplayPosition(getCurrentDisplayedXValue(item));
|
|
double y = getYAxis().getDisplayPosition(getCurrentDisplayedYValue(item));
|
|
Node itemNode = item.getNode();
|
|
BarExtraValues extra = (BarExtraValues) item.getExtraValue();
|
|
if (itemNode instanceof Bar && extra != null) {
|
|
Bar bar = (Bar) itemNode;
|
|
|
|
double close = getXAxis().getDisplayPosition((extra.getClose()));
|
|
// calculate candle width
|
|
double barHeight = -1;
|
|
if (getYAxis() instanceof CategoryAxis) {
|
|
CategoryAxis yAxis = (CategoryAxis) getYAxis();
|
|
barHeight = yAxis.getHeight()/yAxis.getCategories().size(); // no gap between ticks*/
|
|
}
|
|
// update candle
|
|
bar.update(close - x, barHeight, extra.getColor());
|
|
|
|
// position the candle
|
|
bar.setLayoutX(x);
|
|
bar.setLayoutY(y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override protected void dataItemChanged(XYChart.Data<Number, String> item) {
|
|
}
|
|
|
|
@Override protected void dataItemAdded(XYChart.Series<Number, String> series, int itemIndex, XYChart.Data<Number, String> item) {
|
|
Node bar = createBar(item);
|
|
getPlotChildren().add(bar);
|
|
// always draw average line on top
|
|
if (series.getNode() != null) {
|
|
series.getNode().toFront();
|
|
}
|
|
}
|
|
|
|
@Override protected void dataItemRemoved(XYChart.Data<Number, String> item, XYChart.Series<Number, String> series) {
|
|
final Node bar = item.getNode();
|
|
getPlotChildren().remove(bar);
|
|
}
|
|
|
|
@Override protected void seriesAdded(XYChart.Series<Number, String> series, int seriesIndex) {
|
|
// handle any data already in series
|
|
for (int j = 0; j < series.getData().size(); j++) {
|
|
XYChart.Data item = series.getData().get(j);
|
|
Node bar = createBar(item);
|
|
getPlotChildren().add(bar);
|
|
}
|
|
// create series path
|
|
Path seriesPath = new Path();
|
|
//useless :)
|
|
//seriesPath.getStyleClass().setAll("candlestick-average-line", "series" + seriesIndex);
|
|
series.setNode(seriesPath);
|
|
getPlotChildren().add(seriesPath);
|
|
}
|
|
|
|
@Override protected void seriesRemoved(XYChart.Series<Number, String> series) {
|
|
// remove all candle nodes
|
|
for (XYChart.Data<Number, String> d : series.getData()) {
|
|
final Node bar = d.getNode();
|
|
getPlotChildren().remove(bar);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new Bar node to represent a single data item
|
|
*/
|
|
private Node createBar(final XYChart.Data item) {
|
|
Node bar = item.getNode();
|
|
BarExtraValues extra = (BarExtraValues) item.getExtraValue();
|
|
// check if bar has already been created
|
|
if (bar instanceof Bar) {
|
|
((Bar) bar).setSeriesAndDataStyleClasses(extra.getColor());
|
|
} else {
|
|
bar = new Bar(extra.getColor());
|
|
item.setNode(bar);
|
|
}
|
|
return bar;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* This is called when the range has been invalidated and we need to update it. If the axis are auto
|
|
* ranging then we compile a list of all data that the given axis has to plot and call invalidateRange() on the
|
|
* axis passing it that data.
|
|
*/
|
|
@Override
|
|
protected void updateAxisRange() {
|
|
// For candle stick chart we need to override this method as we need to let the axis know that they need to be able
|
|
// to cover the whole area occupied by the high to low range not just its center data value
|
|
final Axis<Number> xAxis = getXAxis();
|
|
final Axis<String> yAxis = getYAxis();
|
|
List<Number> xData = null;
|
|
List<String> yData = null;
|
|
if (xAxis.isAutoRanging()) {
|
|
xData = new ArrayList<Number>();
|
|
}
|
|
if (yAxis.isAutoRanging()) {
|
|
yData = new ArrayList<String>();
|
|
}
|
|
if (xData != null || yData != null) {
|
|
for (XYChart.Series<Number, String> series : getData()) {
|
|
for (XYChart.Data<Number, String> data : series.getData()) {
|
|
if (yData != null) {
|
|
yData.add(data.getYValue());
|
|
}
|
|
if (xData != null) {
|
|
BarExtraValues extras = (BarExtraValues) data.getExtraValue();
|
|
if (extras != null) {
|
|
//yData.addNote(extras.getHigh());
|
|
//yData.addNote(extras.getLow());
|
|
} else {
|
|
xData.add(data.getXValue());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (xData != null) {
|
|
xAxis.invalidateRange(xData);
|
|
}
|
|
if (yData != null) {
|
|
yAxis.invalidateRange(yData);
|
|
}
|
|
}
|
|
}
|
|
}
|