diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 37ef3193..dbce6849 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -22,6 +22,7 @@ import seng302.models.parsers.StreamParser; import seng302.models.parsers.StreamReceiver; import java.sql.Time; +import java.text.DecimalFormat; import java.util.*; /** @@ -59,6 +60,13 @@ public class CanvasController { private double metersToPixels; private List 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 { @@ -138,6 +146,8 @@ public class CanvasController { timer = new AnimationTimer() { + + private int countdown = 60; private int[] currentRaceMarker = {1, 1, 1, 1, 1, 1}; List marks = raceViewController.getRace().getCourse(); @@ -150,6 +160,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,8 +187,6 @@ 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); @@ -285,10 +307,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); } } @@ -307,7 +334,7 @@ public class CanvasController { for (Boat boat : boats) { BoatGroup boatGroup = new BoatGroup(boat, Colors.getColor()); boatGroup.moveTo(startingX, startingY, 0d); - boatGroup.setDestination(firstMarkX, firstMarkY); +// boatGroup.setDestination(firstMarkX, firstMarkY); boatGroup.forceRotation(); group.getChildren().add(boatGroup); raceObjects.add(boatGroup); diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index e781c8d3..214cf02f 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -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; diff --git a/src/main/java/seng302/models/Boat.java b/src/main/java/seng302/models/Boat.java index d275091f..04ef14ab 100644 --- a/src/main/java/seng302/models/Boat.java +++ b/src/main/java/seng302/models/Boat.java @@ -140,4 +140,5 @@ public class Boat { public int getId() { return id; } + } \ No newline at end of file diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index 44f5aa1f..46887e5a 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -8,6 +8,7 @@ import javafx.scene.shape.Polygon; import javafx.scene.text.Text; import javafx.scene.transform.Rotate; import javafx.scene.transform.Translate; +import seng302.models.parsers.StreamParser; import java.util.ArrayList; import java.util.List; @@ -28,6 +29,7 @@ public class BoatGroup extends RaceObject{ private static double expectedUpdateInterval = 200; private static int WAKE_FRAME_INTERVAL = 30; private double framesForNewLine = 0; + private boolean destinationSet; private Point2D lastPoint; private Boat boat; @@ -35,7 +37,7 @@ public class BoatGroup extends RaceObject{ private List wakes = new ArrayList<>(); private List lines = new ArrayList<>(); private Polygon boatPoly; -// private Polygon wakePoly; + private Polygon wakePoly; private Text teamNameObject; private Text velocityObject; @@ -56,12 +58,12 @@ public class BoatGroup extends RaceObject{ // 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); + 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())); @@ -73,9 +75,8 @@ public class BoatGroup extends RaceObject{ velocityObject.setX(VELOCITY_X_OFFSET); velocityObject.setY(VELOCITY_Y_OFFSET); velocityObject.relocate(velocityObject.getX(), velocityObject.getY()); - -// super.getChildren().addAll(wakePoly, boatPoly, teamNameObject, velocityObject); - super.getChildren().addAll(teamNameObject, velocityObject, boatPoly); + destinationSet = false; + super.getChildren().addAll(wakePoly, boatPoly, teamNameObject, velocityObject); } private void initChildren (Color color) { @@ -97,8 +98,8 @@ 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); + wakePoly.setLayoutX(wakePoly.getLayoutX() + dx); + wakePoly.setLayoutY(wakePoly.getLayoutY() + dy); rotateTo(currentRotation); } @@ -119,8 +120,8 @@ public class BoatGroup extends RaceObject{ teamNameObject.setLayoutY(y); velocityObject.setLayoutX(x); velocityObject.setLayoutY(y); -// wakePoly.setLayoutX(x); -// wakePoly.setLayoutY(y); + wakePoly.setLayoutX(x); + wakePoly.setLayoutY(y); } public void updatePosition (double timeInterval) { @@ -158,9 +159,8 @@ public class BoatGroup extends RaceObject{ boatPoly.getLayoutX(), boatPoly.getLayoutY(), pixelVelocityX, - pixelVelocityY, rotation); -// wake.getTransforms().clear(); -// wake.getTransforms().add(new Rotate(rotation, 0, 0)); + pixelVelocityY, + rotation); super.getChildren().add(wake); wakes.add(wake); } @@ -174,13 +174,18 @@ public class BoatGroup extends RaceObject{ lines.add(l); super.getChildren().add(l); } - lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); + if (destinationSet){ + lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); + } } framesForNewLine -= 1; } 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())); + resizeWake(); if (hasRaceId(raceIds)) { this.pixelVelocityX = (newXValue - boatPoly.getLayoutX()) / expectedUpdateInterval; this.pixelVelocityY = (newYValue - boatPoly.getLayoutY()) / expectedUpdateInterval; @@ -191,6 +196,8 @@ public class BoatGroup extends RaceObject{ } public void setDestination (double newXValue, double newYValue, int... raceIDs) { + destinationSet = true; + if (hasRaceId(raceIDs)) { double rotation = Math.abs( Math.toDegrees( @@ -212,16 +219,34 @@ public class BoatGroup extends RaceObject{ } } + 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) { if(rotation != 0) { rotationalGoal = rotation; boatPoly.getTransforms().clear(); boatPoly.getTransforms().add(new Rotate(rotation, BOAT_WIDTH / 2, 0)); + wakePoly.getTransforms().clear(); + wakePoly.getTransforms().add(new Translate(0, BOAT_HEIGHT)); + wakePoly.getTransforms().add(new Rotate(rotation, BOAT_WIDTH/2, -BOAT_HEIGHT)); } -// wakePoly.getTransforms().clear(); -// wakePoly.getTransforms().add(new Rotate(rotation, 0, 0)); } + + public void forceRotation () { rotateTo (rotationalGoal); } diff --git a/src/main/java/seng302/models/Wake.java b/src/main/java/seng302/models/Wake.java index 92b2d30d..09b037ba 100644 --- a/src/main/java/seng302/models/Wake.java +++ b/src/main/java/seng302/models/Wake.java @@ -29,7 +29,7 @@ class Wake extends Arc { super.setType(ArcType.OPEN); super.setFill(new Color(0, 0, 0 ,0)); super.setStrokeWidth(2.0); - super.getTransforms().add(new Rotate(rotation, 5, -15)); + super.getTransforms().add(new Rotate(rotation - 270, startingX + 20, startingY + 20)); // this.velocityX = -velocityX; // this.velocityY = -velocityY; this.velocityX = 0; diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/parsers/StreamParser.java index f49e4d31..c917cd3c 100644 --- a/src/main/java/seng302/models/parsers/StreamParser.java +++ b/src/main/java/seng302/models/parsers/StreamParser.java @@ -14,6 +14,8 @@ 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.*; @@ -25,6 +27,7 @@ import java.util.concurrent.ConcurrentHashMap; public class StreamParser extends Thread{ public static ConcurrentHashMap boatPositions = new ConcurrentHashMap<>(); + public static ConcurrentHashMap boatSpeeds = new ConcurrentHashMap<>(); private static ArrayList boat_IDS = new ArrayList<>(); private String threadName; private Thread t; @@ -253,6 +256,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[] speedBytes = 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 +265,13 @@ public class StreamParser extends Thread{ long lat = bytesToLong(latBytes); long lon = bytesToLong(lonBytes); long heading = bytesToLong(headingBytes); - +// long speed = extractTimeStamp(speedBytes, 2); + ByteBuffer bb = ByteBuffer.allocate(2); + bb.order(ByteOrder.LITTLE_ENDIAN); + bb.put(speedBytes[0]); + bb.put(speedBytes[1]); + double speed = bb.getShort(0)/1000.0; + short s = (short) ((speedBytes[1] & 0xFF) << 8 | (speedBytes[0] & 0xFF)); if ((int)deviceType == 1 || (int)deviceType == 4){ // System.out.println("boatId = " + boatId); // System.out.println("deviceType = " + (long)deviceType); @@ -268,13 +279,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, speed); boatPositions.replace(boatId, point); + boatSpeeds.replace(boatId, speed); // 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]; diff --git a/src/main/resources/config/teams.xml b/src/main/resources/config/teams.xml index f8d1e58d..0ac01cac 100644 --- a/src/main/resources/config/teams.xml +++ b/src/main/resources/config/teams.xml @@ -4,37 +4,37 @@ Oracle Team USA USA - 12.9 + 0.0 102 Artemis Racing ART - 13.1 + 0.0 101 Emirates Team New Zealand NZL - 15.6 + 0.0 103 Land Rover BAR BAR - 13.3 + 0.0 104 SoftBank Team Japan JAP - 14.7 + 0.0 105 Groupama Team France FRC - 11.4 + 0.0 106 \ No newline at end of file diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index f7fcbfeb..2cae1326 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -1,15 +1,18 @@ - - - - - - - + + + + + + + + + + - +