Compare commits

...

29 Commits

Author SHA1 Message Date
Michael Rausch fe76ef9cdc Merge remote-tracking branch 'origin/develop' 2017-05-25 16:58:01 +12:00
Michael Rausch acbaa838ec Change default port to 4940
#story[956] #pair[wmu16, mra106]
2017-05-25 16:56:41 +12:00
Haoming Yin f4134d83b5 Merge branch 'issue#8_create_finish_screen' into 'develop'
Issue#8 create finish screen

Created a finish screen which user can see position of the final result and able to go back to start screen to rewatch new race

See merge request !41
2017-05-25 16:52:59 +12:00
Zhi You Tan 24cc10e1cd Merge remote-tracking branch 'origin/develop' into issue#8_create_finish_screen
# Conflicts:
#	src/main/java/seng302/controllers/CanvasController.java
2017-05-25 16:51:32 +12:00
Zhi You Tan 20b79b40f2 Fix the button position #story[923] 2017-05-25 16:45:35 +12:00
Zhi You Tan 72e2776b7e Fix the finish screen size #story[923] 2017-05-25 16:41:59 +12:00
Kusal Ekanayake 49e4c92da6 Merge branch 'develop' 2017-05-25 16:40:24 +12:00
Kusal Ekanayake ba761e4951 Merge branch 'remove_observers' into 'develop'
Remove observers

Refactor for annotations and a few improvements here and there. Fix for several tracked issues and numerous other bug fixes.

See merge request !40
2017-05-25 16:36:43 +12:00
Calum c1aa1d8eae merged with dev 2017-05-25 16:32:25 +12:00
Calum 4231c3ccd8 Merge remote-tracking branch 'origin/develop' into remove_observers 2017-05-25 16:30:15 +12:00
Calum 65223ceaaf shifted annotation layers. Merged with dev. 2017-05-25 16:29:27 +12:00
Kusal Ekanayake ca22615c08 Add scroll bar to the side panel to cater for smaller screen. 2017-05-25 16:18:47 +12:00
Zhi You Tan 559a9f38c0 Merge remote-tracking branch 'origin/develop' into issue#8_create_finish_screen
# Conflicts:
#	src/main/java/seng302/controllers/CanvasController.java
#	src/main/java/seng302/models/stream/StreamParser.java
2017-05-25 16:12:44 +12:00
Calum b73e4c89db Merge remote-tracking branch 'origin/develop' into remove_observers
# Conflicts:
#	src/main/java/seng302/controllers/CanvasController.java
#	src/main/java/seng302/controllers/RaceViewController.java
#	src/main/java/seng302/fxObjects/BoatGroup.java
#	src/main/java/seng302/fxObjects/MarkGroup.java
#	src/main/java/seng302/models/Yacht.java
2017-05-25 14:52:54 +12:00
Zhi You Tan f163dfdd11 Removed a print statement 2017-05-25 13:14:36 +12:00
Zhi You Tan a1eda8d91d Fix boatPos in StreamParser.java being empty and affecting all the assessors 2017-05-25 12:58:11 +12:00
Zhi You Tan 5ed02a1fe1 Merge remote-tracking branch 'origin/develop' into issue#8_create_finish_screen
# Conflicts:
#	src/main/java/seng302/controllers/CanvasController.java
2017-05-25 12:32:10 +12:00
Zhi You Tan 66d4a4b958 Fix finish screen showing wrong ("-" as) position 2017-05-25 12:28:13 +12:00
Calum 0cd2867ac0 Made annotations more readable when overlapping. 2017-05-25 10:30:01 +12:00
Calum 4d29354797 Merge branch 'develop' into remove_observers
# Conflicts:
#	src/main/java/seng302/controllers/CanvasController.java
#	src/main/java/seng302/fxObjects/BoatGroup.java
#	src/main/java/seng302/models/stream/StreamParser.java
2017-05-25 10:25:54 +12:00
Calum 3085125f3e Merged with develop 2017-05-25 01:21:40 +12:00
Calum 5e26ad7c36 Merge remote-tracking branch 'origin/develop' into remove_observers
# Conflicts:
#	src/main/java/seng302/App.java
#	src/main/java/seng302/controllers/CanvasController.java
#	src/main/java/seng302/controllers/RaceViewController.java
#	src/main/java/seng302/models/Yacht.java
2017-05-25 01:12:10 +12:00
Calum 765ea06c3b Several tweaks and improvements to a annotations and other visual aspects of them program. Fixed bug causing minimization crashes.
#implement #refactor #issue[23]
2017-05-25 01:02:33 +12:00
Calum 14a7305a2d Made FPS marker not draw on canvas.
Have to revert the use of the observer pattern since there is no time to change how and when data is passed.
2017-05-24 17:17:06 +12:00
Zhi You Tan e7060d4b6f Finish screen will show when race finishes, added functionality to return to start screen when in finish screen, updated finishScreenView.fxml to have controller and also four corner anchors to fit to parent 2017-05-24 16:17:56 +12:00
Zhi You Tan 641039720e Merge remote-tracking branch 'origin/develop' into issue#8_create_finish_screen 2017-05-24 16:12:37 +12:00
Zhi You Tan c449da2916 [WIP] created finishScreenView.fxml, finishScreenViewController.java 2017-05-24 14:24:14 +12:00
Calum d22d758757 Fix issues caused by not updating the time since last mark value frequently. BoatAnnotations now has an update() function that must be called somewhat regularly. 2017-05-24 03:23:02 +12:00
Calum acbde5aad8 Moved boat annotations into their own class. Implemented observer pattern.
Observer pattern appears to have caused issues with updating Text objects.
Made annotations look nicer. Kinda.

#refactor
2017-05-24 03:09:11 +12:00
16 changed files with 675 additions and 575 deletions
+15 -5
View File
@@ -4,6 +4,8 @@ import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.stage.Stage;
import seng302.models.PolarTable;
import seng302.models.stream.StreamParser;
@@ -29,12 +31,11 @@ public class App extends Application {
StreamReceiver.noMoreBytes();
System.exit(0);
});
}
public static void main(String[] args) {
try {
StreamReceiver sr = null;
new ServerThread("Racevision Test Server");
@@ -67,13 +68,22 @@ public class App extends Application {
}
}
//Change the StreamReceiver in this else block to change the default data source.
else{
sr = new StreamReceiver("localhost", 4949, "RaceStream");
else {
sr = new StreamReceiver("livedata.americascup.com", 4940, "RaceStream");
}
sr.start();
StreamParser streamParser = new StreamParser("StreamParser");
streamParser.start();
}
catch (Exception e){
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Information Dialog");
alert.setHeaderText("Fatal Error");
alert.setContentText("There was an error connecting to the AC35 stream");
alert.showAndWait();
}
launch(args);
@@ -1,22 +1,38 @@
package seng302.controllers;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.PriorityBlockingQueue;
import javafx.animation.AnimationTimer;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import seng302.models.BoatGroup;
import javafx.scene.shape.Polygon;
import javafx.scene.text.Text;
import seng302.fxObjects.BoatAnnotations;
import seng302.fxObjects.BoatGroup;
import seng302.fxObjects.Wake;
import seng302.models.Colors;
import seng302.models.Yacht;
import seng302.models.mark.GateMark;
import seng302.models.mark.Mark;
import seng302.fxObjects.MarkGroup;
import seng302.models.mark.MarkType;
import seng302.models.mark.SingleMark;
import seng302.models.map.Boundary;
import seng302.models.map.CanvasMap;
import seng302.models.mark.*;
import seng302.models.stream.StreamParser;
import seng302.models.stream.XMLParser;
import seng302.models.stream.XMLParser.RaceXMLObject.Limit;
@@ -66,6 +82,8 @@ public class CanvasController {
private List<MarkGroup> markGroups = new ArrayList<>();
private List<BoatGroup> boatGroups = new ArrayList<>();
private Text FPSdisplay = new Text();
private Polygon raceBorder = new Polygon();
//FRAME RATE
private Double frameRate = 60.0;
@@ -80,7 +98,7 @@ public class CanvasController {
VERTICAL
}
public void setup(RaceViewController raceViewController){
public void setup(RaceViewController raceViewController) {
this.raceViewController = raceViewController;
}
@@ -102,37 +120,43 @@ public class CanvasController {
canvas.heightProperty().bind(new SimpleDoubleProperty(CANVAS_HEIGHT));
}
public void initializeCanvas (){
public void initializeCanvas() {
gc = canvas.getGraphicsContext2D();
gc.setGlobalAlpha(0.5);
fitMarksToCanvas();
drawGoogleMap();
// TODO: 1/05/17 wmu16 - Change this call to now draw the marks as from the xml
initializeBoats();
FPSdisplay.setLayoutX(5);
FPSdisplay.setLayoutY(20);
FPSdisplay.setStrokeWidth(2);
group.getChildren().add(FPSdisplay);
group.getChildren().add(raceBorder);
initializeMarks();
timer = new AnimationTimer() {
initializeBoats();
private int UPDATE_FPM_PERIOD = 50; // update FPM label every 50 frames
private int updateFPMCounter = 100;
timer = new AnimationTimer() {
private long lastTime = 0;
private int FPSCount = 30;
@Override
public void handle(long now) {
//fps stuff
long oldFrameTime = frameTimes[frameTimeIndex] ;
frameTimes[frameTimeIndex] = now ;
frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length ;
if (lastTime == 0) {
lastTime = now;
} else {
if (now - lastTime >= (1e8 / 60)) { //Fix for framerate going above 60 when minimized
long oldFrameTime = frameTimes[frameTimeIndex];
frameTimes[frameTimeIndex] = now;
frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length;
if (frameTimeIndex == 0) {
arrayFilled = true ;
arrayFilled = true;
}
long elapsedNanos;
if (arrayFilled) {
elapsedNanos = now - oldFrameTime ;
long elapsedNanosPerFrame = elapsedNanos / frameTimes.length ;
frameRate = 1_000_000_000.0 / elapsedNanosPerFrame ;
if (updateFPMCounter++ > UPDATE_FPM_PERIOD) {
updateFPMCounter = 0;
elapsedNanos = now - oldFrameTime;
long elapsedNanosPerFrame = elapsedNanos / frameTimes.length;
frameRate = 1_000_000_000.0 / elapsedNanosPerFrame;
if (FPSCount-- == 0) {
FPSCount = 30;
drawFps(frameRate.intValue());
}
raceViewController.updateSparkLine();
@@ -141,10 +165,34 @@ public class CanvasController {
if (StreamParser.isRaceFinished()) {
this.stop();
}
lastTime = now;
}
}
if (StreamParser.isRaceFinished()) {
this.stop();
switchToFinishScreen();
}
}
};
}
private void switchToFinishScreen() {
try {
// canvas view -> anchor pane -> grid pane -> main view
GridPane gridPane = (GridPane) canvasPane.getParent().getParent();
AnchorPane contentPane = (AnchorPane) gridPane.getParent();
contentPane.getChildren().removeAll();
contentPane.getChildren().clear();
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
contentPane.getChildren().addAll(
(Pane) FXMLLoader.load(getClass().getResource("/views/FinishScreenView.fxml")));
} catch (javafx.fxml.LoadException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 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
@@ -153,19 +201,29 @@ public class CanvasController {
findMetersPerPixel();
Point2D topLeftPoint = findScaledXY(maxLatPoint.getLatitude(), minLonPoint.getLongitude());
// distance from top left extreme to panel origin (top left corner)
double distanceFromTopLeftToOrigin = Math.sqrt(Math.pow(topLeftPoint.getX() * metersPerPixelX, 2) + Math.pow(topLeftPoint.getY() * metersPerPixelY, 2));
double distanceFromTopLeftToOrigin = Math.sqrt(
Math.pow(topLeftPoint.getX() * metersPerPixelX, 2) + Math
.pow(topLeftPoint.getY() * metersPerPixelY, 2));
// angle from top left extreme to panel origin
double bearingFromTopLeftToOrigin = Math.toDegrees(Math.atan2(-topLeftPoint.getX(), topLeftPoint.getY()));
double bearingFromTopLeftToOrigin = Math
.toDegrees(Math.atan2(-topLeftPoint.getX(), topLeftPoint.getY()));
// the top left extreme
Position topLeftPos = new Position(maxLatPoint.getLatitude(), minLonPoint.getLongitude());
Position originPos = GeoUtility.getGeoCoordinate(topLeftPos, bearingFromTopLeftToOrigin, distanceFromTopLeftToOrigin);
Position originPos = GeoUtility
.getGeoCoordinate(topLeftPos, bearingFromTopLeftToOrigin, distanceFromTopLeftToOrigin);
// distance from origin corner to bottom right corner of the panel
double distanceFromOriginToBottomRight = Math.sqrt(Math.pow(PANEL_HEIGHT* metersPerPixelY, 2) + Math.pow(PANEL_WIDTH * metersPerPixelX, 2));
double bearingFromOriginToBottomRight = Math.toDegrees(Math.atan2(PANEL_WIDTH, -PANEL_HEIGHT));
Position bottomRightPos = GeoUtility.getGeoCoordinate(originPos, bearingFromOriginToBottomRight, distanceFromOriginToBottomRight);
double distanceFromOriginToBottomRight = Math.sqrt(
Math.pow(PANEL_HEIGHT * metersPerPixelY, 2) + Math
.pow(PANEL_WIDTH * metersPerPixelX, 2));
double bearingFromOriginToBottomRight = Math
.toDegrees(Math.atan2(PANEL_WIDTH, -PANEL_HEIGHT));
Position bottomRightPos = GeoUtility
.getGeoCoordinate(originPos, bearingFromOriginToBottomRight,
distanceFromOriginToBottomRight);
Boundary boundary = new Boundary(originPos.getLat(), bottomRightPos.getLng(), bottomRightPos.getLat(), originPos.getLng());
Boundary boundary = new Boundary(originPos.getLat(), bottomRightPos.getLng(),
bottomRightPos.getLat(), originPos.getLng());
CanvasMap canvasMap = new CanvasMap(boundary);
mapImage.setImage(canvasMap.getMapImage());
}
@@ -173,44 +231,27 @@ public class CanvasController {
/**
* Adds border marks to the canvas, taken from the XML file
*
* NOTE: This is quite confusing as objects are grabbed from the XMLParser such as Mark and CompoundMark which are
* named the same as those in the model package but are, however not the same, so they do not have things such as
* a type and must be derived from the number of marks in a compound mark etc..
* NOTE: This is quite confusing as objects are grabbed from the XMLParser such as Mark and
* CompoundMark which are named the same as those in the model package but are, however not the
* same, so they do not have things such as a type and must be derived from the number of marks
* in a compound mark etc..
*/
private void addRaceBorder() {
XMLParser.RaceXMLObject raceXMLObject = StreamParser.getXmlObject().getRaceXML();
ArrayList<Limit> courseLimits = raceXMLObject.getCourseLimit();
gc.setStroke(Color.DARKBLUE);
gc.setLineWidth(3);
double[] xBoundaryPoints = new double[courseLimits.size()];
double[] yBoundaryPoints = new double[courseLimits.size()];
for (int i = 0; i < courseLimits.size() - 1; i++) {
Limit thisPoint1 = courseLimits.get(i);
SingleMark thisMark1 = new SingleMark("", thisPoint1.getLat(), thisPoint1.getLng(), thisPoint1.getSeqID(), thisPoint1.getSeqID());
Limit thisPoint2 = courseLimits.get(i+1);
SingleMark thisMark2 = new SingleMark("", thisPoint2.getLat(), thisPoint2.getLng(), thisPoint2.getSeqID(), thisPoint2.getSeqID());
Point2D borderPoint1 = findScaledXY(thisMark1);
Point2D borderPoint2 = findScaledXY(thisMark2);
gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(),
borderPoint2.getX(), borderPoint2.getY());
xBoundaryPoints[i] = borderPoint1.getX();
yBoundaryPoints[i] = borderPoint1.getY();
raceBorder.setStroke(new Color(0.0f, 0.0f, 0.74509807f, 1));
raceBorder.setStrokeWidth(3);
raceBorder.setFill(new Color(0,0,0,0));
List<Double> boundaryPoints = new ArrayList<>();
for (Limit limit : courseLimits) {
Point2D location = findScaledXY(limit.getLat(), limit.getLng());
boundaryPoints.add(location.getX());
boundaryPoints.add(location.getY());
}
Limit thisPoint1 = courseLimits.get(courseLimits.size()-1);
SingleMark thisMark1 = new SingleMark("", thisPoint1.getLat(), thisPoint1.getLng(), thisPoint1.getSeqID(), thisPoint1.getSeqID());
Limit thisPoint2 = courseLimits.get(0);
SingleMark thisMark2 = new SingleMark("", thisPoint2.getLat(), thisPoint2.getLng(), thisPoint2.getSeqID(), thisPoint2.getSeqID());
Point2D borderPoint1 = findScaledXY(thisMark1);
Point2D borderPoint2 = findScaledXY(thisMark2);
gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(),
borderPoint2.getX(), borderPoint2.getY());
xBoundaryPoints[courseLimits.size()-1] = borderPoint1.getX();
yBoundaryPoints[courseLimits.size()-1] = borderPoint1.getY();
// gc.setFill(Color.LIGHTBLUE);
// gc.fillPolygon(xBoundaryPoints,yBoundaryPoints,yBoundaryPoints.length);
raceBorder.getPoints().setAll(boundaryPoints);
}
private void updateGroups(){
private void updateGroups() {
for (BoatGroup boatGroup : boatGroups) {
// some raceObjects will have multiple ID's (for instance gate marks)
//checking if the current "ID" has any updates associated with it
@@ -233,8 +274,6 @@ public class CanvasController {
private void checkForCourseChanges() {
if (StreamParser.isNewRaceXmlReceived()){
gc.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
drawGoogleMap();
addRaceBorder();
}
}
@@ -242,12 +281,14 @@ public class CanvasController {
private void updateBoatGroup(BoatGroup boatGroup) {
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.boatLocations.get(boatGroup.getRaceId());
// giving the movementQueue a 5 packet buffer to account for slightly out of order packets
if (movementQueue.size() > 0){
if (movementQueue.size() > 0) {
try {
BoatPositionPacket positionPacket = movementQueue.take();
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
double heading = 360.0 / 0xffff * positionPacket.getHeading();
boatGroup.setDestination(p2d.getX(), p2d.getY(), heading, positionPacket.getGroundSpeed(), positionPacket.getTimeValid(), frameRate, boatGroup.getRaceId());
boatGroup.setDestination(
p2d.getX(), p2d.getY(), heading, positionPacket.getGroundSpeed(),
positionPacket.getTimeValid(), frameRate);
} catch (InterruptedException e){
e.printStackTrace();
}
@@ -262,7 +303,7 @@ public class CanvasController {
BoatPositionPacket positionPacket = movementQueue.take();
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
markGroup.moveMarkTo(p2d.getX(), p2d.getY(), raceId);
} catch (InterruptedException e){
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@@ -273,9 +314,12 @@ public class CanvasController {
*/
private void initializeBoats() {
Map<Integer, Yacht> boats = StreamParser.getBoats();
Group boatAnnotations = new Group();
Group wakes = new Group();
Group trails = new Group();
Group annotations = new Group();
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML().getParticipants();
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML()
.getParticipants();
ArrayList<Integer> participantIDs = new ArrayList<>();
for (Participant p : participants) {
participantIDs.add(p.getsourceID());
@@ -286,10 +330,14 @@ public class CanvasController {
boat.setColour(Colors.getColor());
BoatGroup boatGroup = new BoatGroup(boat, boat.getColour());
boatGroups.add(boatGroup);
boatAnnotations.getChildren().add(boatGroup.getLowPriorityAnnotations());
trails.getChildren().add(boatGroup.getTrail());
wakes.getChildren().add(boatGroup.getWake());
annotations.getChildren().add(boatGroup.getAnnotations());
}
}
group.getChildren().add(boatAnnotations);
group.getChildren().addAll(trails);
group.getChildren().addAll(wakes);
group.getChildren().addAll(annotations);
group.getChildren().addAll(boatGroups);
}
@@ -304,7 +352,8 @@ public class CanvasController {
} else {
GateMark gMark = (GateMark) mark;
MarkGroup markGroup = new MarkGroup(gMark, findScaledXY(gMark.getSingleMark1()), findScaledXY(gMark.getSingleMark2())); //should be 2 objects in the list.
MarkGroup markGroup = new MarkGroup(gMark, findScaledXY(gMark.getSingleMark1()),
findScaledXY(gMark.getSingleMark2())); //should be 2 objects in the list.
markGroups.add(markGroup);
}
}
@@ -336,6 +385,7 @@ public class CanvasController {
public double prefWidth(double height) {
return getWidth();
}
@Override
public double prefHeight(double width) {
return getHeight();
@@ -345,19 +395,16 @@ public class CanvasController {
private void drawFps(int fps){
if (raceViewController.isDisplayFps()){
gc.clearRect(5, 5, 60, 30);
gc.setFont(new Font(16));
gc.setLineWidth(4);
gc.setGlobalAlpha(0.75);
gc.fillText(fps + " FPS", 5, 20);
gc.setGlobalAlpha(0.5);
FPSdisplay.setVisible(true);
FPSdisplay.setText(String.format("%d FPS", fps));
} else {
gc.clearRect(5,5,60,30);
FPSdisplay.setVisible(false);
}
}
/**
* Calculates x and y location for every marker that fits it to the canvas the race will be drawn on.
* Calculates x and y location for every marker that fits it to the canvas the race will be
* drawn on.
*/
private void fitMarksToCanvas() {
//Check is called once to avoid unnecessarily change the course limits once the race is running
@@ -371,8 +418,9 @@ public class CanvasController {
/**
* Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the marker with the leftmost
* marker, rightmost marker, southern most marker and northern most marker respectively.
* Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the marker
* with the leftmost marker, rightmost marker, southern most marker and northern most marker
* respectively.
*/
private void findMinMaxPoint() {
List<Limit> sortedPoints = new ArrayList<>();
@@ -397,12 +445,13 @@ public class CanvasController {
}
/**
* Calculates the location of a reference point, this is always the point with minimum latitude, in relation to the
* canvas.
* Calculates the location of a reference point, this is always the point with minimum latitude,
* in relation to the canvas.
*
* @param minLonToMaxLon The horizontal distance between the point of minimum longitude to maximum longitude.
* @param minLonToMaxLon The horizontal distance between the point of minimum longitude to
* maximum longitude.
*/
private void calculateReferencePointLocation (double minLonToMaxLon) {
private void calculateReferencePointLocation(double minLonToMaxLon) {
Mark referencePoint = minLatPoint;
double referenceAngle;
@@ -431,20 +480,23 @@ public class CanvasController {
/**
* Finds the scale factor necessary to fit all race markers within the onscreen map and assigns it to distanceScaleFactor
* Returns the max horizontal distance of the map.
* Finds the scale factor necessary to fit all race markers within the onscreen map and assigns
* it to distanceScaleFactor Returns the max horizontal distance of the map.
*/
private double scaleRaceExtremities () {
private double scaleRaceExtremities() {
double vertAngle = Math.abs(Mark.calculateHeadingRad(minLatPoint, maxLatPoint));
double vertDistance = Math.cos(vertAngle) * Mark.calculateDistance(minLatPoint, maxLatPoint);
double vertDistance =
Math.cos(vertAngle) * Mark.calculateDistance(minLatPoint, maxLatPoint);
double horiAngle = Mark.calculateHeadingRad(minLonPoint, maxLonPoint);
if (horiAngle <= (Math.PI / 2))
if (horiAngle <= (Math.PI / 2)) {
horiAngle = (Math.PI / 2) - horiAngle;
else
} else {
horiAngle = horiAngle - (Math.PI / 2);
double horiDistance = Math.cos(horiAngle) * Mark.calculateDistance(minLonPoint, maxLonPoint);
}
double horiDistance =
Math.cos(horiAngle) * Mark.calculateDistance(minLonPoint, maxLonPoint);
double vertScale = (CANVAS_HEIGHT - (BUFFER_SIZE + BUFFER_SIZE)) / vertDistance;
@@ -458,8 +510,8 @@ public class CanvasController {
return horiDistance;
}
private Point2D findScaledXY (Mark unscaled) {
return findScaledXY (unscaled.getLatitude(), unscaled.getLongitude());
private Point2D findScaledXY(Mark unscaled) {
return findScaledXY(unscaled.getLatitude(), unscaled.getLongitude());
}
public Point2D findScaledXY (double unscaledLat, double unscaledLon) {
@@ -468,23 +520,35 @@ public class CanvasController {
int xAxisLocation = (int) referencePointX;
int yAxisLocation = (int) referencePointY;
angleFromReference = Mark.calculateHeadingRad(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat, unscaledLon);
distanceFromReference = Mark.calculateDistance(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat, unscaledLon);
angleFromReference = Mark
.calculateHeadingRad(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat,
unscaledLon);
distanceFromReference = Mark
.calculateDistance(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat,
unscaledLon);
if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) {
xAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
yAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
xAxisLocation += (int) Math
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
yAxisLocation -= (int) Math
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
} else if (angleFromReference >= 0) {
angleFromReference = angleFromReference - Math.PI / 2;
xAxisLocation += (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
yAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
xAxisLocation += (int) Math
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
yAxisLocation += (int) Math
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
} else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) {
angleFromReference = Math.abs(angleFromReference);
xAxisLocation -= (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
yAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
xAxisLocation -= (int) Math
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
yAxisLocation -= (int) Math
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
} else {
angleFromReference = Math.abs(angleFromReference) - Math.PI / 2;
xAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
yAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
xAxisLocation -= (int) Math
.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference);
yAxisLocation += (int) Math
.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference);
}
if(horizontalInversion) {
xAxisLocation = CANVAS_WIDTH - BUFFER_SIZE - (xAxisLocation - BUFFER_SIZE);
@@ -495,7 +559,7 @@ public class CanvasController {
/**
* Find the number of meters per pixel.
*/
private void findMetersPerPixel () {
private void findMetersPerPixel() {
Point2D p1, p2;
Mark m1, m2;
double theta, distance, dx, dy, dHorizontal, dVertical;
@@ -0,0 +1,97 @@
package seng302.controllers;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import seng302.models.Yacht;
import seng302.models.stream.StreamParser;
import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
public class FinishScreenViewController implements Initializable {
@FXML
private GridPane finishScreenGridPane;
@FXML
private TableView<Yacht> finishOrderTable;
@FXML
private TableColumn<Yacht, String> posCol;
@FXML
private TableColumn<Yacht, String> boatNameCol;
@FXML
private TableColumn<Yacht, String> shortNameCol;
@FXML
private TableColumn<Yacht, String> countryCol;
@Override
public void initialize(URL location, ResourceBundle resources) {
finishScreenGridPane.getStylesheets()
.add(getClass().getResource("/css/master.css").toString());
finishOrderTable.getStylesheets().add(getClass().getResource("/css/master.css").toString());
// set up data for table
ObservableList<Yacht> data = FXCollections.observableArrayList();
finishOrderTable.setItems(data);
// setting table col data
posCol.setCellValueFactory(
new PropertyValueFactory<>("position")
);
boatNameCol.setCellValueFactory(
new PropertyValueFactory<>("boatName")
);
shortNameCol.setCellValueFactory(
new PropertyValueFactory<>("shortName")
);
countryCol.setCellValueFactory(
new PropertyValueFactory<>("country")
);
// check if the boat is racing
ArrayList<Participant> participants = StreamParser.getXmlObject().getRaceXML()
.getParticipants();
ArrayList<Integer> participantIDs = new ArrayList<>();
for (Participant p : participants) {
participantIDs.add(p.getsourceID());
}
// add data to table
for (Yacht boat : StreamParser.getBoatsPos().values()) {
if (participantIDs.contains(boat.getSourceID())) {
data.add(boat);
}
}
finishOrderTable.refresh();
}
private void setContentPane(String jfxUrl) {
try {
// get the main controller anchor pane (FinishView -> MainView)
AnchorPane contentPane = (AnchorPane) finishScreenGridPane.getParent();
contentPane.getChildren().removeAll();
contentPane.getChildren().clear();
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
contentPane.getChildren()
.addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl)));
} catch (javafx.fxml.LoadException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void switchToStartScreenView() {
setContentPane("/views/StartScreenView.fxml");
}
}
@@ -7,7 +7,6 @@ import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Point2D;
import javafx.geometry.Side;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
@@ -33,10 +32,11 @@ import seng302.controllers.annotations.Annotation;
import seng302.controllers.annotations.ImportantAnnotationController;
import seng302.controllers.annotations.ImportantAnnotationDelegate;
import seng302.controllers.annotations.ImportantAnnotationsState;
import seng302.fxObjects.BoatGroup;
import seng302.fxObjects.MarkGroup;
import seng302.models.*;
import seng302.models.mark.GateMark;
import seng302.models.mark.Mark;
import seng302.models.mark.MarkGroup;
import seng302.models.mark.SingleMark;
import seng302.models.stream.StreamParser;
import seng302.models.stream.XMLParser;
@@ -571,76 +571,31 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
return displayFps;
}
/**
* Display the important annotations for a specific BoatGroup
* @param bg The boat group to set the annotations for
*/
private void setBoatGroupImportantAnnotations(BoatGroup bg) {
if (importantAnnotations.getAnnotationState(Annotation.NAME)) {
bg.setTeamNameObjectVisible(true);
} else {
bg.setTeamNameObjectVisible(false);
}
if (importantAnnotations.getAnnotationState(Annotation.SPEED)) {
bg.setVelocityObjectVisible(true);
} else {
bg.setVelocityObjectVisible(false);
}
if (importantAnnotations.getAnnotationState(Annotation.TRACK)) {
bg.setLineGroupVisible(true);
} else {
bg.setLineGroupVisible(false);
}
if (importantAnnotations.getAnnotationState(Annotation.WAKE)) {
bg.setWakeVisible(true);
} else {
bg.setWakeVisible(false);
}
//TODO fix boat annotations with new boatgroup
if (importantAnnotations.getAnnotationState(Annotation.ESTTIMETONEXTMARK)) {
bg.setEstTimeToNextMarkObjectVisible(true);
} else {
bg.setEstTimeToNextMarkObjectVisible(false);
}
if (importantAnnotations.getAnnotationState(Annotation.LEGTIME)) {
bg.setLegTimeObjectVisible(true);
} else {
bg.setLegTimeObjectVisible(false);
}
}
private void setAnnotations(Integer annotationLevel) {
switch (annotationLevel) {
// No Annotations
case 0:
for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
bg.setTeamNameObjectVisible(false);
bg.setVelocityObjectVisible(false);
bg.setEstTimeToNextMarkObjectVisible(false);
bg.setLegTimeObjectVisible(false);
bg.setLineGroupVisible(false);
bg.setWakeVisible(false);
bg.setVisibility(false, false, false, false, false, false);
}
break;
// Important Annotations
case 1:
for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
setBoatGroupImportantAnnotations(bg);
bg.setVisibility(
importantAnnotations.getAnnotationState(Annotation.NAME),
importantAnnotations.getAnnotationState(Annotation.SPEED),
importantAnnotations.getAnnotationState(Annotation.ESTTIMETONEXTMARK),
importantAnnotations.getAnnotationState(Annotation.LEGTIME),
importantAnnotations.getAnnotationState(Annotation.TRACK),
importantAnnotations.getAnnotationState(Annotation.WAKE)
);
}
break;
// All Annotations
case 2:
for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
bg.setTeamNameObjectVisible(true);
bg.setVelocityObjectVisible(true);
bg.setEstTimeToNextMarkObjectVisible(true);
bg.setLegTimeObjectVisible(true);
bg.setLineGroupVisible(true);
bg.setWakeVisible(true);
bg.setVisibility(true, true, true, true, true, true);
}
break;
}
@@ -77,6 +77,9 @@ public class StartScreenController implements Initializable {
* second.
*/
public void startStream() {
// reset boolean for switch to race view
switchedToRaceView = false;
if (StreamParser.isStreamStatus()) {
streamButton.setVisible(false);
realTime.setVisible(true);
@@ -6,12 +6,8 @@ import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import seng302.controllers.RaceViewController;
import seng302.controllers.annotations.Annotation;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.net.URL;;
import java.util.ResourceBundle;
public class ImportantAnnotationController implements Initializable {
@@ -0,0 +1,133 @@
package seng302.fxObjects;
import javafx.scene.CacheHint;
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import seng302.models.Yacht;
import seng302.models.stream.StreamParser;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
/**
* Collection of annotations for boats.
*/
public class BoatAnnotations extends Group{
//Text offset constants
private static final double X_OFFSET_TEXT = 18d;
private static final double Y_OFFSET_TEXT_INIT = -29d;
private static final double Y_OFFSET_PER_TEXT = 12d;
//Background constants
private static final double TEXT_BUFFER = 3;
private static final double BACKGROUND_X = X_OFFSET_TEXT - TEXT_BUFFER;
private static final double BACKGROUND_Y = Y_OFFSET_TEXT_INIT - TEXT_BUFFER;
private static final double BACKGROUND_H_PER_TEXT = 9.5d;
private static final double BACKGROUND_W = 125d;
private static final double BACKGROUND_ARC_SIZE = 10;
private Rectangle background = new Rectangle();
private Text teamNameObject;
private Text velocityObject;
private Text estTimeToNextMarkObject;
private Text legTimeObject;
private Yacht boat;
BoatAnnotations (Yacht boat, Color theme) {
super.setCache(true);
this.boat = boat;
background.setX(BACKGROUND_X);
background.setY(BACKGROUND_Y);
background.setWidth(BACKGROUND_W);
background.setHeight(Math.abs(BACKGROUND_X) + TEXT_BUFFER + BACKGROUND_H_PER_TEXT * 4);
background.setArcHeight(BACKGROUND_ARC_SIZE);
background.setArcWidth(BACKGROUND_ARC_SIZE);
background.setFill(new Color(1, 1, 1, 0.75));
background.setStroke(theme);
background.setStrokeWidth(2);
background.setCache(true);
background.setCacheHint(CacheHint.SPEED);
teamNameObject = getTextObject(boat.getShortName(), theme);
teamNameObject.relocate(X_OFFSET_TEXT, Y_OFFSET_TEXT_INIT + Y_OFFSET_PER_TEXT);
velocityObject = getTextObject("0 m/s", theme);
velocityObject.relocate(X_OFFSET_TEXT, Y_OFFSET_TEXT_INIT + Y_OFFSET_PER_TEXT * 2);
estTimeToNextMarkObject = getTextObject("Next mark: ", theme);
estTimeToNextMarkObject.relocate(X_OFFSET_TEXT, Y_OFFSET_TEXT_INIT + Y_OFFSET_PER_TEXT * 3);
legTimeObject = getTextObject("Last mark: -", theme);
legTimeObject.relocate(X_OFFSET_TEXT, Y_OFFSET_TEXT_INIT + Y_OFFSET_PER_TEXT * 4);
super.getChildren().addAll(background, teamNameObject, velocityObject, estTimeToNextMarkObject, legTimeObject);
}
/**
* Return a text object with caching and a color applied
*
* @param defaultText The default text to display
* @param fill The text fill color
* @return The text object
*/
private Text getTextObject(String defaultText, Color fill) {
Text text = new Text(defaultText);
text.setFill(fill);
text.setStrokeWidth(2);
text.setCacheHint(CacheHint.SPEED);
text.setCache(true);
return text;
}
void update () {
velocityObject.setText(String.format(String.format("%.2f m/s", boat.getVelocity())));
if (boat.getTimeTillNext() != null) {
DateFormat format = new SimpleDateFormat("mm:ss");
String timeToNextMark = format
.format(boat.getTimeTillNext() - StreamParser.getCurrentTimeLong());
estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark);
} else {
estTimeToNextMarkObject.setText("Next mark: -");
}
if (boat.getMarkRoundTime() != null) {
DateFormat format = new SimpleDateFormat("mm:ss");
String elapsedTime = format
.format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundTime());
legTimeObject.setText("Last mark: " + elapsedTime);
}else {
legTimeObject.setText("Last mark: - ");
}
}
void setVisibile (boolean nameVisibility, boolean speedVisibility,
boolean estTimeVisibility, boolean lastMarkVisibility) {
int totalVisible = 0;
totalVisible = updateVisibility(nameVisibility, teamNameObject, totalVisible);
totalVisible = updateVisibility(speedVisibility, velocityObject, totalVisible);
totalVisible = updateVisibility(estTimeVisibility, estTimeToNextMarkObject, totalVisible);
totalVisible = updateVisibility(lastMarkVisibility, legTimeObject, totalVisible);
if (totalVisible != 0) {
background.setVisible(true);
background.setHeight(Math.abs(BACKGROUND_X) + TEXT_BUFFER + BACKGROUND_H_PER_TEXT * totalVisible);
} else {
background.setVisible(false);
}
}
private int updateVisibility (boolean visibility, Text text, int totalVisible) {
if (visibility){
totalVisible ++;
text.setVisible(true);
text.setLayoutX(X_OFFSET_TEXT);
text.setLayoutY(Y_OFFSET_TEXT_INIT + Y_OFFSET_PER_TEXT * totalVisible);
} else {
text.setVisible(false);
}
return totalVisible;
}
}
@@ -1,4 +1,4 @@
package seng302.models;
package seng302.fxObjects;
import java.util.ArrayList;
import javafx.event.EventHandler;
@@ -10,6 +10,7 @@ import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon;
import javafx.scene.text.Text;
import javafx.scene.transform.Rotate;
import seng302.models.Yacht;
import seng302.GeometryUtils;
import seng302.controllers.CanvasController;
import seng302.models.mark.GateMark;
@@ -31,14 +32,6 @@ import java.text.SimpleDateFormat;
public class BoatGroup extends Group {
//Constants for drawing
private static final double TEAMNAME_X_OFFSET = 10d;
private static final double TEAMNAME_Y_OFFSET = -29d;
private static final double VELOCITY_X_OFFSET = 10d;
private static final double VELOCITY_Y_OFFSET = -17d;
private static final double ESTTIMETONEXTMARK_X_OFFSET = 10d;
private static final double ESTTIMETONEXTMARK_Y_OFFSET = -5d;
private static final double LEGTIME_X_OFFSET = 10d;
private static final double LEGTIME_Y_OFFSET = 7d;
private static final double BOAT_HEIGHT = 15d;
private static final double BOAT_WIDTH = 10d;
//Variables for boat logic.
@@ -52,19 +45,15 @@ public class BoatGroup extends Group {
private Yacht boat;
private Group lineGroup = new Group();
private Polygon boatPoly;
private Text teamNameObject;
private Text velocityObject;
private Text estTimeToNextMarkObject;
private Text legTimeObject;
private Wake wake;
private Line leftLayLine;
private Line rightLayline;
private Double distanceTravelled = 0.0;
private Point2D lastPoint;
private boolean destinationSet;
private Color textColor = Color.RED;
private BoatAnnotations boatAnnotations;
private Boolean isSelected = true; //All boats are initalised as selected
private Boolean isSelected = true; //All boats are initialised as selected
/**
* Creates a BoatGroup with the default triangular boat polygon.
@@ -74,9 +63,9 @@ public class BoatGroup extends Group {
* @param color The colour of the boat polygon and the trailing line.
*/
public BoatGroup(Yacht boat, Color color) {
destinationSet = false;
this.boat = boat;
initChildren(color);
this.textColor = color;
}
/**
@@ -90,27 +79,11 @@ public class BoatGroup extends Group {
* polygon.
*/
public BoatGroup(Yacht boat, Color color, double... points) {
destinationSet = false;
this.boat = boat;
initChildren(color, points);
}
/**
* Return a text object with caching and a color applied
*
* @param defaultText The default text to display
* @param fill The text fill color
* @return The text object
*/
private Text getTextObject(String defaultText, Color fill) {
Text text = new Text(defaultText);
text.setFill(fill);
text.setCacheHint(CacheHint.SPEED);
text.setCache(true);
return text;
}
/**
* Creates the javafx objects that will be the in the group by default.
*
@@ -119,52 +92,26 @@ public class BoatGroup extends Group {
* polygon.
*/
private void initChildren(Color color, double... points) {
textColor = color;
destinationSet = false;
boatPoly = new Polygon(points);
boatPoly.setFill(color);
boatPoly.setOnMouseEntered(event -> boatPoly.setFill(Color.FLORALWHITE));
boatPoly.setOnMouseExited(event -> boatPoly.setFill(color));
boatPoly.setOnMouseEntered(event -> {
boatPoly.setFill(Color.FLORALWHITE);
boatPoly.setStroke(Color.RED);
});
boatPoly.setOnMouseExited(event -> {
boatPoly.setFill(color);
boatPoly.setStroke(Color.BLACK);
});
boatPoly.setOnMouseClicked(event -> setIsSelected(!isSelected));
boatPoly.setCache(true);
boatPoly.setCacheHint(CacheHint.SPEED);
teamNameObject = getTextObject(boat.getShortName(), textColor);
velocityObject = getTextObject(boat.getVelocity().toString(), textColor);
teamNameObject.setX(TEAMNAME_X_OFFSET);
teamNameObject.setY(TEAMNAME_Y_OFFSET);
teamNameObject.relocate(teamNameObject.getX(), teamNameObject.getY());
velocityObject.setX(VELOCITY_X_OFFSET);
velocityObject.setY(VELOCITY_Y_OFFSET);
velocityObject.relocate(velocityObject.getX(), velocityObject.getY());
updateLastMarkRoundingTime();
updateTimeTillNextMark();
if (estTimeToNextMarkObject != null) {
estTimeToNextMarkObject.setX(ESTTIMETONEXTMARK_X_OFFSET);
estTimeToNextMarkObject.setY(ESTTIMETONEXTMARK_Y_OFFSET);
estTimeToNextMarkObject
.relocate(estTimeToNextMarkObject.getX(), estTimeToNextMarkObject.getY());
}
if (legTimeObject != null) {
legTimeObject.setX(LEGTIME_X_OFFSET);
legTimeObject.setY(LEGTIME_Y_OFFSET);
legTimeObject.relocate(legTimeObject.getX(), legTimeObject.getY());
}
boatAnnotations = new BoatAnnotations(boat, color);
leftLayLine = new Line();
rightLayline = new Line();
wake = new Wake(0, -BOAT_HEIGHT);
super.getChildren()
.addAll(teamNameObject, velocityObject, boatPoly, estTimeToNextMarkObject,
legTimeObject, leftLayLine, rightLayline);
super.getChildren().addAll(boatPoly, boatAnnotations);
}
/**
@@ -189,14 +136,8 @@ public class BoatGroup extends Group {
private void moveGroupBy(double dx, double dy) {
boatPoly.setLayoutX(boatPoly.getLayoutX() + dx);
boatPoly.setLayoutY(boatPoly.getLayoutY() + dy);
teamNameObject.setLayoutX(teamNameObject.getLayoutX() + dx);
teamNameObject.setLayoutY(teamNameObject.getLayoutY() + dy);
velocityObject.setLayoutX(velocityObject.getLayoutX() + dx);
velocityObject.setLayoutY(velocityObject.getLayoutY() + dy);
estTimeToNextMarkObject.setLayoutX(estTimeToNextMarkObject.getLayoutX() + dx);
estTimeToNextMarkObject.setLayoutY(estTimeToNextMarkObject.getLayoutY() + dy);
legTimeObject.setLayoutX(legTimeObject.getLayoutX() + dx);
legTimeObject.setLayoutY(legTimeObject.getLayoutY() + dy);
boatAnnotations.setLayoutX(boatAnnotations.getLayoutX() + dx);
boatAnnotations.setLayoutY(boatAnnotations.getLayoutY() + dy);
wake.setLayoutX(wake.getLayoutX() + dx);
wake.setLayoutY(wake.getLayoutY() + dy);
}
@@ -212,14 +153,8 @@ public class BoatGroup extends Group {
rotateTo(rotation);
boatPoly.setLayoutX(x);
boatPoly.setLayoutY(y);
teamNameObject.setLayoutX(x);
teamNameObject.setLayoutY(y);
velocityObject.setLayoutX(x);
velocityObject.setLayoutY(y);
estTimeToNextMarkObject.setLayoutX(x);
estTimeToNextMarkObject.setLayoutY(y);
legTimeObject.setLayoutX(x);
legTimeObject.setLayoutY(y);
boatAnnotations.setLayoutX(x);
boatAnnotations.setLayoutY(y);
wake.setLayoutX(x);
wake.setLayoutY(y);
wake.rotate(rotation);
@@ -229,42 +164,6 @@ public class BoatGroup extends Group {
boatPoly.getTransforms().setAll(new Rotate(rotation));
}
/**
* Updates the time until next mark label, will create a label if one doesn't exist
*/
private void updateTimeTillNextMark() {
if (estTimeToNextMarkObject == null) {
estTimeToNextMarkObject = getTextObject("Next mark: -", textColor);
}
if (boat.getEstimateTimeAtNextMark() != null) {
DateFormat format = new SimpleDateFormat("mm:ss");
String timeToNextMark = format
.format(boat.getEstimateTimeAtNextMark() - StreamParser.getCurrentTimeLong());
estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark);
} else {
estTimeToNextMarkObject.setText("Next mark: -");
}
}
/**
* Updates the time since last mark rounding, will create a label if one doesn't exist
*/
private void updateLastMarkRoundingTime() {
if (legTimeObject == null) {
legTimeObject = getTextObject("Last mark: -", textColor);
}
if (boat.getMarkRoundingTime() != null) {
DateFormat format = new SimpleDateFormat("mm:ss");
String elapsedTime = format
.format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundingTime());
legTimeObject.setText("Last mark: " + elapsedTime);
} else {
legTimeObject.setText("Last mark: -");
}
}
public void move() {
double dx = xIncrement * framesToMove;
double dy = yIncrement * framesToMove;
@@ -298,33 +197,7 @@ public class BoatGroup extends Group {
lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY());
}
}
wake.updatePosition(1000 / 60);
}
/**
* Calculates the rotational velocity required to reach the rotationalGoal from the
* currentRotation.
*/
protected Double calculateRotationalVelocity(Double rotationalGoal) {
Double rotationalVelocity = 0.0;
if (Math.abs(rotationalGoal - lastRotation) > 180) {
if (rotationalGoal - lastRotation >= 0.0) {
rotationalVelocity = ((rotationalGoal - lastRotation) - 360) / 200;
} else {
rotationalVelocity = (360 + (rotationalGoal - lastRotation)) / 200;
}
} else {
rotationalVelocity = (rotationalGoal - lastRotation) / 200;
}
//Sometimes the rotation is too large to be realistic. In that case just do it instantly.
if (Math.abs(rotationalVelocity) > 1) {
rotationalVelocity = 0.0;
}
return rotationalVelocity;
wake.updatePosition();
}
/**
@@ -336,7 +209,7 @@ public class BoatGroup extends Group {
* @param timeValid the time the position values are valid for
*/
public void setDestination(double newXValue, double newYValue, double rotation,
double groundSpeed, long timeValid, double frameRate, long id) {
double groundSpeed, long timeValid, double frameRate) {
if (lastTimeValid == 0) {
lastTimeValid = timeValid - 200;
moveTo(newXValue, newYValue, rotation);
@@ -350,24 +223,13 @@ public class BoatGroup extends Group {
destinationSet = true;
Double rotationalVelocity = calculateRotationalVelocity(rotation);
updateTimeTillNextMark();
updateLastMarkRoundingTime();
if (Math.abs(rotationalVelocity) > 0.075) {
rotationalVelocity = 0.0;
wake.rotate(rotation);
}
rotateTo(rotation);
wake.setRotationalVelocity(rotationalVelocity, groundSpeed);
velocityObject.setText(String.format("%.2f m/s", groundSpeed));
wake.setRotation(rotation, groundSpeed);
boat.setVelocity(groundSpeed);
lastTimeValid = timeValid;
isStopped = false;
lastRotation = rotation;
boatAnnotations.update();
}
@@ -411,30 +273,16 @@ public class BoatGroup extends Group {
public void setIsSelected(Boolean isSelected) {
this.isSelected = isSelected;
setTeamNameObjectVisible(isSelected);
setVelocityObjectVisible(isSelected);
setLineGroupVisible(isSelected);
setWakeVisible(isSelected);
setEstTimeToNextMarkObjectVisible(isSelected);
setLegTimeObjectVisible(isSelected);
boatAnnotations.setVisible(isSelected);
setLayLinesVisible(isSelected);
}
public void setTeamNameObjectVisible(Boolean visible) {
teamNameObject.setVisible(visible);
}
public void setVelocityObjectVisible(Boolean visible) {
velocityObject.setVisible(visible);
}
public void setEstTimeToNextMarkObjectVisible(Boolean visible) {
estTimeToNextMarkObject.setVisible(visible);
}
public void setLegTimeObjectVisible(Boolean visible) {
legTimeObject.setVisible(visible);
public void setVisibility (boolean teamName, boolean velocity, boolean estTime, boolean legTime, boolean trail, boolean wake) {
boatAnnotations.setVisibile(teamName, velocity, estTime, legTime);
this.wake.setVisible(wake);
this.lineGroup.setVisible(trail);
}
public void setLineGroupVisible(Boolean visible) {
@@ -475,19 +323,17 @@ public class BoatGroup extends Group {
return boat.getSourceID();
}
/**
* Due to javaFX limitations annotations associated with a boat that you want to appear below
* all boats in the Z-axis need to be pulled out of the BoatGroup and added to the parent group
* of the BoatGroups. This function returns these annotations as a group.
*
* @return A group containing low priority annotations.
*/
public Group getLowPriorityAnnotations() {
Group group = new Group();
group.getChildren().addAll(wake, lineGroup);
return group;
public Group getWake () {
return wake;
}
public Group getTrail() {
return lineGroup;
}
public Group getAnnotations() {
return boatAnnotations;
}
public Double getBoatLayoutX() {
return boatPoly.getLayoutX();
@@ -506,4 +352,5 @@ public class BoatGroup extends Group {
public String toString() {
return boat.toString();
}
}
@@ -1,4 +1,4 @@
package seng302.models.mark;
package seng302.fxObjects;
import java.util.ArrayList;
import java.util.List;
@@ -8,6 +8,10 @@ import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import seng302.models.mark.GateMark;
import seng302.models.mark.Mark;
import seng302.models.mark.MarkType;
import seng302.models.mark.SingleMark;
import seng302.GeometryUtils;
/**
@@ -109,30 +113,30 @@ public class MarkGroup extends Group {
}
super.getChildren().add(line);
//Laylines
// if (mark.)
// addLayLine(points1, 12.0, 90.0);
// addLayLine(points2, 12.0, 90.0);
}
public void moveMarkTo (double x, double y, long raceId)
{
if (mainMark.getMarkType() == MarkType.SINGLE_MARK) {
Circle markCircle = (Circle) super.getChildren().get(0);
//One of the test streams produced frequent, jittery movements. Added this as a fix.
if (Math.abs(markCircle.getCenterX() - x) > 5 || Math.abs(markCircle.getCenterY() - y) > 5) {
markCircle.setCenterX(x);
markCircle.setCenterY(y);
}
} else {
Circle markCircle1 = (Circle) super.getChildren().get(0);
Circle markCircle2 = (Circle) super.getChildren().get(1);
Line connectingLine = (Line) super.getChildren().get(2);
if (marks.get(0).getId() == raceId) {
if (Math.abs(markCircle1.getCenterX() - x) > 5 || Math.abs(markCircle1.getCenterY() - y) > 5) {
markCircle1.setCenterX(x);
markCircle1.setCenterY(y);
connectingLine.setStartX(markCircle1.getCenterX());
connectingLine.setStartY(markCircle1.getCenterY());
}
} else if (marks.get(1).getId() == raceId) {
if (Math.abs(markCircle2.getCenterX() - x) > 5 || Math.abs(markCircle2.getCenterY() - y) > 5) {
markCircle2.setCenterX(x);
markCircle2.setCenterY(y);
connectingLine.setEndX(markCircle2.getCenterX());
@@ -140,6 +144,7 @@ public class MarkGroup extends Group {
}
}
}
}
public boolean hasRaceId (int... raceIds) {
for (int id : raceIds)
@@ -1,4 +1,4 @@
package seng302.models;
package seng302.fxObjects;
import javafx.scene.CacheHint;
import javafx.scene.Group;
@@ -7,24 +7,24 @@ import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
/**
* A group containing objects used to represent wakes onscreen. Contains functionality for their animation.
*/
class Wake extends Group {
public class Wake extends Group {
//The number of wakes
private int numWakes = 8;
//The total possible difference between the first wake and the last. Increasing/Decreasing this will make wakes fan out more/less.
private final double MAX_DIFF = 75;
//Increasing/decreasing this will alter the speed that wakes converge when the heading stop changing. Anything over about 1500 may cause oscillation.
private final int UNIFICATION_SPEED = 750;
private final int UNIFICATION_SPEED = 45;
private Arc[] arcs = new Arc[numWakes];
private double[] rotationalVelocities = new double[numWakes];
private double[] rotations = new double[numWakes];
private double baseRad;
/**
* Create a wake at the given location.
@@ -40,26 +40,26 @@ class Wake extends Group {
//Default triangle is -110 deg out of phase with a default wake and has angle of 40 deg.
arc = new Arc(0, 0, 0, 0, -110, 40);
arc.setCache(true);
arc.setCacheHint(CacheHint.SPEED);
arc.setCacheHint(CacheHint.ROTATE);
arc.setType(ArcType.OPEN);
arc.setStroke(new Color(0.18, 0.7, 1.0, 1.0 + (-0.99 / numWakes * i)));
arc.setStrokeWidth(3.0);
arc.setStrokeLineCap(StrokeLineCap.ROUND);
arc.setFill(new Color(0.0, 0.0, 0.0, 0.0));
baseRad = (20 / numWakes);
arcs[i] = arc;
arc.getTransforms().setAll(
new Rotate(1)
);
}
super.getChildren().addAll(arcs);
}
/**
* Sets the rotationalVelocity of each arc.
*
* @param rotationalVelocity The rotationalVelocity the wake should move at.
* @param velocity The real world velocity of the boat in m/s.
*/
void setRotationalVelocity(double rotationalVelocity, double velocity) {
rotationalVelocities[0] = rotationalVelocity;
void setRotation (double rotation, double velocity) {
if (Math.abs(rotations[0] - rotation) > 20) {
rotate(rotation);
} else {
rotations[0] = rotation;
((Rotate) arcs[0].getTransforms().get(0)).setAngle(rotation);
for (int i = 1; i < numWakes; i++) {
double wakeSeparationRad = Math.toRadians(rotations[i - 1] - rotations[i]);
double shortestDistance = Math.atan2(
@@ -67,35 +67,33 @@ class Wake extends Group {
Math.cos(wakeSeparationRad)
);
double distDeg = Math.toDegrees(shortestDistance);
if (rotationalVelocities[i - 1] < 0.01 && rotationalVelocities[i - 1] > -0.01) {
rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes);
rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * 2 * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes);
} else {
if (distDeg < (MAX_DIFF / numWakes))
rotationalVelocities[i] = rotationalVelocities[i - 1] * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes);
else
if (distDeg < (MAX_DIFF / numWakes)) {
rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes);
} else
rotationalVelocities[i] = rotationalVelocities[i - 1];
}
}
}
double rad = baseRad + velocity;
double rad = (14 / numWakes) + velocity;
for (Arc arc : arcs) {
arc.setRadiusX(rad);
arc.setRadiusY(rad);
rad += (20 / numWakes) + (velocity / 2);
rad += (14 / numWakes) + (velocity / 2.5);
}
}
/**
* Arcs rotate based on the distance they would have travelled over the supplied time interval.
*
* @param timeInterval the time interval, in microseconds, that the wake should move.
*/
void updatePosition(long timeInterval) {
void updatePosition() {
for (int i = 0; i < numWakes; i++) {
rotations[i] = rotations[i] + rotationalVelocities[i] * timeInterval;
arcs[i].getTransforms().setAll(new Rotate(rotations[i]));
rotations[i] = rotations[i] + rotationalVelocities[i];
((Rotate) arcs[i].getTransforms().get(0)).setAngle(rotations[i]);
}
}
+19 -20
View File
@@ -6,8 +6,6 @@ import seng302.controllers.RaceViewController;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import seng302.models.stream.StreamParser;
import seng302.models.stream.XMLParser.RaceXMLObject.Corner;
/**
* Yacht class for the racing boat.
@@ -19,7 +17,6 @@ public class Yacht {
// Used in boat group
private Color colour;
private double velocity;
private String boatType;
private Integer sourceID;
@@ -32,11 +29,13 @@ public class Yacht {
private Integer legNumber;
private Integer penaltiesAwarded;
private Integer penaltiesServed;
private Long estimateTimeAtNextMark;
private Long estimateTimeAtFinish;
private String position;
private double velocity;
private Long timeTillNext;
private Long markRoundTime;
// Mark rounding
private Long markRoundingTime;
private Mark lastMarkRounded;
private Mark nextMark;
@@ -134,14 +133,8 @@ public class Yacht {
this.penaltiesServed = penaltiesServed;
}
public Long getEstimateTimeAtNextMark() {
// DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
// return format.format(estimateTimeAtNextMark);
return estimateTimeAtNextMark;
}
public void setEstimateTimeAtNextMark(Long estimateTimeAtNextMark) {
this.estimateTimeAtNextMark = estimateTimeAtNextMark;
timeTillNext = estimateTimeAtNextMark;
}
public String getEstimateTimeAtFinish() {
@@ -169,20 +162,25 @@ public class Yacht {
this.colour = colour;
}
public Double getVelocity() {
return velocity;
}
public void setVelocity(double velocity) {
this.velocity = velocity;
}
public Long getMarkRoundingTime() {
return markRoundingTime;
}
public void setMarkRoundingTime(Long markRoundingTime) {
this.markRoundingTime = markRoundingTime;
this.markRoundTime = markRoundingTime;
}
public double getVelocity() {
return velocity;
}
public Long getTimeTillNext() {
return timeTillNext;
}
public Long getMarkRoundTime() {
return markRoundTime;
}
public Mark getLastMarkRounded() {
@@ -205,4 +203,5 @@ public class Yacht {
public Mark getNextMark(){
return nextMark;
}
}
@@ -416,9 +416,10 @@ public class StreamParser extends Thread {
double groundSpeed = bytesToLong(Arrays.copyOfRange(payload, 38, 40)) / 1000.0;
//type 1 is a racing yacht and type 3 is a mark, needed for updating positions of the mark and boat
if (deviceType == 1) {
BoatPositionPacket boatPacket = new BoatPositionPacket(boatId, timeValid, lat, lon,
heading, groundSpeed);
if (deviceType == 1){
Yacht boat = boats.get((int) boatId);
boat.setVelocity(groundSpeed);
BoatPositionPacket boatPacket = new BoatPositionPacket(boatId, timeValid, lat, lon, heading, groundSpeed);
//add a new priority que to the boatLocations HashMap
if (!boatLocations.containsKey(boatId)) {
+2 -2
View File
@@ -168,7 +168,7 @@ Remove scroll bars
.ui-table *.scroll-bar:vertical *.increment-arrow,
.ui-table *.scroll-bar:vertical *.decrement-arrow {
-fx-background-color: null;
-fx-background-color: #0e6d6c;
-fx-background-radius: 0;
-fx-background-insets: 0;
-fx-padding: 0;
@@ -177,7 +177,7 @@ Remove scroll bars
.ui-table *.scroll-bar:vertical *.increment-button,
.ui-table *.scroll-bar:vertical *.decrement-button {
-fx-background-color: null;
-fx-background-color: #0e6d6c;
-fx-background-radius: 0;
-fx-background-insets: 0;
-fx-padding: 0;
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>
<GridPane fx:id="finishScreenGridPane" maxHeight="837.0" maxWidth="837.0" minHeight="837.0" minWidth="837.0" nodeOrientation="LEFT_TO_RIGHT" prefHeight="837.0" prefWidth="837.0" style="-fx-background-color: #2C2c36;" 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.controllers.FinishScreenViewController">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="259.0" minHeight="259.0" prefHeight="259.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="64.0" minHeight="64.0" prefHeight="64.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="257.0" minHeight="257.0" prefHeight="257.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="257.0" minHeight="257.0" prefHeight="257.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label alignment="CENTER" text="Race Finished!" textFill="WHITE" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
<font>
<Font size="40.0" />
</font>
</Label>
<Label alignment="CENTER" text="Race Result:" textFill="WHITE" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER">
<font>
<Font size="28.0" />
</font>
</Label>
<TableView fx:id="finishOrderTable" maxWidth="661.0" prefHeight="324.0" prefWidth="629.0" styleClass="ui-table" GridPane.halignment="CENTER" GridPane.rowIndex="2">
<columns>
<TableColumn fx:id="posCol" editable="false" maxWidth="74.0" minWidth="74.0" prefWidth="74.0" resizable="false" sortable="false" text="Position" />
<TableColumn fx:id="boatNameCol" editable="false" maxWidth="171.0" minWidth="171.0" prefWidth="171.0" resizable="false" sortable="false" text="Boat Name" />
<TableColumn fx:id="shortNameCol" editable="false" maxWidth="155.18472290039062" minWidth="107.0" prefWidth="155.18472290039062" resizable="false" sortable="false" text="Short Name" />
<TableColumn fx:id="countryCol" editable="false" maxWidth="258.9999694824219" minWidth="147.0" prefWidth="258.9999694824219" resizable="false" sortable="false" text="Country" />
</columns>
<GridPane.margin>
<Insets bottom="50.0" />
</GridPane.margin>
</TableView>
<Button mnemonicParsing="false" onAction="#switchToStartScreenView" styleClass="blue-ui-btn" text="Return to Start Screen" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="TOP" />
</children>
</GridPane>
-55
View File
@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="raceResults" maxHeight="1080.0" maxWidth="1920.0" minHeight="1080.0" minWidth="1920.0" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<GridPane layoutX="444.0" layoutY="256.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="620.1734008789062" minWidth="10.0" prefWidth="493.2829895019531" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="862.0581665039062" minWidth="10.0" prefWidth="786.7170104980469" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="348.0" minHeight="10.0" prefHeight="112.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="348.0" minHeight="10.0" prefHeight="99.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="348.0" minHeight="7.0" prefHeight="88.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="348.0" minHeight="0.0" prefHeight="278.3376770019531" vgrow="SOMETIMES" />
<RowConstraints maxHeight="653.0" minHeight="0.0" prefHeight="98.66232299804688" vgrow="SOMETIMES" />
<RowConstraints maxHeight="812.0" minHeight="10.0" prefHeight="418.4577941894531" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Race Results:" wrappingWidth="616.5260620117188" GridPane.rowIndex="2">
<font>
<Font size="45.0" />
</font>
<GridPane.margin>
<Insets left="50.0" />
</GridPane.margin>
</Text>
<VBox fx:id="resultsVBox" prefHeight="44.0" prefWidth="571.0" GridPane.rowIndex="3">
<opaqueInsets>
<Insets />
</opaqueInsets>
<GridPane.margin>
<Insets left="50.0" />
</GridPane.margin>
<padding>
<Insets top="60.0" />
</padding></VBox>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Finish!" wrappingWidth="623.9530334472656" GridPane.rowIndex="1">
<font>
<Font size="75.0" />
</font>
<GridPane.margin>
<Insets left="50.0" />
</GridPane.margin>
</Text>
</children>
</GridPane>
</children>
</AnchorPane>
+8 -4
View File
@@ -9,8 +9,8 @@
<GridPane maxHeight="960.0" maxWidth="1530.0" prefHeight="960.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.controllers.RaceViewController">
<columnConstraints>
<ColumnConstraints maxWidth="246.0" minWidth="246.0" prefWidth="246.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="1034.0" />
<ColumnConstraints maxWidth="419.0" minWidth="-Infinity" prefWidth="258.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1278.0" minWidth="10.0" prefWidth="1272.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="500.0" prefHeight="30.0" vgrow="SOMETIMES" />
@@ -18,7 +18,9 @@
<RowConstraints />
</rowConstraints>
<children>
<AnchorPane prefHeight="960.0" prefWidth="250.0" style="-fx-background-color: #2C2c36;" GridPane.rowSpan="3">
<ScrollPane prefHeight="200.0" prefWidth="200.0">
<content>
<AnchorPane prefHeight="996.0" prefWidth="240.0" style="-fx-background-color: #2C2c36;">
<children>
<Label layoutX="11.0" layoutY="259.0" text="Team Position" textFill="WHITE" />
<Label layoutX="13.0" layoutY="432.0" text="Settings" textFill="WHITE" />
@@ -51,7 +53,7 @@
<Button fx:id="selectAnnotationBtn" 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="boatSelectionComboBox" layoutX="37.0" layoutY="664.0" prefHeight="25.0" prefWidth="170.0" promptText="Select Boat" 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">
<LineChart fx:id="raceSparkLine" layoutX="-1.0" layoutY="719.0" legendVisible="false" prefHeight="277.0" prefWidth="233.0" title="Boat Positions">
<xAxis>
<CategoryAxis label="Leg Number" side="BOTTOM" styleClass="spark-line-xaxis" />
</xAxis>
@@ -61,6 +63,8 @@
</LineChart>
</children>
</AnchorPane>
</content>
</ScrollPane>
<AnchorPane fx:id="contentAnchorPane" maxHeight="960.0" maxWidth="1280.0" prefHeight="960.0" prefWidth="1280.0" style="-fx-background-color: skyblue;" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2147483647" GridPane.valignment="TOP">
<children>
<fx:include fx:id="includedCanvas" source="CanvasView.fxml" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />