Further work on new wake system. Wakes turn correctly but need to scale with velocity and

eventually desync with the boats. Needs to reset to the boats position on straights.
This commit is contained in:
Calum
2017-04-28 23:25:49 +12:00
parent 765f27f987
commit 474f0ee427
10 changed files with 296 additions and 267 deletions
@@ -11,6 +11,8 @@ import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.text.Font;
import javafx.util.Pair;
import seng302.models.Boat;
@@ -22,6 +24,7 @@ import seng302.models.parsers.StreamParser;
import seng302.models.parsers.StreamReceiver;
import java.sql.Time;
import java.text.DecimalFormat;
import java.util.*;
/**
@@ -59,6 +62,13 @@ public class CanvasController {
private double metersToPixels;
private List<RaceObject> raceObjects = new ArrayList<>();
//FRAME RATE
private static final double UPDATE_TIME = 0.016666; // 1 / 60 ie 60fps
private final long[] frameTimes = new long[30];
private int frameTimeIndex = 0;
private boolean arrayFilled = false;
private DecimalFormat decimalFormat2dp = new DecimalFormat("0.00");
public AnimationTimer timer;
private enum ScaleDirection {
@@ -80,8 +90,8 @@ public class CanvasController {
// Bind canvas size to stack pane size.
canvas.widthProperty().bind(new SimpleDoubleProperty(CANVAS_WIDTH));
canvas.heightProperty().bind(new SimpleDoubleProperty(CANVAS_HEIGHT));
group.minWidth(CANVAS_WIDTH);
group.minHeight(CANVAS_HEIGHT);
//group.minWidth(CANVAS_WIDTH);
//group.minHeight(CANVAS_HEIGHT);
}
public void initializeCanvas (){
@@ -138,6 +148,8 @@ public class CanvasController {
timer = new AnimationTimer() {
private int countdown = 60;
private int[] currentRaceMarker = {1, 1, 1, 1, 1, 1};
List<Mark> marks = raceViewController.getRace().getCourse();
@@ -150,6 +162,20 @@ public class CanvasController {
int boatIndex = 0;
Mark nextMark;
long oldFrameTime = frameTimes[frameTimeIndex] ;
frameTimes[frameTimeIndex] = now ;
frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length ;
if (frameTimeIndex == 0) {
arrayFilled = true ;
}
if (arrayFilled) {
long elapsedNanos = now - oldFrameTime ;
long elapsedNanosPerFrame = elapsedNanos / frameTimes.length ;
Double frameRate = 1_000_000_000.0 / elapsedNanosPerFrame ;
drawFps(frameRate.intValue());
}
//if (countdown == 0) {
//System.out.println("called the at");
for (RaceObject raceObject : raceObjects) {
@@ -163,13 +189,10 @@ public class CanvasController {
//descending = nextMark.getY() > boatGroup.getLayoutY();
//leftToRight = nextMark.getX() < boatGroup.getLayoutX();
raceObject.updatePosition(1000 / 60);
for (int id : raceObject.getRaceIds()) {
//System.out.println("id = " + id);
if (id != 0 && StreamParser.boatPositions.size() > 0) {
boolean test = StreamParser.boatPositions.containsKey(id);
if (StreamParser.boatPositions.containsKey((long) id)) {
Point3D p = StreamParser.boatPositions.get((long) id);
Point2D p2d = latLonToXY(p.getX(), p.getY());
@@ -285,10 +308,15 @@ public class CanvasController {
private void drawFps(int fps){
if (raceViewController.isDisplayFps()){
gc.clearRect(5,5,50,20);
gc.setFill(Color.BLACK);
gc.setFont(new Font(14));
gc.setLineWidth(3);
gc.fillText(fps + " FPS", 5, 20);
} else {
gc.clearRect(5,5,50,20);
gc.setFill(Color.SKYBLUE);
gc.fillRect(4,4,51,21);
}
}
@@ -303,16 +331,28 @@ public class CanvasController {
Double startingY = raceObjects.get(0).getLayoutY();
Double firstMarkX = raceObjects.get(1).getLayoutX();
Double firstMarkY = raceObjects.get(1).getLayoutY();
Arc a = new Arc(300, 300, 45, 45, -90, 45);
a.setType(ArcType.ROUND);
group.getChildren().add(a);
a = new Arc(500, 500, 45, 45, 450, 45);
a.setType(ArcType.ROUND);
group.getChildren().add(a);
Group boatAnnotations = new Group();
for (Boat boat : boats) {
BoatGroup boatGroup = new BoatGroup(boat, Colors.getColor());
System.out.println("MADE A BOAT GROUP FOR " + boatGroup.getBoat().getShortName());
boatGroup.moveTo(startingX, startingY, 0d);
boatGroup.setDestination(firstMarkX, firstMarkY);
// boatGroup.setDestination(firstMarkX, firstMarkY);
boatGroup.forceRotation();
group.getChildren().add(boatGroup);
//group.getChildren().add(boatGroup);
raceObjects.add(boatGroup);
boatAnnotations.getChildren().add(boatGroup.getLowPriorityAnnotations());
// drawBoat(boat.getLongitude(), boat.getLatitude(), boat.getColor(), boat.getShortName(), boat.getSpeedInKnots(), boat.getHeading());
}
group.getChildren().add(boatAnnotations);
group.getChildren().addAll(raceObjects);
}
@@ -555,14 +595,15 @@ public class CanvasController {
// gateMark.getSingleMark2().setY((int) canvasLocation.getY());
markGroup = new MarkGroup(mark, findScaledXY(gateMark.getSingleMark1()), findScaledXY(gateMark.getSingleMark2()));
group.getChildren().add(markGroup);
//group.getChildren().add(markGroup);
raceObjects.add(markGroup);
} else {
// canvasLocation = findScaledXY(mark);
// mark.setX((int) canvasLocation.getX());
// mark.setY((int) canvasLocation.getY());
markGroup = new MarkGroup(mark, findScaledXY(mark));
group.getChildren().add(markGroup);
raceObjects.add(markGroup);
//group.getChildren().add(markGroup);
}
processed.add(mark);
}
@@ -5,6 +5,7 @@ import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.CheckBox;
+1
View File
@@ -140,4 +140,5 @@ public class Boat {
public int getId() {
return id;
}
}
+68 -99
View File
@@ -2,16 +2,12 @@ package seng302.models;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon;
import javafx.scene.text.Text;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import java.util.ArrayList;
import java.util.List;
import seng302.models.parsers.StreamParser;
/**
* Created by CJIRWIN on 25/04/2017.
@@ -25,23 +21,23 @@ public class BoatGroup extends RaceObject{
private static final double VELOCITY_WAKE_RATIO = 2d;
private static final double BOAT_HEIGHT = 15d;
private static final double BOAT_WIDTH = 10d;
private static final int LINE_INTERVAL = 120;
//Time between sections of race - Should be changed to 200 for actual program.
private static final int LINE_INTERVAL = 180;
private static double expectedUpdateInterval = 200;
private static int WAKE_FRAME_INTERVAL = 30;
private double framesForNewLine = LINE_INTERVAL;
private double framesForNewLine = 0;
private boolean destinationSet;
private Point2D lastPoint;
private int wakeGenerationDelay;
private Boat boat;
private int wakeCounter = WAKE_FRAME_INTERVAL;
private List<Wake> wakes = new ArrayList<>();
//private List<Line> lines = new ArrayList<>();
private Group lines = new Group();
private Group lineGroup = new Group();
private Group wakeGroup = new Group();
private Polygon boatPoly;
// private Polygon wakePoly;
private Polygon wakePoly;
private Text teamNameObject;
private Text velocityObject;
private Wake2 wake;
private Wake wake;
public BoatGroup (Boat boat, Color color){
this.boat = boat;
@@ -56,16 +52,6 @@ public class BoatGroup extends RaceObject{
private void initChildren (Color color, double... points) {
boatPoly = new Polygon(points);
boatPoly.setFill(color);
// boatPoly.setLayoutX(0);
// boatPoly.setLayoutY(0);
// boatPoly.relocate(boatPoly.getLayoutX(), boatPoly.getLayoutY());
//
// wakePoly = new Polygon(
// 5.0,0.0,
// 10.0, boat.getVelocity() * VELOCITY_WAKE_RATIO,
// 0.0, boat.getVelocity() * VELOCITY_WAKE_RATIO
// );
// wakePoly.setFill(Color.DARKBLUE);
teamNameObject = new Text(boat.getShortName());
velocityObject = new Text(String.valueOf(boat.getVelocity()));
@@ -77,10 +63,11 @@ public class BoatGroup extends RaceObject{
velocityObject.setX(VELOCITY_X_OFFSET);
velocityObject.setY(VELOCITY_Y_OFFSET);
velocityObject.relocate(velocityObject.getX(), velocityObject.getY());
destinationSet = false;
wake = new Wake2(0, 0, 0);
// super.getChildren().addAll(wakePoly, boatPoly, teamNameObject, velocityObject);
super.getChildren().addAll(lines, wake, teamNameObject, velocityObject, boatPoly);
wake = new Wake(0, 0);
wakeGenerationDelay = wake.numWakes;
super.getChildren().addAll(teamNameObject, velocityObject, boatPoly);
}
private void initChildren (Color color) {
@@ -102,9 +89,9 @@ public class BoatGroup extends RaceObject{
teamNameObject.setLayoutY(teamNameObject.getLayoutY() + dy);
velocityObject.setLayoutX(velocityObject.getLayoutX() + dx);
velocityObject.setLayoutY(velocityObject.getLayoutY() + dy);
// wakePoly.setLayoutX(wakePoly.getLayoutX() + dx);
// wakePoly.setLayoutY(wakePoly.getLayoutY() + dy);
rotateTo(currentRotation);
wake.setLayoutX(wake.getLayoutX() + dx);
wake.setLayoutY(wake.getLayoutY() + dy);
rotateTo(rotation + currentRotation);
}
/**
@@ -124,55 +111,19 @@ public class BoatGroup extends RaceObject{
teamNameObject.setLayoutY(y);
velocityObject.setLayoutX(x);
velocityObject.setLayoutY(y);
wake.moveTo(x, y, currentRotation);
// wakePoly.setLayoutX(x);
// wakePoly.setLayoutY(y);
wake.setLayoutX(x + BOAT_WIDTH / 2);
wake.setLayoutY(y);
wake.rotate(currentRotation);
}
public void updatePosition (long timeInterval) {
double dx = pixelVelocityX * timeInterval;
double dy = pixelVelocityY * timeInterval;
double rotation = 0d;
if (rotationalGoal > currentRotation && rotationalVelocity > 0) {
rotation = rotationalVelocity * timeInterval;
} else if (rotationalGoal < currentRotation && rotationalVelocity < 0) {
rotation = rotationalVelocity * timeInterval;
}
moveGroupBy(dx, dy, rotation);
// if (super.getChildren().size() > 3) {
// for (Node wake : super.getChildren().subList(4, super.getChildren().size())) {
// if (!((Wake) wake).updatePosition(timeInterval))
// super.getChildren().remove(wake);
// }
// }
// for (Wake wake : wakes) {
// if (wake.updatePosition(timeInterval)) {
// super.getChildren().remove(wake);
// }
// }
// if (wakeCounter-- == 0) {
//// if (boat.getShortName().equals("BAR"))
//// System.out.println("thinking");
// wakeCounter = WAKE_FRAME_INTERVAL;
// if (pixelVelocityX > 0 && pixelVelocityY > 0) {
//// super.getChildren().add(
//// new Wake(
//// super.getLayoutX() + BOAT_HEIGHT, super.getLayoutY() + BOAT_HEIGHT, pixelVelocityX, pixelVelocityY
//// )
//// );
// Wake wake = new Wake(
// boatPoly.getLayoutX(),
// boatPoly.getLayoutY(),
// pixelVelocityX,
// pixelVelocityY, rotation);
//// wake.getTransforms().clear();
//// wake.getTransforms().add(new Rotate(rotation, 0, 0));
// super.getChildren().add(wake);
// wakes.add(wake);
// }
//
// }
if (framesForNewLine == 0) {
if (framesForNewLine-- == 0) {
framesForNewLine = LINE_INTERVAL;
if (lastPoint != null) {
Line l = new Line(
@@ -183,28 +134,38 @@ public class BoatGroup extends RaceObject{
);
l.getStrokeDashArray().setAll(4d, 6d);
l.setStroke(boatPoly.getFill());
//lines.add(l);
lines.getChildren().add(l);
lineGroup.getChildren().add(l);
}
lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY());
if (destinationSet){
lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY());
}
if (lineGroup.getChildren().size() > 100)
lineGroup.getChildren().remove(0);
}
framesForNewLine -= 1;
wake.updatePosition(timeInterval);
}
public void setDestination (double newXValue, double newYValue, double rotation, int... raceIds) {
//System.out.println("MADE IT");
destinationSet = true;
boat.setVelocity(StreamParser.boatSpeeds.get((long)boat.getId()));
if (hasRaceId(raceIds)) {
this.pixelVelocityX = (newXValue - boatPoly.getLayoutX()) / expectedUpdateInterval;
this.pixelVelocityY = (newYValue - boatPoly.getLayoutY()) / expectedUpdateInterval;
this.rotationalGoal = rotation;
calculateRotationalVelocity();
rotateTo(rotation);
wake.setRotationalVelocity(rotationalVelocity);
if (wakeGenerationDelay > 0) {
wake.rotate(rotationalGoal);
wakeGenerationDelay--;
} else {
wake.setRotationalVelocity(rotationalVelocity);
}
}
}
public void setDestination (double newXValue, double newYValue, int... raceIDs) {
destinationSet = true;
if (hasRaceId(raceIDs)) {
double rotation = Math.abs(
Math.toDegrees(
@@ -213,41 +174,43 @@ public class BoatGroup extends RaceObject{
)
)
);
// if (boatPoly.getLayoutY() >= newYValue && boatPoly.getLayoutX() <= newXValue)
// rotation = 90 - rotation;
// else if (boatPoly.getLayoutY() < newYValue && boatPoly.getLayoutX() <= newXValue)
// rotation = 90 + rotation;
// else if (boatPoly.getLayoutY() >= newYValue && boatPoly.getLayoutX() > newXValue)
// rotation = 270 + rotation;
// else
// rotation = 270 - rotation;
setDestination(newXValue, newYValue, rotation, raceIDs);
}
}
public void rotateTo (double rotation) {
//if(rotation != 0) {
//rotationalGoal = rotation;
currentRotation = rotation;
boatPoly.getTransforms().clear();
boatPoly.getTransforms().add(new Rotate(rotation, BOAT_WIDTH / 2, 0));
//}
// wakePoly.getTransforms().clear();
// wakePoly.getTransforms().add(new Rotate(rotation, 0, 0));
void resizeWake(){
velocityObject.setText(String.valueOf(boat.getVelocity()));
super.getChildren().remove(wakePoly);
wakePoly = new Polygon(
5.0,0.0,
10.0, boat.getVelocity() * VELOCITY_WAKE_RATIO,
0.0, boat.getVelocity() * VELOCITY_WAKE_RATIO
);
wakePoly.setLayoutX(boatPoly.getLayoutX());
wakePoly.setLayoutY(boatPoly.getLayoutY());
wakePoly.setFill(Color.DARKBLUE);
super.getChildren().add(wakePoly);
}
public void rotateTo (double rotation) {
currentRotation = rotation;
boatPoly.getTransforms().clear();
boatPoly.getTransforms().add(new Rotate(rotation, BOAT_WIDTH / 2, 0));
}
public void forceRotation () {
rotateTo (rotationalGoal);
wake.rotate(rotationalGoal);
}
public void toggleAnnotations () {
teamNameObject.setVisible(!teamNameObject.isVisible());
velocityObject.setVisible(!velocityObject.isVisible());
for (Wake wake : wakes) {
wake.setVisible(!wake.isVisible());
}
lines.setVisible(!lines.isVisible());
lineGroup.setVisible(!lineGroup.isVisible());
wake.setVisible(!wake.isVisible());
}
public Boat getBoat() {
@@ -265,4 +228,10 @@ public class BoatGroup extends RaceObject{
public int[] getRaceIds () {
return new int[] {boat.getId()};
}
public Group getLowPriorityAnnotations () {
Group group = new Group();
group.getChildren().addAll(wake, lineGroup);
return group;
}
}
+11 -6
View File
@@ -4,11 +4,12 @@ import javafx.geometry.Point2D;
import javafx.scene.Group;
/**
* Created by CJIRWIN on 26/04/2017.
* RaceObject defines the behaviour that animated objects whose position is updated from a yacht race data stream must
* adhere to.
*/
public abstract class RaceObject extends Group {
//Time between sections of race - Should be changed to 200 for actual program.
//Time between sections of race
protected static double expectedUpdateInterval = 200;
protected double rotationalGoal;
@@ -17,10 +18,6 @@ public abstract class RaceObject extends Group {
protected double pixelVelocityX;
protected double pixelVelocityY;
public boolean isSamePos (Point2D point) {
return point.getX() == super.getLayoutX() && point.getY() == super.getLayoutY();
}
public Point2D getPosition () {
return new Point2D(super.getLayoutX(), getLayoutY());
}
@@ -45,6 +42,14 @@ public abstract class RaceObject extends Group {
}
}
/**
* Sets the destination of everything within the RaceObject that has an ID in the array raceIds. The destination is
* set to the co-ordinates (x, y) with the given rotation.
* @param x
* @param y
* @param rotation
* @param raceIds
*/
public abstract void setDestination (double x, double y, double rotation, int... raceIds);
public abstract void setDestination (double x, double y, int... raceIds);
+79 -36
View File
@@ -1,51 +1,94 @@
package seng302.models;
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
/**
* Created by CJIRWIN on 27/04/2017.
* By default wake is a group containing 5 arcs. Each arc starts from the same point. Each arc is larger and more
* transparent than the last. On calling updatePositions() arcs rotate at velocities given by setRotationalVelocity().
* The larger and more transparent an arc is the longer the delay before it rotates at the latest velocity. It is
* assumed that rotationalVelocities() are set regularly as wakes do not stop rotating and an array of velocities needs
* to be populated for the class to work as expected.
*/
class Wake extends Arc {
class Wake extends Group {
private final double OPACITY_INCREASE = -0.10;
private final double RADIUS_INCREASE = 10;
final int numWakes = 5;
private double[] velocities = new double[numWakes * 3];
private Arc[] arcs = new Arc[numWakes];
private double[] rotations = new double[numWakes];
private int velocitiesIndex = 0;
private static int VELOCITY_SCALE_FACTOR = 3;
private static int MAX_LIFESPAN = 210;
private static double LIFESPAN_PER_FRAME = 1.0 / MAX_LIFESPAN;
//private static double LENGTH_PER_FRAME = 120 / MAX_LIFESPAN;
private static double LENGTH_PER_FRAME = 0.25;
private double velocityX;
private double velocityY;
private double opacity;
private int lifespan = MAX_LIFESPAN;
Wake (double startingX, double startingY, double velocityX, double velocityY, double rotation) {
super(startingX, startingY, 20, 30, 180, 0);
//super.setFill(Color.BLUE);
super.setStroke(Color.DEEPSKYBLUE);
super.setType(ArcType.OPEN);
super.setFill(new Color(0, 0, 0 ,0));
super.setStrokeWidth(2.0);
super.getTransforms().add(new Rotate(rotation, 5, -15));
// this.velocityX = -velocityX;
// this.velocityY = -velocityY;
this.velocityX = 0;
this.velocityY = 0;
/**
* Create a wake at the given location.
* @param startingX x location where the tip of wake arcs will be.
* @param startingY y location where the tip of wake arcs will be.
*/
Wake(double startingX, double startingY) {
super.setLayoutX(startingX);
super.setLayoutY(startingY);
Arc arc;
for (int i = 0; i < numWakes; i++) {
arc = new Arc(
0,
0,
30 + RADIUS_INCREASE * i,
30 + RADIUS_INCREASE * i,
-110,
40
);
arc.setFill(new Color(0.18, 0.7, 1.0, 0.50 + OPACITY_INCREASE * i));
arc.setType(ArcType.ROUND);
arcs[i] = arc;
}
super.getChildren().addAll(arcs);
}
boolean updatePosition (double timeInterval) {
lifespan--;
//super.setOpacity(LIFESPAN_PER_FRAME * lifespan * super.getOpacity());
//opacity = LIFESPAN_PER_FRAME * lifespan * opacity;
//super.setFill(new Color(0.0f, 0.0f, 1.0f, opacity));
super.setLayoutX(super.getLayoutX() + velocityX * timeInterval);
super.setLayoutY(super.getLayoutY() + velocityY * timeInterval);
super.setStartAngle(super.getStartAngle() - LENGTH_PER_FRAME);
super.setLength(super.getLength() + LENGTH_PER_FRAME * 2);
return lifespan < 0;
/**
* Sets the rotationalVelocity of each arc. Each arc is 3 velocities behind the next smallest arc. The smallest uses
* the latest given velocity.
* @param rotationalVelocity The rotationalVelocity the wake should move at.
*/
void setRotationalVelocity (double rotationalVelocity) {
velocitiesIndex = (velocitiesIndex + 1) % 14;
velocities[velocitiesIndex] = rotationalVelocity;
}
/**
* 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) {
int temp = velocitiesIndex;
for (int i = 0; i < arcs.length; i++) {
//temp = ((temp + 3) % 14);
rotations[i] = rotations[i] + velocities[temp] * timeInterval;
int j = 0;
//I have no idea why I have to do this to make it work.
//I will buy you a block of chocolate if you can tell me why.
switch (i) {
case 4:
j = 1; break;
case 3:
j = 2; break;
case 2:
j = 3; break;
case 1:
j = 4; break;
}
arcs[j].getTransforms().setAll(new Rotate(rotations[i]));
temp = ((temp + 3) % 14);
}
}
void rotate (double rotation) {
for (int i = 0; i < arcs.length; i++) {
rotations[i] = rotation;
arcs[i].getTransforms().setAll(new Rotate(rotation));
}
}
}
-86
View File
@@ -1,86 +0,0 @@
package seng302.models;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.transform.Rotate;
/**
* Created by cir27 on 28/04/17.
*/
class Wake2 extends Arc {
private final double OPACITY_INCREASE = -0.2;
private final double RADIUS_INCREASE = 10;
private final int DELAY = 60;
private double opacity;
private double rotationalVelocity;
private Wake2 subWake;
private boolean hasSubWake = false;
private int delayCount = DELAY;
Wake2 (double startingX, double startingY, double rotationalVelocity) {
super(startingX, startingY, 30, 30, 150, 60);
opacity = 1.0;
super.setFill(new Color(0.0, 0.0, 0.0, opacity));
subWake = new Wake2(
startingX,
startingY,
super.getRadiusX() + RADIUS_INCREASE,
rotationalVelocity,
opacity + OPACITY_INCREASE
);
hasSubWake = true;
this.rotationalVelocity = rotationalVelocity;
}
Wake2 (double startingX, double startingY, double radius, double rotationalVelocity, double opacity) {
super(startingX, startingY, radius, radius, 150, 60);
super.setFill(new Color(0.0, 0.0, 0.0, opacity));
this.opacity = opacity;
if (!(opacity < 0)) {
subWake = new Wake2(
startingX,
startingY,
super.getRadiusX() + RADIUS_INCREASE,
rotationalVelocity,
opacity + OPACITY_INCREASE
);
hasSubWake = true;
}
this.rotationalVelocity = rotationalVelocity;
}
void setRotationalVelocity (double rotationalVelocity) {
this.rotationalVelocity = rotationalVelocity;
delayCount = DELAY;
}
void updatePosition (long timeInterval) {
if (delayCount-- == 0)
subWake.setRotationalVelocity(rotationalVelocity);
super.getTransforms().clear();
super.getTransforms().add(
new Rotate(
rotationalVelocity * timeInterval,
super.getCenterX(),
super.getCenterY()
)
);
if(hasSubWake)
subWake.updatePosition(timeInterval);
}
void moveTo (double x, double y, double rotation) {
super.setLayoutX(x);
super.setLayoutY(y);
super.getTransforms().clear();
super.getTransforms().add(
new Rotate(
rotation,
super.getCenterX(),
super.getCenterY()
)
);
if(hasSubWake)
subWake.moveTo(x, y, rotation);
}
}
@@ -1,12 +1,10 @@
package seng302.models.parsers;
import javafx.geometry.Point2D;
import javafx.geometry.Point3D;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import sun.awt.UNIXToolkit;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -14,19 +12,24 @@ import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* The purpose of this class is to take in the stream of divided packets so they can be read
* and parsed in by turning the byte arrays into useful data. There are two public static hashmaps
* that are threadsafe so the visualiser can always access the latest speed and position avaible
* Created by kre39 on 23/04/17.
*/
public class StreamParser extends Thread{
public static ConcurrentHashMap<Long,Point3D> boatPositions = new ConcurrentHashMap<>();
private static ArrayList<Long> boat_IDS = new ArrayList<>();
private String threadName;
public static ConcurrentHashMap<Long,Double> boatSpeeds = new ConcurrentHashMap<>();
private String threadName;
private Thread t;
private static boolean raceStarted = false;
@@ -34,7 +37,11 @@ public class StreamParser extends Thread{
this.threadName = threadName;
}
public void run(){
/**
* Used to within threading so when the stream parser thread runs, it will keep looking for a packet to
* process until it is unable to find anymore packets
*/
public void run(){
try {
System.out.println("START OF STREAM");
while (StreamReceiver.packetBuffer == null || StreamReceiver.packetBuffer.size() < 1) {
@@ -54,6 +61,9 @@ public class StreamParser extends Thread{
}
}
/**
* Used to start the stream parser thread when multithreading
*/
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
@@ -62,7 +72,7 @@ public class StreamParser extends Thread{
}
}
static void parsePacket(StreamPacket packet) {
private static void parsePacket(StreamPacket packet) {
switch (packet.getType()){
case HEARTBEAT:
extractHeartBeat(packet);
@@ -106,12 +116,20 @@ public class StreamParser extends Thread{
}
}
/**
* Extracts the seq num used in the heartbeat packet
* @param packet Packet parsed in to use the payload
*/
private static void extractHeartBeat(StreamPacket packet) {
long heartbeat = bytesToLong(packet.getPayload());
// System.out.println("Heartbeat: " + heartbeat);
}
/**
* Extracts the useful race status data from race status type packets. This method will also print to the
* console the current state of the race (if it has started/finished or is about to start), along side
* this it'll also display the amount of time since the race has started or time till it starts
* @param packet Packet parsed in to use the payload
*/
private static void extractRaceStatus(StreamPacket packet){
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
@@ -155,6 +173,10 @@ public class StreamParser extends Thread{
}
}
/**
* Used to extract the messages passed through with the display message packet
* @param packet Packet parsed in to use the payload
*/
private static void extractDisplayMessage(StreamPacket packet){
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
@@ -169,7 +191,11 @@ public class StreamParser extends Thread{
}
}
static void extractXmlMessage(StreamPacket packet){
/**
* Used to read in the xml data. Will call the specific methods to create the course and boats
* @param packet Packet parsed in to use the payload
*/
private static void extractXmlMessage(StreamPacket packet){
byte[] payload = packet.getPayload();
String xmlMessage = "";
@@ -197,16 +223,17 @@ public class StreamParser extends Thread{
db = dbf.newDocumentBuilder();
Document doc = db.parse(new InputSource(new StringReader(xmlMessage)));
// TODO: 25/04/17 ajm412: Check that the object matches expected structure and return Document object.
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
} catch (ParserConfigurationException | IOException | SAXException e) {
e.printStackTrace();
}
}
/**
* Extracts the race start status from the packet, currently is unused within the app but
* is here for potential future use
* @param packet Packet parsed in to use the payload
*/
private static void extractRaceStartStatus(StreamPacket packet){
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
@@ -216,6 +243,11 @@ public class StreamParser extends Thread{
int notificationType = payload[19];
}
/**
* When a yacht event occurs this will parse the byte array to retrieve the necessary info,
* currently unused
* @param packet Packet parsed in to use the payload
*/
private static void extractYachtEventCode(StreamPacket packet){
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
@@ -226,6 +258,11 @@ public class StreamParser extends Thread{
int eventId = payload[21];
}
/**
* When a yacht action occurs this will parse the parse the byte array to retrieve the necessary info,
* currently unused
* @param packet Packet parsed in to use the payload
*/
private static void extractYachtActionCode(StreamPacket packet){
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
@@ -236,6 +273,10 @@ public class StreamParser extends Thread{
// System.out.println("eventId = " + eventId);
}
/**
* Strips the message from the chatter text type packets, currently the message is unused
* @param packet Packet parsed in to use the payload
*/
private static void extractChatterText(StreamPacket packet){
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
@@ -244,8 +285,12 @@ public class StreamParser extends Thread{
String message = new String(Arrays.copyOfRange(payload,3,3 + length));
}
static void extractBoatLocation(StreamPacket packet){
/**
* Used to breakdown the boatlocation packets so the boat coordinates, id and groundspeed are all used
* All the other extra data is still being read and translated however is unused.
* @param packet Packet parsed in to use the payload
*/
private static void extractBoatLocation(StreamPacket packet){
byte[] payload = packet.getPayload();
byte deviceType = payload[15];
byte[] seqBytes = Arrays.copyOfRange(payload,11,15);
@@ -253,6 +298,8 @@ public class StreamParser extends Thread{
byte[] lonBytes = Arrays.copyOfRange(payload,20,24);
byte[] boatIdBytes = Arrays.copyOfRange(payload,7,11);
byte[] headingBytes = Arrays.copyOfRange(payload,28,30);
byte[] groundSpeedBytes = Arrays.copyOfRange(payload,38,40);
long timeStamp = extractTimeStamp(Arrays.copyOfRange(payload,1,7), 6);
// int boatSeq = ByteBuffer.wrap(seqBytes).getInt();
long seq = bytesToLong(seqBytes);
@@ -260,7 +307,9 @@ public class StreamParser extends Thread{
long lat = bytesToLong(latBytes);
long lon = bytesToLong(lonBytes);
long heading = bytesToLong(headingBytes);
// long speed = extractTimeStamp(speedBytes, 2);
double groundSpeed = bytesToLong(groundSpeedBytes)/1000.0;
short s = (short) ((groundSpeedBytes[1] & 0xFF) << 8 | (groundSpeedBytes[0] & 0xFF));
if ((int)deviceType == 1 || (int)deviceType == 4){
// System.out.println("boatId = " + boatId);
// System.out.println("deviceType = " + (long)deviceType);
@@ -268,13 +317,16 @@ public class StreamParser extends Thread{
//needs to be validated
Point3D point = new Point3D(((180d * (double)lat)/Math.pow(2,31)),((180d *(double)lon)/Math.pow(2,31)),(double)heading);
boatPositions.putIfAbsent(boatId, point);
boatSpeeds.putIfAbsent(boatId, groundSpeed);
boatPositions.replace(boatId, point);
boatSpeeds.replace(boatId, groundSpeed);
// System.out.println("lon = " + ((180d * (double)lon)/Math.pow(2,31)));
// System.out.println("lat = " + ((180d *(double)lat)/Math.pow(2,31)));
}
}
private static void extractMarkRounding(StreamPacket packet){
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
+6 -6
View File
@@ -4,37 +4,37 @@
<team>
<name>Oracle Team USA</name>
<alias>USA</alias>
<velocity>12.9</velocity>
<velocity>0.0</velocity>
<id>102</id>
</team>
<team>
<name>Artemis Racing</name>
<alias>ART</alias>
<velocity>13.1</velocity>
<velocity>0.0</velocity>
<id>101</id>
</team>
<team>
<name>Emirates Team New Zealand</name>
<alias>NZL</alias>
<velocity>15.6</velocity>
<velocity>0.0</velocity>
<id>103</id>
</team>
<team>
<name>Land Rover BAR</name>
<alias>BAR</alias>
<velocity>13.3</velocity>
<velocity>0.0</velocity>
<id>104</id>
</team>
<team>
<name>SoftBank Team Japan</name>
<alias>JAP</alias>
<velocity>14.7</velocity>
<velocity>0.0</velocity>
<id>105</id>
</team>
<team>
<name>Groupama Team France</name>
<alias>FRC</alias>
<velocity>11.4</velocity>
<velocity>0.0</velocity>
<id>106</id>
</team>
</teams>
+11 -8
View File
@@ -1,15 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.shape.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.canvas.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<GridPane prefHeight="1080.0" prefWidth="1920.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 prefHeight="1080.0" prefWidth="1920.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8.0.112" 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" />