Compare commits

...

15 Commits

Author SHA1 Message Date
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
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
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
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
12 changed files with 389 additions and 449 deletions
-3
View File
@@ -29,9 +29,6 @@ public class App extends Application {
StreamReceiver.noMoreBytes(); StreamReceiver.noMoreBytes();
System.exit(0); System.exit(0);
}); });
} }
public static void main(String[] args) { public static void main(String[] args) {
@@ -10,13 +10,20 @@ import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.text.Font; import javafx.scene.shape.Polygon;
import seng302.models.BoatGroup; import javafx.scene.text.Text;
import seng302.fxObjects.BoatAnnotations;
import seng302.fxObjects.BoatGroup;
import seng302.fxObjects.Wake;
import seng302.models.Colors; import seng302.models.Colors;
import seng302.models.Yacht; 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.Boundary;
import seng302.models.map.CanvasMap; import seng302.models.map.CanvasMap;
import seng302.models.mark.*;
import seng302.models.stream.StreamParser; import seng302.models.stream.StreamParser;
import seng302.models.stream.XMLParser; import seng302.models.stream.XMLParser;
import seng302.models.stream.XMLParser.RaceXMLObject.Limit; import seng302.models.stream.XMLParser.RaceXMLObject.Limit;
@@ -45,7 +52,7 @@ public class CanvasController {
private Group group; private Group group;
private GraphicsContext gc; private GraphicsContext gc;
private ImageView mapImage; private ImageView mapImage;
private final int BUFFER_SIZE = 50; private final int BUFFER_SIZE = 50;
private final int PANEL_WIDTH = 1260; // it should be 1280 but, minors 40 to cancel the bias. private final int PANEL_WIDTH = 1260; // it should be 1280 but, minors 40 to cancel the bias.
private final int PANEL_HEIGHT = 960; private final int PANEL_HEIGHT = 960;
@@ -66,6 +73,8 @@ public class CanvasController {
private List<MarkGroup> markGroups = new ArrayList<>(); private List<MarkGroup> markGroups = new ArrayList<>();
private List<BoatGroup> boatGroups = new ArrayList<>(); private List<BoatGroup> boatGroups = new ArrayList<>();
private Text FPSdisplay = new Text();
private Polygon raceBorder = new Polygon();
//FRAME RATE //FRAME RATE
private Double frameRate = 60.0; private Double frameRate = 60.0;
@@ -108,36 +117,48 @@ public class CanvasController {
gc.setGlobalAlpha(0.5); gc.setGlobalAlpha(0.5);
fitMarksToCanvas(); fitMarksToCanvas();
drawGoogleMap(); drawGoogleMap();
// TODO: 1/05/17 wmu16 - Change this call to now draw the marks as from the xml FPSdisplay.setLayoutX(5);
initializeBoats(); FPSdisplay.setLayoutY(20);
FPSdisplay.setStrokeWidth(2);
group.getChildren().add(FPSdisplay);
group.getChildren().add(raceBorder);
initializeMarks(); initializeMarks();
timer = new AnimationTimer() { initializeBoats();
private int UPDATE_FPM_PERIOD = 50; // update FPM label every 50 frames timer = new AnimationTimer() {
private int updateFPMCounter = 100; private long lastTime = 0;
private int FPSCount = 30;
@Override @Override
public void handle(long now) { public void handle(long now) {
if (lastTime == 0) {
//fps stuff lastTime = now;
long oldFrameTime = frameTimes[frameTimeIndex] ; } else {
frameTimes[frameTimeIndex] = now ; if (now - lastTime >= (1e8 / 60)) { //Fix for framerate going above 60 when minimized
frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length ; long oldFrameTime = frameTimes[frameTimeIndex];
if (frameTimeIndex == 0) { frameTimes[frameTimeIndex] = now;
arrayFilled = true ; frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length;
} if (frameTimeIndex == 0) {
long elapsedNanos; arrayFilled = true;
if (arrayFilled) { }
elapsedNanos = now - oldFrameTime ; long elapsedNanos;
long elapsedNanosPerFrame = elapsedNanos / frameTimes.length ; if (arrayFilled) {
frameRate = 1_000_000_000.0 / elapsedNanosPerFrame ; elapsedNanos = now - oldFrameTime;
if (updateFPMCounter++ > UPDATE_FPM_PERIOD) { long elapsedNanosPerFrame = elapsedNanos / frameTimes.length;
updateFPMCounter = 0; frameRate = 1_000_000_000.0 / elapsedNanosPerFrame;
drawFps(frameRate.intValue()); if (FPSCount-- == 0) {
FPSCount = 30;
drawFps(frameRate.intValue());
}
raceViewController.updateSparkLine();
}
updateGroups();
if (StreamParser.isRaceFinished()) {
this.stop();
}
lastTime = now;
}
} }
raceViewController.updateSparkLine();
}
updateGroups();
if (StreamParser.isRaceFinished()) { if (StreamParser.isRaceFinished()) {
this.stop(); this.stop();
} }
@@ -180,34 +201,16 @@ public class CanvasController {
private void addRaceBorder() { private void addRaceBorder() {
XMLParser.RaceXMLObject raceXMLObject = StreamParser.getXmlObject().getRaceXML(); XMLParser.RaceXMLObject raceXMLObject = StreamParser.getXmlObject().getRaceXML();
ArrayList<Limit> courseLimits = raceXMLObject.getCourseLimit(); ArrayList<Limit> courseLimits = raceXMLObject.getCourseLimit();
gc.setStroke(Color.DARKBLUE); raceBorder.setStroke(new Color(0.0f, 0.0f, 0.74509807f, 1));
gc.setLineWidth(3); raceBorder.setStrokeWidth(3);
double[] xBoundaryPoints = new double[courseLimits.size()]; raceBorder.setFill(new Color(0,0,0,0));
double[] yBoundaryPoints = new double[courseLimits.size()]; List<Double> boundaryPoints = new ArrayList<>();
for (int i = 0; i < courseLimits.size() - 1; i++) { for (Limit limit : courseLimits) {
Limit thisPoint1 = courseLimits.get(i); Point2D location = findScaledXY(limit.getLat(), limit.getLng());
SingleMark thisMark1 = new SingleMark("", thisPoint1.getLat(), thisPoint1.getLng(), thisPoint1.getSeqID(), thisPoint1.getSeqID()); boundaryPoints.add(location.getX());
Limit thisPoint2 = courseLimits.get(i+1); boundaryPoints.add(location.getY());
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();
} }
Limit thisPoint1 = courseLimits.get(courseLimits.size()-1); raceBorder.getPoints().setAll(boundaryPoints);
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);
} }
private void updateGroups(){ private void updateGroups(){
@@ -233,8 +236,6 @@ public class CanvasController {
private void checkForCourseChanges() { private void checkForCourseChanges() {
if (StreamParser.isNewRaceXmlReceived()){ if (StreamParser.isNewRaceXmlReceived()){
gc.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
drawGoogleMap();
addRaceBorder(); addRaceBorder();
} }
} }
@@ -247,7 +248,9 @@ public class CanvasController {
BoatPositionPacket positionPacket = movementQueue.take(); BoatPositionPacket positionPacket = movementQueue.take();
Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon()); Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon());
double heading = 360.0 / 0xffff * positionPacket.getHeading(); 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){ } catch (InterruptedException e){
e.printStackTrace(); e.printStackTrace();
} }
@@ -273,7 +276,9 @@ public class CanvasController {
*/ */
private void initializeBoats() { private void initializeBoats() {
Map<Integer, Yacht> boats = StreamParser.getBoats(); 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<>(); ArrayList<Integer> participantIDs = new ArrayList<>();
@@ -286,10 +291,14 @@ public class CanvasController {
boat.setColour(Colors.getColor()); boat.setColour(Colors.getColor());
BoatGroup boatGroup = new BoatGroup(boat, boat.getColour()); BoatGroup boatGroup = new BoatGroup(boat, boat.getColour());
boatGroups.add(boatGroup); 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); group.getChildren().addAll(boatGroups);
} }
@@ -345,14 +354,10 @@ public class CanvasController {
private void drawFps(int fps){ private void drawFps(int fps){
if (raceViewController.isDisplayFps()){ if (raceViewController.isDisplayFps()){
gc.clearRect(5, 5, 60, 30); FPSdisplay.setVisible(true);
gc.setFont(new Font(16)); FPSdisplay.setText(String.format("%d FPS", fps));
gc.setLineWidth(4);
gc.setGlobalAlpha(0.75);
gc.fillText(fps + " FPS", 5, 20);
gc.setGlobalAlpha(0.5);
} else { } else {
gc.clearRect(5,5,60,30); FPSdisplay.setVisible(false);
} }
} }
@@ -7,7 +7,6 @@ import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.geometry.Point2D; import javafx.geometry.Point2D;
import javafx.geometry.Side;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.chart.LineChart; import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis; import javafx.scene.chart.NumberAxis;
@@ -33,10 +32,11 @@ import seng302.controllers.annotations.Annotation;
import seng302.controllers.annotations.ImportantAnnotationController; import seng302.controllers.annotations.ImportantAnnotationController;
import seng302.controllers.annotations.ImportantAnnotationDelegate; import seng302.controllers.annotations.ImportantAnnotationDelegate;
import seng302.controllers.annotations.ImportantAnnotationsState; import seng302.controllers.annotations.ImportantAnnotationsState;
import seng302.fxObjects.BoatGroup;
import seng302.fxObjects.MarkGroup;
import seng302.models.*; import seng302.models.*;
import seng302.models.mark.GateMark; import seng302.models.mark.GateMark;
import seng302.models.mark.Mark; import seng302.models.mark.Mark;
import seng302.models.mark.MarkGroup;
import seng302.models.mark.SingleMark; import seng302.models.mark.SingleMark;
import seng302.models.stream.StreamParser; import seng302.models.stream.StreamParser;
import seng302.models.stream.XMLParser; import seng302.models.stream.XMLParser;
@@ -571,76 +571,31 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
return displayFps; 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) { private void setAnnotations(Integer annotationLevel) {
switch (annotationLevel) { switch (annotationLevel) {
// No Annotations // No Annotations
case 0: case 0:
for (BoatGroup bg : includedCanvasController.getBoatGroups()) { for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
bg.setTeamNameObjectVisible(false); bg.setVisibility(false, false, false, false, false, false);
bg.setVelocityObjectVisible(false);
bg.setEstTimeToNextMarkObjectVisible(false);
bg.setLegTimeObjectVisible(false);
bg.setLineGroupVisible(false);
bg.setWakeVisible(false);
} }
break; break;
// Important Annotations // Important Annotations
case 1: case 1:
for (BoatGroup bg : includedCanvasController.getBoatGroups()) { 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; break;
// All Annotations // All Annotations
case 2: case 2:
for (BoatGroup bg : includedCanvasController.getBoatGroups()) { for (BoatGroup bg : includedCanvasController.getBoatGroups()) {
bg.setTeamNameObjectVisible(true); bg.setVisibility(true, true, true, true, true, true);
bg.setVelocityObjectVisible(true);
bg.setEstTimeToNextMarkObjectVisible(true);
bg.setLegTimeObjectVisible(true);
bg.setLineGroupVisible(true);
bg.setWakeVisible(true);
} }
break; break;
} }
@@ -6,12 +6,8 @@ import javafx.scene.control.Button;
import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBox;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage; import javafx.stage.Stage;
import seng302.controllers.RaceViewController;
import seng302.controllers.annotations.Annotation;
import java.net.URL; import java.net.URL;;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
public class ImportantAnnotationController implements Initializable { 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 java.util.ArrayList;
import javafx.event.EventHandler; import javafx.event.EventHandler;
@@ -10,6 +10,7 @@ import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon; import javafx.scene.shape.Polygon;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.scene.transform.Rotate; import javafx.scene.transform.Rotate;
import seng302.models.Yacht;
import seng302.GeometryUtils; import seng302.GeometryUtils;
import seng302.controllers.CanvasController; import seng302.controllers.CanvasController;
import seng302.models.mark.GateMark; import seng302.models.mark.GateMark;
@@ -31,14 +32,6 @@ import java.text.SimpleDateFormat;
public class BoatGroup extends Group { public class BoatGroup extends Group {
//Constants for drawing //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_HEIGHT = 15d;
private static final double BOAT_WIDTH = 10d; private static final double BOAT_WIDTH = 10d;
//Variables for boat logic. //Variables for boat logic.
@@ -52,19 +45,15 @@ public class BoatGroup extends Group {
private Yacht boat; private Yacht boat;
private Group lineGroup = new Group(); private Group lineGroup = new Group();
private Polygon boatPoly; private Polygon boatPoly;
private Text teamNameObject;
private Text velocityObject;
private Text estTimeToNextMarkObject;
private Text legTimeObject;
private Wake wake; private Wake wake;
private Line leftLayLine; private Line leftLayLine;
private Line rightLayline; private Line rightLayline;
private Double distanceTravelled = 0.0; private Double distanceTravelled = 0.0;
private Point2D lastPoint; private Point2D lastPoint;
private boolean destinationSet; 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. * 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. * @param color The colour of the boat polygon and the trailing line.
*/ */
public BoatGroup(Yacht boat, Color color) { public BoatGroup(Yacht boat, Color color) {
destinationSet = false;
this.boat = boat; this.boat = boat;
initChildren(color); initChildren(color);
this.textColor = color;
} }
/** /**
@@ -90,27 +79,11 @@ public class BoatGroup extends Group {
* polygon. * polygon.
*/ */
public BoatGroup(Yacht boat, Color color, double... points) { public BoatGroup(Yacht boat, Color color, double... points) {
destinationSet = false;
this.boat = boat; this.boat = boat;
initChildren(color, points); 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. * Creates the javafx objects that will be the in the group by default.
* *
@@ -119,52 +92,26 @@ public class BoatGroup extends Group {
* polygon. * polygon.
*/ */
private void initChildren(Color color, double... points) { private void initChildren(Color color, double... points) {
textColor = color;
destinationSet = false;
boatPoly = new Polygon(points); boatPoly = new Polygon(points);
boatPoly.setFill(color); boatPoly.setFill(color);
boatPoly.setOnMouseEntered(event -> boatPoly.setFill(Color.FLORALWHITE)); boatPoly.setOnMouseEntered(event -> {
boatPoly.setOnMouseExited(event -> boatPoly.setFill(color)); boatPoly.setFill(Color.FLORALWHITE);
boatPoly.setStroke(Color.RED);
});
boatPoly.setOnMouseExited(event -> {
boatPoly.setFill(color);
boatPoly.setStroke(Color.BLACK);
});
boatPoly.setOnMouseClicked(event -> setIsSelected(!isSelected)); boatPoly.setOnMouseClicked(event -> setIsSelected(!isSelected));
boatPoly.setCache(true); boatPoly.setCache(true);
boatPoly.setCacheHint(CacheHint.SPEED); boatPoly.setCacheHint(CacheHint.SPEED);
boatAnnotations = new BoatAnnotations(boat, color);
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());
}
leftLayLine = new Line(); leftLayLine = new Line();
rightLayline = new Line(); rightLayline = new Line();
wake = new Wake(0, -BOAT_HEIGHT); wake = new Wake(0, -BOAT_HEIGHT);
super.getChildren() super.getChildren().addAll(boatPoly, boatAnnotations);
.addAll(teamNameObject, velocityObject, boatPoly, estTimeToNextMarkObject,
legTimeObject, leftLayLine, rightLayline);
} }
/** /**
@@ -189,14 +136,8 @@ public class BoatGroup extends Group {
private void moveGroupBy(double dx, double dy) { private void moveGroupBy(double dx, double dy) {
boatPoly.setLayoutX(boatPoly.getLayoutX() + dx); boatPoly.setLayoutX(boatPoly.getLayoutX() + dx);
boatPoly.setLayoutY(boatPoly.getLayoutY() + dy); boatPoly.setLayoutY(boatPoly.getLayoutY() + dy);
teamNameObject.setLayoutX(teamNameObject.getLayoutX() + dx); boatAnnotations.setLayoutX(boatAnnotations.getLayoutX() + dx);
teamNameObject.setLayoutY(teamNameObject.getLayoutY() + dy); boatAnnotations.setLayoutY(boatAnnotations.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);
wake.setLayoutX(wake.getLayoutX() + dx); wake.setLayoutX(wake.getLayoutX() + dx);
wake.setLayoutY(wake.getLayoutY() + dy); wake.setLayoutY(wake.getLayoutY() + dy);
} }
@@ -212,14 +153,8 @@ public class BoatGroup extends Group {
rotateTo(rotation); rotateTo(rotation);
boatPoly.setLayoutX(x); boatPoly.setLayoutX(x);
boatPoly.setLayoutY(y); boatPoly.setLayoutY(y);
teamNameObject.setLayoutX(x); boatAnnotations.setLayoutX(x);
teamNameObject.setLayoutY(y); boatAnnotations.setLayoutY(y);
velocityObject.setLayoutX(x);
velocityObject.setLayoutY(y);
estTimeToNextMarkObject.setLayoutX(x);
estTimeToNextMarkObject.setLayoutY(y);
legTimeObject.setLayoutX(x);
legTimeObject.setLayoutY(y);
wake.setLayoutX(x); wake.setLayoutX(x);
wake.setLayoutY(y); wake.setLayoutY(y);
wake.rotate(rotation); wake.rotate(rotation);
@@ -229,42 +164,6 @@ public class BoatGroup extends Group {
boatPoly.getTransforms().setAll(new Rotate(rotation)); 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() { public void move() {
double dx = xIncrement * framesToMove; double dx = xIncrement * framesToMove;
double dy = yIncrement * framesToMove; double dy = yIncrement * framesToMove;
@@ -298,33 +197,7 @@ public class BoatGroup extends Group {
lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY());
} }
} }
wake.updatePosition();
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;
} }
/** /**
@@ -336,7 +209,7 @@ public class BoatGroup extends Group {
* @param timeValid the time the position values are valid for * @param timeValid the time the position values are valid for
*/ */
public void setDestination(double newXValue, double newYValue, double rotation, 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) { if (lastTimeValid == 0) {
lastTimeValid = timeValid - 200; lastTimeValid = timeValid - 200;
moveTo(newXValue, newYValue, rotation); moveTo(newXValue, newYValue, rotation);
@@ -350,24 +223,13 @@ public class BoatGroup extends Group {
destinationSet = true; destinationSet = true;
Double rotationalVelocity = calculateRotationalVelocity(rotation);
updateTimeTillNextMark();
updateLastMarkRoundingTime();
if (Math.abs(rotationalVelocity) > 0.075) {
rotationalVelocity = 0.0;
wake.rotate(rotation);
}
rotateTo(rotation); rotateTo(rotation);
wake.setRotationalVelocity(rotationalVelocity, groundSpeed); wake.setRotation(rotation, groundSpeed);
boat.setVelocity(groundSpeed);
velocityObject.setText(String.format("%.2f m/s", groundSpeed));
lastTimeValid = timeValid; lastTimeValid = timeValid;
isStopped = false; isStopped = false;
lastRotation = rotation; lastRotation = rotation;
boatAnnotations.update();
} }
@@ -411,30 +273,16 @@ public class BoatGroup extends Group {
public void setIsSelected(Boolean isSelected) { public void setIsSelected(Boolean isSelected) {
this.isSelected = isSelected; this.isSelected = isSelected;
setTeamNameObjectVisible(isSelected);
setVelocityObjectVisible(isSelected);
setLineGroupVisible(isSelected); setLineGroupVisible(isSelected);
setWakeVisible(isSelected); setWakeVisible(isSelected);
setEstTimeToNextMarkObjectVisible(isSelected); boatAnnotations.setVisible(isSelected);
setLegTimeObjectVisible(isSelected);
setLayLinesVisible(isSelected); setLayLinesVisible(isSelected);
} }
public void setVisibility (boolean teamName, boolean velocity, boolean estTime, boolean legTime, boolean trail, boolean wake) {
public void setTeamNameObjectVisible(Boolean visible) { boatAnnotations.setVisibile(teamName, velocity, estTime, legTime);
teamNameObject.setVisible(visible); this.wake.setVisible(wake);
} this.lineGroup.setVisible(trail);
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 setLineGroupVisible(Boolean visible) { public void setLineGroupVisible(Boolean visible) {
@@ -475,19 +323,17 @@ public class BoatGroup extends Group {
return boat.getSourceID(); return boat.getSourceID();
} }
/** public Group getWake () {
* Due to javaFX limitations annotations associated with a boat that you want to appear below return wake;
* 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 getTrail() {
return lineGroup;
}
public Group getAnnotations() {
return boatAnnotations;
}
public Double getBoatLayoutX() { public Double getBoatLayoutX() {
return boatPoly.getLayoutX(); return boatPoly.getLayoutX();
@@ -506,4 +352,5 @@ public class BoatGroup extends Group {
public String toString() { public String toString() {
return boat.toString(); return boat.toString();
} }
} }
@@ -1,4 +1,4 @@
package seng302.models.mark; package seng302.fxObjects;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -8,6 +8,10 @@ import javafx.scene.Node;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.shape.Circle; import javafx.scene.shape.Circle;
import javafx.scene.shape.Line; 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; import seng302.GeometryUtils;
/** /**
@@ -109,34 +113,35 @@ public class MarkGroup extends Group {
} }
super.getChildren().add(line); 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) public void moveMarkTo (double x, double y, long raceId)
{ {
if (mainMark.getMarkType() == MarkType.SINGLE_MARK) { if (mainMark.getMarkType() == MarkType.SINGLE_MARK) {
Circle markCircle = (Circle) super.getChildren().get(0); Circle markCircle = (Circle) super.getChildren().get(0);
//One of the test streams produced frequent, jittery movements. Added this as a fix.
markCircle.setCenterX(x); if (Math.abs(markCircle.getCenterX() - x) > 5 || Math.abs(markCircle.getCenterY() - y) > 5) {
markCircle.setCenterY(y); markCircle.setCenterX(x);
markCircle.setCenterY(y);
}
} else { } else {
Circle markCircle1 = (Circle) super.getChildren().get(0); Circle markCircle1 = (Circle) super.getChildren().get(0);
Circle markCircle2 = (Circle) super.getChildren().get(1); Circle markCircle2 = (Circle) super.getChildren().get(1);
Line connectingLine = (Line) super.getChildren().get(2); Line connectingLine = (Line) super.getChildren().get(2);
if (marks.get(0).getId() == raceId) { if (marks.get(0).getId() == raceId) {
markCircle1.setCenterX(x); if (Math.abs(markCircle1.getCenterX() - x) > 5 || Math.abs(markCircle1.getCenterY() - y) > 5) {
markCircle1.setCenterY(y); markCircle1.setCenterX(x);
connectingLine.setStartX(markCircle1.getCenterX()); markCircle1.setCenterY(y);
connectingLine.setStartY(markCircle1.getCenterY()); connectingLine.setStartX(markCircle1.getCenterX());
connectingLine.setStartY(markCircle1.getCenterY());
}
} else if (marks.get(1).getId() == raceId) { } else if (marks.get(1).getId() == raceId) {
markCircle2.setCenterX(x); if (Math.abs(markCircle2.getCenterX() - x) > 5 || Math.abs(markCircle2.getCenterY() - y) > 5) {
markCircle2.setCenterY(y); markCircle2.setCenterX(x);
connectingLine.setEndX(markCircle2.getCenterX()); markCircle2.setCenterY(y);
connectingLine.setEndY(markCircle2.getCenterY()); connectingLine.setEndX(markCircle2.getCenterX());
connectingLine.setEndY(markCircle2.getCenterY());
}
} }
} }
} }
@@ -1,4 +1,4 @@
package seng302.models; package seng302.fxObjects;
import javafx.scene.CacheHint; import javafx.scene.CacheHint;
import javafx.scene.Group; import javafx.scene.Group;
@@ -7,24 +7,24 @@ import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType; import javafx.scene.shape.ArcType;
import javafx.scene.shape.StrokeLineCap; import javafx.scene.shape.StrokeLineCap;
import javafx.scene.transform.Rotate; import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
/** /**
* A group containing objects used to represent wakes onscreen. Contains functionality for their animation. * 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 //The number of wakes
private int numWakes = 8; 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. //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; 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. //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 Arc[] arcs = new Arc[numWakes];
private double[] rotationalVelocities = new double[numWakes]; private double[] rotationalVelocities = new double[numWakes];
private double[] rotations = new double[numWakes]; private double[] rotations = new double[numWakes];
private double baseRad;
/** /**
* Create a wake at the given location. * Create a wake at the given location.
@@ -40,62 +40,60 @@ class Wake extends Group {
//Default triangle is -110 deg out of phase with a default wake and has angle of 40 deg. //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 = new Arc(0, 0, 0, 0, -110, 40);
arc.setCache(true); arc.setCache(true);
arc.setCacheHint(CacheHint.SPEED); arc.setCacheHint(CacheHint.ROTATE);
arc.setType(ArcType.OPEN); arc.setType(ArcType.OPEN);
arc.setStroke(new Color(0.18, 0.7, 1.0, 1.0 + (-0.99 / numWakes * i))); arc.setStroke(new Color(0.18, 0.7, 1.0, 1.0 + (-0.99 / numWakes * i)));
arc.setStrokeWidth(3.0); arc.setStrokeWidth(3.0);
arc.setStrokeLineCap(StrokeLineCap.ROUND); arc.setStrokeLineCap(StrokeLineCap.ROUND);
arc.setFill(new Color(0.0, 0.0, 0.0, 0.0)); arc.setFill(new Color(0.0, 0.0, 0.0, 0.0));
baseRad = (20 / numWakes);
arcs[i] = arc; arcs[i] = arc;
arc.getTransforms().setAll(
new Rotate(1)
);
} }
super.getChildren().addAll(arcs); super.getChildren().addAll(arcs);
} }
/** void setRotation (double rotation, double velocity) {
* Sets the rotationalVelocity of each arc. if (Math.abs(rotations[0] - rotation) > 20) {
* rotate(rotation);
* @param rotationalVelocity The rotationalVelocity the wake should move at. } else {
* @param velocity The real world velocity of the boat in m/s. rotations[0] = rotation;
*/ ((Rotate) arcs[0].getTransforms().get(0)).setAngle(rotation);
void setRotationalVelocity(double rotationalVelocity, double velocity) { for (int i = 1; i < numWakes; i++) {
rotationalVelocities[0] = rotationalVelocity; double wakeSeparationRad = Math.toRadians(rotations[i - 1] - rotations[i]);
for (int i = 1; i < numWakes; i++) { double shortestDistance = Math.atan2(
double wakeSeparationRad = Math.toRadians(rotations[i - 1] - rotations[i]); Math.sin(wakeSeparationRad),
double shortestDistance = Math.atan2( Math.cos(wakeSeparationRad)
Math.sin(wakeSeparationRad), );
Math.cos(wakeSeparationRad) double distDeg = Math.toDegrees(shortestDistance);
); if (rotationalVelocities[i - 1] < 0.01 && rotationalVelocities[i - 1] > -0.01) {
double distDeg = Math.toDegrees(shortestDistance); rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * 2 * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes);
if (rotationalVelocities[i - 1] < 0.01 && rotationalVelocities[i - 1] > -0.01) { } else {
rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes); if (distDeg < (MAX_DIFF / numWakes)) {
rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes);
} else { } else
if (distDeg < (MAX_DIFF / numWakes)) rotationalVelocities[i] = rotationalVelocities[i - 1];
rotationalVelocities[i] = rotationalVelocities[i - 1] * 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) { for (Arc arc : arcs) {
arc.setRadiusX(rad); arc.setRadiusX(rad);
arc.setRadiusY(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. * 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++) { for (int i = 0; i < numWakes; i++) {
rotations[i] = rotations[i] + rotationalVelocities[i] * timeInterval; rotations[i] = rotations[i] + rotationalVelocities[i];
arcs[i].getTransforms().setAll(new Rotate(rotations[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.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import seng302.models.stream.StreamParser;
import seng302.models.stream.XMLParser.RaceXMLObject.Corner;
/** /**
* Yacht class for the racing boat. * Yacht class for the racing boat.
@@ -19,7 +17,6 @@ public class Yacht {
// Used in boat group // Used in boat group
private Color colour; private Color colour;
private double velocity;
private String boatType; private String boatType;
private Integer sourceID; private Integer sourceID;
@@ -32,11 +29,13 @@ public class Yacht {
private Integer legNumber; private Integer legNumber;
private Integer penaltiesAwarded; private Integer penaltiesAwarded;
private Integer penaltiesServed; private Integer penaltiesServed;
private Long estimateTimeAtNextMark;
private Long estimateTimeAtFinish; private Long estimateTimeAtFinish;
private String position; private String position;
private double velocity;
private Long timeTillNext;
private Long markRoundTime;
// Mark rounding // Mark rounding
private Long markRoundingTime;
private Mark lastMarkRounded; private Mark lastMarkRounded;
private Mark nextMark; private Mark nextMark;
@@ -134,14 +133,8 @@ public class Yacht {
this.penaltiesServed = penaltiesServed; 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) { public void setEstimateTimeAtNextMark(Long estimateTimeAtNextMark) {
this.estimateTimeAtNextMark = estimateTimeAtNextMark; timeTillNext = estimateTimeAtNextMark;
} }
public String getEstimateTimeAtFinish() { public String getEstimateTimeAtFinish() {
@@ -169,20 +162,25 @@ public class Yacht {
this.colour = colour; this.colour = colour;
} }
public Double getVelocity() {
return velocity;
}
public void setVelocity(double velocity) { public void setVelocity(double velocity) {
this.velocity = velocity; this.velocity = velocity;
} }
public Long getMarkRoundingTime() {
return markRoundingTime;
}
public void setMarkRoundingTime(Long 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() { public Mark getLastMarkRounded() {
@@ -205,4 +203,5 @@ public class Yacht {
public Mark getNextMark(){ public Mark getNextMark(){
return nextMark; return nextMark;
} }
} }
@@ -416,9 +416,10 @@ public class StreamParser extends Thread {
double groundSpeed = bytesToLong(Arrays.copyOfRange(payload, 38, 40)) / 1000.0; 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 //type 1 is a racing yacht and type 3 is a mark, needed for updating positions of the mark and boat
if (deviceType == 1) { if (deviceType == 1){
BoatPositionPacket boatPacket = new BoatPositionPacket(boatId, timeValid, lat, lon, Yacht boat = boats.get((int) boatId);
heading, groundSpeed); boat.setVelocity(groundSpeed);
BoatPositionPacket boatPacket = new BoatPositionPacket(boatId, timeValid, lat, lon, heading, groundSpeed);
//add a new priority que to the boatLocations HashMap //add a new priority que to the boatLocations HashMap
if (!boatLocations.containsKey(boatId)) { 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 *.increment-arrow,
.ui-table *.scroll-bar:vertical *.decrement-arrow { .ui-table *.scroll-bar:vertical *.decrement-arrow {
-fx-background-color: null; -fx-background-color: #0e6d6c;
-fx-background-radius: 0; -fx-background-radius: 0;
-fx-background-insets: 0; -fx-background-insets: 0;
-fx-padding: 0; -fx-padding: 0;
@@ -177,7 +177,7 @@ Remove scroll bars
.ui-table *.scroll-bar:vertical *.increment-button, .ui-table *.scroll-bar:vertical *.increment-button,
.ui-table *.scroll-bar:vertical *.decrement-button { .ui-table *.scroll-bar:vertical *.decrement-button {
-fx-background-color: null; -fx-background-color: #0e6d6c;
-fx-background-radius: 0; -fx-background-radius: 0;
-fx-background-insets: 0; -fx-background-insets: 0;
-fx-padding: 0; -fx-padding: 0;
+49 -45
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"> <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>
<ColumnConstraints maxWidth="246.0" minWidth="246.0" prefWidth="246.0" /> <ColumnConstraints maxWidth="419.0" minWidth="-Infinity" prefWidth="258.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="1034.0" /> <ColumnConstraints hgrow="SOMETIMES" maxWidth="1278.0" minWidth="10.0" prefWidth="1272.0" />
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints minHeight="500.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="500.0" prefHeight="30.0" vgrow="SOMETIMES" />
@@ -18,49 +18,53 @@
<RowConstraints /> <RowConstraints />
</rowConstraints> </rowConstraints>
<children> <children>
<AnchorPane prefHeight="960.0" prefWidth="250.0" style="-fx-background-color: #2C2c36;" GridPane.rowSpan="3"> <ScrollPane prefHeight="200.0" prefWidth="200.0">
<children> <content>
<Label layoutX="11.0" layoutY="259.0" text="Team Position" textFill="WHITE" /> <AnchorPane prefHeight="996.0" prefWidth="240.0" style="-fx-background-color: #2C2c36;">
<Label layoutX="13.0" layoutY="432.0" text="Settings" textFill="WHITE" /> <children>
<Label layoutX="11.0" layoutY="14.0" text="Timer" textFill="WHITE" /> <Label layoutX="11.0" layoutY="259.0" text="Team Position" textFill="WHITE" />
<Label layoutX="11.0" layoutY="88.0" text="Wind direction" textFill="WHITE" /> <Label layoutX="13.0" layoutY="432.0" text="Settings" textFill="WHITE" />
<Circle fx:id="windBackgroundCircle" blendMode="DARKEN" fill="#3dcdc8" layoutX="110.0" layoutY="166.0" radius="35.0" stroke="#d7d7d7" strokeType="INSIDE" strokeWidth="3.0" /> <Label layoutX="11.0" layoutY="14.0" text="Timer" textFill="WHITE" />
<Text fx:id="windArrowText" fill="#a8a8a8" layoutX="86.0" layoutY="186.0" strokeType="OUTSIDE" strokeWidth="0.0" text="↓"> <Label layoutX="11.0" layoutY="88.0" text="Wind direction" textFill="WHITE" />
<font> <Circle fx:id="windBackgroundCircle" blendMode="DARKEN" fill="#3dcdc8" layoutX="110.0" layoutY="166.0" radius="35.0" stroke="#d7d7d7" strokeType="INSIDE" strokeWidth="3.0" />
<Font name="AdobeArabic-Regular" size="55.0" /> <Text fx:id="windArrowText" fill="#a8a8a8" layoutX="86.0" layoutY="186.0" strokeType="OUTSIDE" strokeWidth="0.0" text="↓">
</font> <font>
</Text> <Font name="AdobeArabic-Regular" size="55.0" />
<Text fx:id="windDirectionText" fill="#d3d3d3" layoutX="171.0" layoutY="214.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0°" textAlignment="RIGHT"> </font>
<font> </Text>
<Font name="System Bold" size="13.0" /> <Text fx:id="windDirectionText" fill="#d3d3d3" layoutX="171.0" layoutY="214.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0°" textAlignment="RIGHT">
</font> <font>
</Text> <Font name="System Bold" size="13.0" />
<CheckBox fx:id="toggleFps" 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" /> </font>
<VBox fx:id="positionVbox" layoutX="12.0" layoutY="280.0" prefHeight="140.0" prefWidth="200.0" styleClass="text-white" /> </Text>
<Pane layoutX="11.0" layoutY="30.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="51.0" prefWidth="193.0"> <CheckBox fx:id="toggleFps" 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" />
<children> <VBox fx:id="positionVbox" layoutX="12.0" layoutY="280.0" prefHeight="140.0" prefWidth="200.0" styleClass="text-white" />
<Text fx:id="timerLabel" fill="#f8f8f8" layoutX="-26.0" layoutY="34.0" strokeType="OUTSIDE" strokeWidth="0.0" text="00:00" textAlignment="CENTER" wrappingWidth="246.0"> <Pane layoutX="11.0" layoutY="30.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="51.0" prefWidth="193.0">
<font> <children>
<Font size="25.0" /> <Text fx:id="timerLabel" fill="#f8f8f8" layoutX="-26.0" layoutY="34.0" strokeType="OUTSIDE" strokeWidth="0.0" text="00:00" textAlignment="CENTER" wrappingWidth="246.0">
</font> <font>
</Text> <Font size="25.0" />
</children> </font>
</Pane> </Text>
<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" /> </children>
<Label layoutX="10.0" layoutY="499.0" text="Annotations" textFill="WHITE" /> </Pane>
<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" /> <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" />
<Text fill="WHITE" layoutX="11.0" layoutY="649.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Boat Selection" /> <Label layoutX="10.0" layoutY="499.0" text="Annotations" textFill="WHITE" />
<ComboBox fx:id="boatSelectionComboBox" layoutX="37.0" layoutY="664.0" prefHeight="25.0" prefWidth="170.0" promptText="Select Boat" styleClass="combo-box-base" /> <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" />
<LineChart fx:id="raceSparkLine" layoutX="-1.0" layoutY="719.0" legendVisible="false" prefHeight="277.0" prefWidth="246.0" title="Boat Positions"> <Text fill="WHITE" layoutX="11.0" layoutY="649.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Boat Selection" />
<xAxis> <ComboBox fx:id="boatSelectionComboBox" layoutX="37.0" layoutY="664.0" prefHeight="25.0" prefWidth="170.0" promptText="Select Boat" styleClass="combo-box-base" />
<CategoryAxis label="Leg Number" side="BOTTOM" styleClass="spark-line-xaxis" /> <LineChart fx:id="raceSparkLine" layoutX="-1.0" layoutY="719.0" legendVisible="false" prefHeight="277.0" prefWidth="233.0" title="Boat Positions">
</xAxis> <xAxis>
<yAxis> <CategoryAxis label="Leg Number" side="BOTTOM" styleClass="spark-line-xaxis" />
<NumberAxis fx:id="sparklineYAxis" minorTickCount="1" minorTickLength="1.0" side="LEFT" styleClass="spark-line-yaxis" tickLabelGap="1.0" tickUnit="1.0" upperBound="7.0" /> </xAxis>
</yAxis> <yAxis>
</LineChart> <NumberAxis fx:id="sparklineYAxis" minorTickCount="1" minorTickLength="1.0" side="LEFT" styleClass="spark-line-yaxis" tickLabelGap="1.0" tickUnit="1.0" upperBound="7.0" />
</children> </yAxis>
</AnchorPane> </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"> <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> <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" /> <fx:include fx:id="includedCanvas" source="CanvasView.fxml" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />