package seng302.Model; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.chart.LineChart; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javafx.scene.paint.Color; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; /** * Class to process and modify a sparkline display. This display keeps visual * track of {@link seng302.Model.Boat Boats}s in a race and their current * placing position as they complete each {@link seng302.Model.Leg Leg} by * passing a course {@link seng302.Model.Marker Marker}.
* This sparkline is displayed using the * {@link seng302.Controllers.RaceController RaceController}. */ public class Sparkline { private ArrayList colours; private ArrayList startBoats = new ArrayList<>(); private Map boatColours = new HashMap<>(); private Integer legNum; private Integer sparkLineNumber = 0; @FXML LineChart sparklineChart; @FXML NumberAxis xAxis; @FXML NumberAxis yAxis; /** * Constructor to set up initial sparkline (LineChart) object * @param boats boats to display on the sparkline * @param legNum total number of legs in the race * @param sparklineChart javaFX LineChart for the sparkline */ public Sparkline(ObservableList boats, Integer legNum, LineChart sparklineChart) { this.sparklineChart = sparklineChart; this.legNum = legNum; this.yAxis = (NumberAxis)sparklineChart.getYAxis(); this.xAxis = (NumberAxis)sparklineChart.getXAxis(); startBoats.addAll(boats); makeColours(); mapBoatColours(); createSparkline(); } /** * Creates and sets initial display for Sparkline for race positions. * A data series for each boat in the race is added. * Position numbers are displayed. */ public void createSparkline(){ // NOTE: Y axis is in negatives to display correct positions // all boats start in 'last' place for (int i=0; i series = new XYChart.Series(); series.getData().add(new XYChart.Data(0, -startBoats.size())); series.getData().add(new XYChart.Data(0, -startBoats.size())); sparklineChart.getData().add(series); sparklineChart.getData().get(i).getNode().setStyle("-fx-stroke: " + ""+boatColours.get(startBoats.get(i).getSourceID())+";"); } sparklineChart.setCreateSymbols(false); // set x axis details xAxis.setAutoRanging(false); xAxis.setTickMarkVisible(false); xAxis.setTickLabelsVisible(false); xAxis.setMinorTickVisible(false); xAxis.setUpperBound((startBoats.size()+1)*legNum); xAxis.setTickUnit((startBoats.size()+1)*legNum); // set y axis details yAxis.setLowerBound(-(startBoats.size()+1)); yAxis.setUpperBound(0); yAxis.setAutoRanging(false); yAxis.setLabel("Position in Race"); yAxis.setTickUnit(1); yAxis.setTickMarkVisible(false); yAxis.setMinorTickVisible(false); // hide minus number from displaying on axis yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) { @Override public String toString(Number value) { if ((Double)value == 0.0 || (Double)value < -startBoats.size()){ return ""; } else { return String.format("%7.0f", -value.doubleValue()); } } }); } /** * 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 boatsInRace){ int placingVal = boatsInRace.size(); sparkLineNumber++; for (int i = boatsInRace.size() - 1; i >= 0; i--){ for (int j = startBoats.size() - 1; j >= 0; j--){ if (boatsInRace.get(i)==startBoats.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; } } } } private void makeColours() { colours = new ArrayList<>(Arrays.asList( colourToHex(Color.BLUEVIOLET), colourToHex(Color.BLACK), colourToHex(Color.RED), colourToHex(Color.ORANGE), colourToHex(Color.DARKOLIVEGREEN), colourToHex(Color.LIMEGREEN), colourToHex(Color.PURPLE), colourToHex(Color.DARKGRAY), colourToHex(Color.YELLOW) )); } private String colourToHex(Color color) { return String.format( "#%02X%02X%02X", (int)( color.getRed() * 255 ), (int)( color.getGreen() * 255 ), (int)( color.getBlue() * 255 ) ); } private void mapBoatColours() { int currentColour = 0; for (Boat boat : startBoats) { if (!boatColours.containsKey(boat.getSourceID())) { boatColours.put(boat.getSourceID(), colours.get(currentColour)); } currentColour = (currentColour + 1) % colours.size(); } } }