- Updated raceview.fxml to scale race view when resize.
- Implemented a listener on game view to know the current window size.
Issues:
- Border updates with resizing until a certain size and it draws out of canvas
- Marks are not drawn within border
- Trails do not scale at all
This commit is contained in:
Zhi You Tan
2017-09-01 17:10:00 +12:00
parent 5fb8a0c2c1
commit 54ca12f3b6
3 changed files with 214 additions and 115 deletions
+83 -40
View File
@@ -11,6 +11,8 @@ import javafx.animation.KeyFrame;
import javafx.animation.KeyValue; import javafx.animation.KeyValue;
import javafx.animation.Timeline; import javafx.animation.Timeline;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.geometry.Point2D; import javafx.geometry.Point2D;
import javafx.scene.Group; import javafx.scene.Group;
@@ -99,6 +101,9 @@ public class GameView extends Pane {
double scaleFactor = 1; double scaleFactor = 1;
private List<CompoundMark> newCourse;
private List<Corner> sequence;
private void zoomOut() { private void zoomOut() {
scaleFactor = 0.1; scaleFactor = 0.1;
if (this.getScaleX() > 0.5) { if (this.getScaleX() > 0.5) {
@@ -142,6 +147,32 @@ public class GameView extends Pane {
gameObjects.add(raceBorder); gameObjects.add(raceBorder);
gameObjects.add(markers); gameObjects.add(markers);
initializeTimer(); initializeTimer();
this.widthProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue,
Number newValue) {
if (oldValue.doubleValue() != 0.0) {
System.out.println("Width: " + newValue);
canvasWidth = newValue.doubleValue() - 40.0;
updateBorder(borderPoints);
updateCourse(newCourse, sequence);
}
}
});
this.heightProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue,
Number newValue) {
if (oldValue.doubleValue() != 0.0) {
System.out.println("Height: " + newValue);
canvasHeight = newValue.doubleValue();
updateBorder(borderPoints);
updateCourse(newCourse, sequence);
}
}
});
} }
private void initializeTimer() { private void initializeTimer() {
@@ -185,42 +216,42 @@ public class GameView extends Pane {
}; };
} }
/** // /**
* First find the top right and bottom left points' geo locations, then retrieve map from google // * First find the top right and bottom left points' geo locations, then retrieve map from google
* to display on image view. - Haoming 22/5/2017 // * to display on image view. - Haoming 22/5/2017
*/ // */
private void drawGoogleMap() { // private void drawGoogleMap() {
findMetersPerPixel(); // findMetersPerPixel();
Point2D topLeftPoint = findScaledXY(maxLatPoint.getLat(), minLonPoint.getLng()); // Point2D topLeftPoint = findScaledXY(maxLatPoint.getLat(), minLonPoint.getLng());
// distance from top left extreme to panel origin (top left corner) // // distance from top left extreme to panel origin (top left corner)
double distanceFromTopLeftToOrigin = Math.sqrt( // double distanceFromTopLeftToOrigin = Math.sqrt(
Math.pow(topLeftPoint.getX() * metersPerPixelX, 2) + Math // Math.pow(topLeftPoint.getX() * metersPerPixelX, 2) + Math
.pow(topLeftPoint.getY() * metersPerPixelY, 2)); // .pow(topLeftPoint.getY() * metersPerPixelY, 2));
// angle from top left extreme to panel origin // // angle from top left extreme to panel origin
double bearingFromTopLeftToOrigin = Math // double bearingFromTopLeftToOrigin = Math
.toDegrees(Math.atan2(-topLeftPoint.getX(), topLeftPoint.getY())); // .toDegrees(Math.atan2(-topLeftPoint.getX(), topLeftPoint.getY()));
// the top left extreme // // the top left extreme
GeoPoint topLeftPos = new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng()); // GeoPoint topLeftPos = new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng());
GeoPoint originPos = GeoUtility // GeoPoint originPos = GeoUtility
.getGeoCoordinate(topLeftPos, bearingFromTopLeftToOrigin, distanceFromTopLeftToOrigin); // .getGeoCoordinate(topLeftPos, bearingFromTopLeftToOrigin, distanceFromTopLeftToOrigin);
//
// distance from origin corner to bottom right corner of the panel // // distance from origin corner to bottom right corner of the panel
double distanceFromOriginToBottomRight = Math.sqrt( // double distanceFromOriginToBottomRight = Math.sqrt(
Math.pow(panelHeight * metersPerPixelY, 2) + Math // Math.pow(panelHeight * metersPerPixelY, 2) + Math
.pow(panelWidth * metersPerPixelX, 2)); // .pow(panelWidth * metersPerPixelX, 2));
double bearingFromOriginToBottomRight = Math // double bearingFromOriginToBottomRight = Math
.toDegrees(Math.atan2(panelWidth, -panelHeight)); // .toDegrees(Math.atan2(panelWidth, -panelHeight));
GeoPoint bottomRightPos = GeoUtility // GeoPoint bottomRightPos = GeoUtility
.getGeoCoordinate(originPos, bearingFromOriginToBottomRight, // .getGeoCoordinate(originPos, bearingFromOriginToBottomRight,
distanceFromOriginToBottomRight); // distanceFromOriginToBottomRight);
//
Boundary boundary = new Boundary(originPos.getLat(), bottomRightPos.getLng(), // Boundary boundary = new Boundary(originPos.getLat(), bottomRightPos.getLng(),
bottomRightPos.getLat(), originPos.getLng()); // bottomRightPos.getLat(), originPos.getLng());
CanvasMap canvasMap = new CanvasMap(boundary); // CanvasMap canvasMap = new CanvasMap(boundary);
mapImage.setImage(canvasMap.getMapImage()); // mapImage.setImage(canvasMap.getMapImage());
mapImage.fitWidthProperty().bind(((AnchorPane) this.getParent()).heightProperty()); // mapImage.fitWidthProperty().bind(((AnchorPane) this.getParent()).heightProperty());
mapImage.fitHeightProperty().bind(((AnchorPane) this.getParent()).heightProperty()); // mapImage.fitHeightProperty().bind(((AnchorPane) this.getParent()).heightProperty());
} // }
// TODO: 16/08/17 Break up this function // TODO: 16/08/17 Break up this function
/** /**
@@ -231,6 +262,11 @@ public class GameView extends Pane {
* @param sequence The sequence the marks travel through * @param sequence The sequence the marks travel through
*/ */
public void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence) { public void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence) {
System.out.println("ran update course");
this.newCourse = newCourse;
this.sequence = sequence;
course = new ArrayList<>();
markerObjects = new HashMap<>(); markerObjects = new HashMap<>();
for (Corner corner : sequence) { //Makes course out of all compound marks. for (Corner corner : sequence) { //Makes course out of all compound marks.
@@ -281,9 +317,9 @@ public class GameView extends Pane {
createMarkArrows(sequence); createMarkArrows(sequence);
//Scale race to markers if there is no border. //Scale race to markers if there is no border.
if (borderPoints == null) { // if (borderPoints == null) {
rescaleRace(new ArrayList<>(markerObjects.keySet())); rescaleRace(new ArrayList<>(markerObjects.keySet()));
} // }
//Move the Markers to initial position. //Move the Markers to initial position.
markerObjects.forEach(((mark, marker) -> { markerObjects.forEach(((mark, marker) -> {
Point2D p2d = findScaledXY(mark.getLat(), mark.getLng()); Point2D p2d = findScaledXY(mark.getLat(), mark.getLng());
@@ -422,16 +458,18 @@ public class GameView extends Pane {
* @param border the race border to be drawn. * @param border the race border to be drawn.
*/ */
public void updateBorder(List<Limit> border) { public void updateBorder(List<Limit> border) {
if (borderPoints == null) { System.out.println("ran update border");
// if (borderPoints == null) {
borderPoints = border; borderPoints = border;
rescaleRace(new ArrayList<>(borderPoints)); rescaleRace(new ArrayList<>(borderPoints));
} // }
List<Double> boundaryPoints = new ArrayList<>(); List<Double> boundaryPoints = new ArrayList<>();
for (Limit limit : border) { for (Limit limit : border) {
Point2D location = findScaledXY(limit.getLat(), limit.getLng()); Point2D location = findScaledXY(limit.getLat(), limit.getLng());
boundaryPoints.add(location.getX()); boundaryPoints.add(location.getX());
boundaryPoints.add(location.getY()); boundaryPoints.add(location.getY());
} }
raceBorder.getPoints().remove(0, raceBorder.getPoints().size());
raceBorder.getPoints().setAll(boundaryPoints); raceBorder.getPoints().setAll(boundaryPoints);
} }
@@ -457,6 +495,7 @@ public class GameView extends Pane {
*/ */
private void rescaleRace(List<GeoPoint> limitingCoordinates) { private void rescaleRace(List<GeoPoint> limitingCoordinates) {
//Check is called once to avoid unnecessarily change the course limits once the race is running //Check is called once to avoid unnecessarily change the course limits once the race is running
System.out.println("ran rescale race");
findMinMaxPoint(limitingCoordinates); findMinMaxPoint(limitingCoordinates);
double minLonToMaxLon = scaleRaceExtremities(); double minLonToMaxLon = scaleRaceExtremities();
calculateReferencePointLocation(minLonToMaxLon); calculateReferencePointLocation(minLonToMaxLon);
@@ -559,6 +598,7 @@ public class GameView extends Pane {
* respectively. * respectively.
*/ */
private void findMinMaxPoint(List<GeoPoint> points) { private void findMinMaxPoint(List<GeoPoint> points) {
System.out.println("ran find min max point");
List<GeoPoint> sortedPoints = new ArrayList<>(points); List<GeoPoint> sortedPoints = new ArrayList<>(points);
sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLat)); sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLat));
minLatPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng()); minLatPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng());
@@ -582,6 +622,7 @@ public class GameView extends Pane {
* maximum longitude. * maximum longitude.
*/ */
private void calculateReferencePointLocation(double minLonToMaxLon) { private void calculateReferencePointLocation(double minLonToMaxLon) {
System.out.println("ran calculate reference point location");
GeoPoint referencePoint = minLatPoint; GeoPoint referencePoint = minLatPoint;
double referenceAngle; double referenceAngle;
@@ -625,6 +666,7 @@ public class GameView extends Pane {
* it to distanceScaleFactor Returns the max horizontal distance of the map. * it to distanceScaleFactor Returns the max horizontal distance of the map.
*/ */
private double scaleRaceExtremities() { private double scaleRaceExtremities() {
System.out.println("ran scale race extremities");
double vertAngle = Math.abs( double vertAngle = Math.abs(
GeoUtility.getBearingRad(minLatPoint, maxLatPoint) GeoUtility.getBearingRad(minLatPoint, maxLatPoint)
@@ -659,6 +701,7 @@ public class GameView extends Pane {
} }
private Point2D findScaledXY(double unscaledLat, double unscaledLon) { private Point2D findScaledXY(double unscaledLat, double unscaledLon) {
// System.out.println("ran find scaled XY");
double distanceFromReference; double distanceFromReference;
double angleFromReference; double angleFromReference;
double xAxisLocation = referencePointX; double xAxisLocation = referencePointX;
@@ -26,6 +26,7 @@ import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox; import javafx.scene.control.ComboBox;
import javafx.scene.control.Slider; import javafx.scene.control.Slider;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
@@ -64,7 +65,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
@FXML @FXML
private Text timerLabel; private Text timerLabel;
@FXML @FXML
private AnchorPane contentAnchorPane; private GridPane contentAnchorPane;
@FXML @FXML
private Text windArrowText, windDirectionText; private Text windArrowText, windDirectionText;
@FXML @FXML
@@ -91,6 +92,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private ImportantAnnotationsState importantAnnotations; private ImportantAnnotationsState importantAnnotations;
public void initialize() { public void initialize() {
// Load a default important annotation state // Load a default important annotation state
importantAnnotations = new ImportantAnnotationsState(); importantAnnotations = new ImportantAnnotationsState();
@@ -134,7 +136,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
updateOrder(raceState.getPlayerPositions()); updateOrder(raceState.getPlayerPositions());
gameView = new GameView(); gameView = new GameView();
gameView.setFrameRateFXText(fpsDisplay); gameView.setFrameRateFXText(fpsDisplay);
Platform.runLater(() -> contentAnchorPane.getChildren().add(0, gameView)); Platform.runLater(() -> contentAnchorPane.getChildren().addAll(gameView));
// gameView.prefWidthProperty().bind(contentAnchorPane.widthProperty());
// gameView.prefHeightProperty().bind(contentAnchorPane.heightProperty());
gameView.setBoats(new ArrayList<>(participants.values())); gameView.setBoats(new ArrayList<>(participants.values()));
gameView.updateBorder(raceData.getCourseLimit()); gameView.updateBorder(raceData.getCourseLimit());
gameView.updateCourse( gameView.updateCourse(
+125 -73
View File
@@ -24,83 +24,135 @@
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?> <?import javafx.scene.text.Text?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="998.0" prefWidth="1530.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.RaceViewController"> <AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="998.0" prefWidth="1530.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="seng302.visualiser.controllers.RaceViewController">
<children> <children>
<AnchorPane layoutX="322.0" layoutY="130.0" prefHeight="998.0" prefWidth="1281.0" <GridPane prefHeight="998.0" prefWidth="1530.0" AnchorPane.bottomAnchor="0.0"
style="-fx-background-color: skyblue;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="250.0" minWidth="250.0"
prefWidth="250.0"/>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="1283.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
</rowConstraints>
<children> <children>
<GridPane prefHeight="998.0" prefWidth="1281.0" AnchorPane.bottomAnchor="0.0" <AnchorPane minHeight="10.0" minWidth="250.0" prefHeight="960.0" prefWidth="250.0"
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" style="-fx-background-color: #2C2c36;">
AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="630.0" minWidth="10.0"
prefWidth="68.0"/>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1213.0" minWidth="10.0"
prefWidth="1213.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="489.0" minHeight="1.0" prefHeight="24.0"
vgrow="SOMETIMES"/>
<RowConstraints maxHeight="997.0" minHeight="10.0" prefHeight="974.0"
vgrow="SOMETIMES"/>
</rowConstraints>
<children> <children>
<AnchorPane fx:id="contentAnchorPane" prefHeight="200.0" prefWidth="200.0" <Label layoutX="11.0" layoutY="283.0" text="Team Position" textFill="WHITE"/>
GridPane.columnSpan="2" GridPane.rowSpan="2"/> <Label layoutX="13.0" layoutY="432.0" text="Settings" textFill="WHITE"/>
<Text fx:id="fpsDisplay" strokeType="OUTSIDE" strokeWidth="0.0" text="60 FPS" <Label layoutX="11.0" layoutY="41.0" text="Timer" textFill="WHITE"/>
GridPane.halignment="CENTER" GridPane.valignment="CENTER"/> <Label layoutX="11.0" layoutY="112.0" text="Wind direction"
textFill="WHITE"/>
<Circle fx:id="windBackgroundCircle" blendMode="DARKEN" fill="#3dcdc8"
layoutX="110.0" layoutY="190.0" radius="35.0" stroke="#d7d7d7"
strokeType="INSIDE" strokeWidth="3.0"/>
<Text fx:id="windArrowText" fill="#a8a8a8" layoutX="86.0" layoutY="210.0"
strokeType="OUTSIDE" strokeWidth="0.0" text="↓">
<font>
<Font name="AdobeArabic-Regular" size="55.0"/>
</font>
</Text>
<Text fx:id="windDirectionText" fill="#d3d3d3" layoutX="171.0"
layoutY="238.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0°"
textAlignment="RIGHT">
<font>
<Font name="System Bold" size="13.0"/>
</font>
</Text>
<Text fx:id="windSpeedText" fill="#d3d3d3" layoutX="12.0" layoutY="237.0"
strokeType="OUTSIDE" strokeWidth="0.0" text="0.0 Knot"
textAlignment="RIGHT">
<font>
<Font name="System Bold" size="13.0"/>
</font>
</Text>
<CheckBox fx:id="toggleFps" focusTraversable="false" graphicTextGap="0.0"
layoutX="21.0" layoutY="453.0" mnemonicParsing="false" prefHeight="18.0"
prefWidth="143.0" selected="true" styleClass="ui-checkbox" text="Show FPS"
textFill="WHITE"/>
<VBox fx:id="positionVbox" layoutX="12.0" layoutY="304.0" prefHeight="116.0"
prefWidth="200.0" styleClass="text-white"/>
<Pane layoutX="11.0" layoutY="39.0" maxHeight="-Infinity"
maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="51.0" prefWidth="193.0">
<children>
<Text fx:id="timerLabel" fill="#f8f8f8" layoutX="-26.0"
layoutY="51.0" strokeType="OUTSIDE" strokeWidth="0.0" text="00:00"
textAlignment="CENTER" wrappingWidth="246.0">
<font>
<Font size="25.0"/>
</font>
</Text>
</children>
</Pane>
<Slider fx:id="annotationSlider" blockIncrement="1.0" layoutX="38.0"
layoutY="527.0" majorTickUnit="1.0" max="2.0" minorTickCount="0"
prefHeight="51.0" prefWidth="170.0" showTickLabels="true"
showTickMarks="true" snapToTicks="true" styleClass="ui-slider"/>
<Label layoutX="10.0" layoutY="499.0" text="Annotations" textFill="WHITE"/>
<Button fx:id="selectAnnotationBtn" focusTraversable="false" layoutX="35.0"
layoutY="578.0" mnemonicParsing="false" prefHeight="18.0" prefWidth="170.0"
styleClass="blue-ui-btn" text="Select Annotations" textFill="WHITE"/>
<Text fill="WHITE" layoutX="11.0" layoutY="649.0" strokeType="OUTSIDE"
strokeWidth="0.0" text="Boat Selection"/>
<ComboBox fx:id="yachtSelectionComboBox" focusTraversable="false"
layoutX="37.0" layoutY="664.0" prefHeight="25.0" prefWidth="170.0"
promptText="Select Yacht" styleClass="combo-box-base"/>
<LineChart fx:id="raceSparkLine" layoutX="-1.0" layoutY="719.0"
legendVisible="false" prefHeight="277.0" prefWidth="246.0"
title="Boat Positions">
<xAxis>
<CategoryAxis label="Leg Number" side="BOTTOM"
styleClass="spark-line-xaxis"/>
</xAxis>
<yAxis>
<NumberAxis fx:id="sparklineYAxis" minorTickCount="1"
minorTickLength="1.0" side="LEFT" styleClass="spark-line-yaxis"
tickLabelGap="1.0" tickUnit="1.0" upperBound="7.0"/>
</yAxis>
</LineChart>
</children> </children>
</GridPane> </AnchorPane>
<AnchorPane prefHeight="998.0" prefWidth="1281.0"
style="-fx-background-color: skyblue;" GridPane.columnIndex="1">
<children>
<GridPane minHeight="10.0" prefHeight="998.0" prefWidth="1281.0"
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="68.0" minWidth="68.0"
prefWidth="68.0"/>
<ColumnConstraints hgrow="SOMETIMES"/>
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="24.0" minHeight="24.0" prefHeight="24.0"
vgrow="SOMETIMES"/>
<RowConstraints vgrow="SOMETIMES"/>
</rowConstraints>
<children>
<GridPane fx:id="contentAnchorPane" GridPane.columnSpan="2"
GridPane.rowSpan="2">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES"/>
</columnConstraints>
<rowConstraints>
<RowConstraints vgrow="SOMETIMES"/>
</rowConstraints>
</GridPane>
<Text fx:id="fpsDisplay" strokeType="OUTSIDE" strokeWidth="0.0"
text="60 FPS" GridPane.halignment="CENTER"
GridPane.valignment="CENTER"/>
</children>
</GridPane>
</children>
</AnchorPane>
</children> </children>
</AnchorPane> </GridPane>
<AnchorPane prefHeight="960.0" prefWidth="250.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label layoutX="11.0" layoutY="283.0" text="Team Position" textFill="WHITE" />
<Label layoutX="13.0" layoutY="432.0" text="Settings" textFill="WHITE" />
<Label layoutX="11.0" layoutY="41.0" text="Timer" textFill="WHITE" />
<Label layoutX="11.0" layoutY="112.0" text="Wind direction" textFill="WHITE" />
<Circle fx:id="windBackgroundCircle" blendMode="DARKEN" fill="#3dcdc8" layoutX="110.0" layoutY="190.0" radius="35.0" stroke="#d7d7d7" strokeType="INSIDE" strokeWidth="3.0" />
<Text fx:id="windArrowText" fill="#a8a8a8" layoutX="86.0" layoutY="210.0" strokeType="OUTSIDE" strokeWidth="0.0" text="↓">
<font>
<Font name="AdobeArabic-Regular" size="55.0" />
</font>
</Text>
<Text fx:id="windDirectionText" fill="#d3d3d3" layoutX="171.0" layoutY="238.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0°" textAlignment="RIGHT">
<font>
<Font name="System Bold" size="13.0" />
</font>
</Text>
<Text fx:id="windSpeedText" fill="#d3d3d3" layoutX="12.0" layoutY="237.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0 Knot" textAlignment="RIGHT">
<font>
<Font name="System Bold" size="13.0" />
</font>
</Text>
<CheckBox fx:id="toggleFps" focusTraversable="false" graphicTextGap="0.0" layoutX="21.0" layoutY="453.0" mnemonicParsing="false" prefHeight="18.0" prefWidth="143.0" selected="true" styleClass="ui-checkbox" text="Show FPS" textFill="WHITE" />
<VBox fx:id="positionVbox" layoutX="12.0" layoutY="304.0" prefHeight="116.0" prefWidth="200.0" styleClass="text-white" />
<Pane layoutX="11.0" layoutY="39.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="51.0" prefWidth="193.0">
<children>
<Text fx:id="timerLabel" fill="#f8f8f8" layoutX="-26.0" layoutY="51.0" strokeType="OUTSIDE" strokeWidth="0.0" text="00:00" textAlignment="CENTER" wrappingWidth="246.0">
<font>
<Font size="25.0" />
</font>
</Text>
</children>
</Pane>
<Slider fx:id="annotationSlider" blockIncrement="1.0" layoutX="38.0" layoutY="527.0" majorTickUnit="1.0" max="2.0" minorTickCount="0" prefHeight="51.0" prefWidth="170.0" showTickLabels="true" showTickMarks="true" snapToTicks="true" styleClass="ui-slider" />
<Label layoutX="10.0" layoutY="499.0" text="Annotations" textFill="WHITE" />
<Button fx:id="selectAnnotationBtn" focusTraversable="false" layoutX="35.0" layoutY="578.0" mnemonicParsing="false" prefHeight="18.0" prefWidth="170.0" styleClass="blue-ui-btn" text="Select Annotations" textFill="WHITE" />
<Text fill="WHITE" layoutX="11.0" layoutY="649.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Boat Selection" />
<ComboBox fx:id="yachtSelectionComboBox" focusTraversable="false" layoutX="37.0" layoutY="664.0" prefHeight="25.0" prefWidth="170.0" promptText="Select Yacht" styleClass="combo-box-base" />
<LineChart fx:id="raceSparkLine" layoutX="-1.0" layoutY="719.0" legendVisible="false" prefHeight="277.0" prefWidth="246.0" title="Boat Positions">
<xAxis>
<CategoryAxis label="Leg Number" side="BOTTOM" styleClass="spark-line-xaxis" />
</xAxis>
<yAxis>
<NumberAxis fx:id="sparklineYAxis" minorTickCount="1" minorTickLength="1.0" side="LEFT" styleClass="spark-line-yaxis" tickLabelGap="1.0" tickUnit="1.0" upperBound="7.0" />
</yAxis>
</LineChart>
</children>
</AnchorPane>
</children> </children>
</AnchorPane> </AnchorPane>