@ -1,12 +1,14 @@
package visualiser.model ;
package visualiser.model ;
import javafx.application.Platform ;
import javafx.collections.ObservableList ;
import javafx.collections.ObservableList ;
import javafx.fxml.FXML ;
import javafx.scene.chart.LineChart ;
import javafx.scene.chart.LineChart ;
import javafx.scene.chart.NumberAxis ;
import javafx.scene.chart.NumberAxis ;
import javafx.scene.chart.XYChart ;
import javafx.scene.chart.XYChart ;
import javafx.scene.paint.Color ;
import javafx.scene.paint.Color ;
import java.util.List ;
/ * *
/ * *
* Class to process and modify a sparkline display . This display keeps visual
* Class to process and modify a sparkline display . This display keeps visual
@ -18,6 +20,11 @@ import javafx.scene.paint.Color;
* /
* /
public class Sparkline {
public class Sparkline {
/ * *
* The race to observe .
* /
private VisualiserRace race ;
/ * *
/ * *
* The boats to observe .
* The boats to observe .
* /
* /
@ -29,43 +36,39 @@ public class Sparkline {
* /
* /
private Integer legNum ;
private Integer legNum ;
//TODO comment
private Integer sparkLineNumber = 0 ;
/ * *
/ * *
* The linchart to plot sparklines on .
* The linchart to plot sparklines on .
* /
* /
@FXML LineChart < Number , Number > sparklineChart ;
private LineChart < Number , Number > sparklineChart ;
/ * *
/ * *
* The x axis of the sparkline chart .
* The x axis of the sparkline chart .
* /
* /
@FXML NumberAxis xAxis ;
private NumberAxis xAxis ;
/ * *
/ * *
* The y axis of the sparkline chart .
* The y axis of the sparkline chart .
* /
* /
@FXML NumberAxis yAxis ;
private NumberAxis yAxis ;
/ * *
/ * *
* Constructor to set up initial sparkline ( LineChart ) object
* Constructor to set up initial sparkline ( LineChart ) object
* @param boats boats to display on the sparkline
* @param race The race to listen to .
* @param legNum total number of legs in the race
* @param sparklineChart JavaFX LineChart for the sparkline .
* @param sparklineChart javaFX LineChart for the sparkline
* /
* /
public Sparkline ( ObservableList < VisualiserBoat > boats , Integer legNum ,
public Sparkline ( VisualiserRace race , LineChart < Number , Number > sparklineChart ) {
LineChart < Number , Number > sparklineChart ) {
this . race = race ;
this . boats = boats ;
this . boats = race . getBoats ( ) ;
this . legNum = race . getLegCount ( ) ;
this . sparklineChart = sparklineChart ;
this . sparklineChart = sparklineChart ;
this . legNum = legNum ;
this . yAxis = ( NumberAxis ) sparklineChart . getYAxis ( ) ;
this . yAxis = ( NumberAxis ) sparklineChart . getYAxis ( ) ;
this . xAxis = ( NumberAxis ) sparklineChart . getXAxis ( ) ;
this . xAxis = ( NumberAxis ) sparklineChart . getXAxis ( ) ;
createSparkline ( ) ;
createSparkline ( ) ;
//TODO refactor
//what we probably want to do is listen to the boats list, and when it updates, we update an observable list of series with new data. the linechart listens to the list of series.
}
}
@ -74,105 +77,105 @@ public class Sparkline {
* A data series for each boat in the race is added .
* A data series for each boat in the race is added .
* Position numbers are displayed .
* Position numbers are displayed .
* /
* /
p ublic void createSparkline ( ) {
p rivate void createSparkline ( ) {
// NOTE: Y axis is in negatives to display correct positions
// NOTE: Y axis is in negatives to display correct positions
// all boats start in 'last' place
//For each boat...
for ( int i = 0 ; i < boats . size ( ) ; i + + ) {
for ( VisualiserBoat boat : this . boats ) {
XYChart . Series < Number , Number > series = new XYChart . Series ( ) ;
series . getData ( ) . add ( new XYChart . Data ( 0 , - boats . size ( ) ) ) ;
//Create data series for each boat.
series . getData ( ) . add ( new XYChart . Data ( 0 , - boats . size ( ) ) ) ;
XYChart . Series < Number , Number > series = new XYChart . Series < > ( ) ;
//All boats start in "last" place.
series . getData ( ) . add ( new XYChart . Data < > ( 0 , boats . size ( ) ) ) ;
//Listen for changes in the boat's leg - we only update the graph when it changes leg.
boat . legProperty ( ) . addListener (
( observable , oldValue , newValue ) - > {
//Get the data to plot.
List < VisualiserBoat > boatOrder = race . getLegCompletionOrder ( ) . get ( oldValue ) ;
//Find boat position in list.
int boatPosition = boatOrder . indexOf ( boat ) + 1 ;
//Get leg number.
int legNumber = oldValue . getLegNumber ( ) + 1 ;
//Create new data point for boat's position at the new leg.
XYChart . Data < Number , Number > dataPoint = new XYChart . Data < > ( legNumber , boatPosition ) ;
//Add to series.
Platform . runLater ( ( ) - > series . getData ( ) . add ( dataPoint ) ) ;
} ) ;
//Add to chart.
sparklineChart . getData ( ) . add ( series ) ;
sparklineChart . getData ( ) . add ( series ) ;
sparklineChart . getData ( ) . get ( i ) . getNode ( ) . setStyle ( "-fx-stroke: " +
"" + colourToHex ( boats . get ( i ) . getColor ( ) ) + ";" ) ;
//Color using boat's color. We need to do this after adding the series to a chart, otherwise we get null pointer exceptions.
series . getNode ( ) . setStyle ( "-fx-stroke: " + colourToHex ( boat . getColor ( ) ) + ";" ) ;
}
}
sparklineChart . setCreateSymbols ( false ) ;
sparklineChart . setCreateSymbols ( false ) ;
// set x axis details
// S et x axis details
xAxis . setAutoRanging ( false ) ;
xAxis . setAutoRanging ( false ) ;
xAxis . setTickMarkVisible ( false ) ;
xAxis . setTickMarkVisible ( false ) ;
xAxis . setTickLabelsVisible ( false ) ;
xAxis . setTickLabelsVisible ( false ) ;
xAxis . setMinorTickVisible ( false ) ;
xAxis . setMinorTickVisible ( false ) ;
xAxis . setUpperBound ( ( boats . size ( ) + 1 ) * legNum ) ;
xAxis . setLowerBound ( 0 ) ;
xAxis . setTickUnit ( ( boats . size ( ) + 1 ) * legNum ) ;
xAxis . setUpperBound ( legNum + 2 ) ;
xAxis . setTickUnit ( 1 ) ;
// set y axis details
// S et y axis details
yAxis . setLowerBound ( - ( boats . size ( ) + 1 ) ) ;
yAxis . setLowerBound ( boats . size ( ) + 1 ) ;
yAxis . setUpperBound ( 0 ) ;
yAxis . setUpperBound ( 0 ) ;
yAxis . setAutoRanging ( false ) ;
yAxis . setAutoRanging ( false ) ;
yAxis . setLabel ( "Position in Race" ) ;
yAxis . setLabel ( "Position in Race" ) ;
yAxis . setTickUnit ( 1 ) ;
yAxis . setTickUnit ( - 1 ) ; //Negative tick reverses the y axis.
yAxis . setTickMarkVisible ( false ) ;
yAxis . setMinorTickVisible ( false ) ;
yAxis . setTickMarkVisible ( true ) ;
yAxis . setTickLabelsVisible ( true ) ;
yAxis . setTickMarkVisible ( true ) ;
yAxis . setMinorTickVisible ( true ) ;
// hide minus number from displaying on axis
/ * TODO FIX currently this doesn ' t work - I broke it : (
TODO only 0 and 7 get passed in to it for some reason
//Hide minus number from displaying on y axis.
yAxis . setTickLabelFormatter ( new NumberAxis . DefaultFormatter ( yAxis ) {
yAxis . setTickLabelFormatter ( new NumberAxis . DefaultFormatter ( yAxis ) {
@Override
@Override
public String toString ( Number value ) {
public String toString ( Number value ) {
if ( ( Double ) value = = 0.0
| | ( Double ) value < - boats . size ( ) ) {
return "" ;
}
else {
return String . format ( "%7.0f" , - value . doubleValue ( ) ) ;
}
}
} ) ;
}
//We only label the values between [1,boats.size()].
System . out . print ( "y axis: " + value . doubleValue ( ) ) ; //TEMP remove
if ( ( value . doubleValue ( ) > = 1 )
& & ( value . doubleValue ( ) < = boats . size ( ) ) ) {
System . out . println ( " is good" ) ; //TEMP
return String . format ( "%7.0f" , value . doubleValue ( ) ) ;
} else {
System . out . println ( " is bad" ) ; //TEMP
return "" ;
/ * *
* Updates the sparkline to display current boat positions .
* New points are plotted to represent each boat when required .
* @param boatsInRace current position of the boats in race
* /
public void updateSparkline ( ObservableList < VisualiserBoat > boatsInRace ) {
int placingVal = boatsInRace . size ( ) ;
sparkLineNumber + + ;
for ( int i = boatsInRace . size ( ) - 1 ; i > = 0 ; i - - ) {
for ( int j = boats . size ( ) - 1 ; j > = 0 ; j - - ) {
if ( boatsInRace . get ( i ) = = boats . get ( j ) ) {
// when a boat is on its first leg
if ( boatsInRace . get ( i ) . getCurrentLeg ( ) . getLegNumber ( ) = = 0 ) {
// adjust boats latest point on X axis
sparklineChart . getData ( ) . get ( j ) . getData ( ) . get ( 1 )
. setXValue ( sparkLineNumber ) ;
}
// when a boat first enters its second leg
else if ( boatsInRace . get ( i ) . getCurrentLeg ( ) . getLegNumber
( ) = = 1 & & sparklineChart . getData ( ) . get ( j ) . getData
( ) . size ( ) = = 2 ) {
// adjust boats position from start mark
sparklineChart . getData ( ) . get ( j ) . getData ( ) . get ( 1 )
. setYValue ( - placingVal ) ;
sparklineChart . getData ( ) . get ( j ) . getData ( ) . get ( 1 )
. setXValue ( sparkLineNumber ) ;
sparklineChart . getData ( ) . get ( j ) . getData ( ) . add ( new XYChart . Data < >
( sparkLineNumber , - placingVal ) ) ;
}
// plot new point for boats current position
else {
sparklineChart . getData ( ) . get ( j ) . getData ( ) . add
( new XYChart . Data < > ( sparkLineNumber , - placingVal ) ) ;
}
placingVal - = 1 ;
}
}
}
}
}
} ) ;
* /
}
}
/ * *
/ * *
* Converts a color to a hex string , starting with a { @literal # } symbol .
* Converts a color to a hex string , starting with a { @literal # } symbol .
* @param color
* @param color The color to convert .
* @return
* @return Hex string of the color ( e . g . , { @literal "#11AB4C" } ) .
* /
* /
private String colourToHex ( Color color ) {
private static String colourToHex ( Color color ) {
return String . format ( "#%02X%02X%02X" ,
return String . format ( "#%02X%02X%02X" ,
( int ) ( color . getRed ( ) * 255 ) ,
( int ) ( color . getRed ( ) * 255 ) ,
( int ) ( color . getGreen ( ) * 255 ) ,
( int ) ( color . getGreen ( ) * 255 ) ,