From 5fe330bfbb322cc2c20a0a5b722817cde2b21ac8 Mon Sep 17 00:00:00 2001 From: Kusal Ekanayake Date: Mon, 1 May 2017 16:41:58 +1200 Subject: [PATCH] Boat trials and wakes now work with both fast and slow data sets. Instead of fixed, hard coded thresholds and scale factors dynamically changing values that scale with the onscreen movement are used to determine how graphical objects are drawn. #implement #story[816] --- src/main/java/seng302/App.java | 2 +- .../seng302/controllers/CanvasController.java | 1 - src/main/java/seng302/models/BoatGroup.java | 55 +++++++++++++------ src/main/java/seng302/models/Wake.java | 29 ++++++---- .../java/seng302/models/mark/MarkGroup.java | 8 +-- 5 files changed, 61 insertions(+), 34 deletions(-) diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index a66be5a9..dc6de280 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -26,7 +26,7 @@ public class App extends Application sr = new StreamReceiver("localhost", 8085, "TestThread1"); } else{ - //StreamReceiver sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941,"TestThread1"); +// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941,"TestThread1"); sr = new StreamReceiver("livedata.americascup.com", 4941, "TestThread1"); } diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index a862b607..84a58663 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -125,7 +125,6 @@ public class CanvasController { } // TODO: 1/05/17 cir27 - Make the RaceObjects update on the actual delay. elapsedNanos = 1000 / 60; - System.out.println("elapsedNanos = " + elapsedNanos); for (RaceObject raceObject : raceObjects) { raceObject.updatePosition(elapsedNanos); for (int id : raceObject.getRaceIds()) { diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index 99fcf0a6..618a3b59 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -22,11 +22,11 @@ public class BoatGroup extends RaceObject{ private static final double VELOCITY_Y_OFFSET = -5d; private static final double BOAT_HEIGHT = 15d; private static final double BOAT_WIDTH = 10d; - private static final int LINE_INTERVAL = 30; - private double framesForNewLine = 0; + private static double expectedUpdateInterval = 200; private boolean destinationSet; private Point2D lastPoint; - private int wakeGenerationDelay; + private int wakeGenerationDelay = 10; + private double distanceTravelled; private Boat boat; private Group lineGroup = new Group(); @@ -81,7 +81,6 @@ public class BoatGroup extends RaceObject{ destinationSet = false; wake = new Wake(0, -BOAT_HEIGHT); - wakeGenerationDelay = wake.numWakes; super.getChildren().addAll(teamNameObject, velocityObject, boatPoly); } @@ -150,10 +149,11 @@ public class BoatGroup extends RaceObject{ double dx = pixelVelocityX * timeInterval; double dy = pixelVelocityY * timeInterval; double rotation = rotationalVelocity * timeInterval; + distanceTravelled += Math.abs(dx) + Math.abs(dy); moveGroupBy(dx, dy, rotation); - - if (framesForNewLine-- == 0) { - framesForNewLine = LINE_INTERVAL; + //Draw a new section of the trail every 20 pixels of movement. + if (distanceTravelled > 20) { + distanceTravelled = 0; if (lastPoint != null) { Line l = new Line( lastPoint.getX(), @@ -161,19 +161,24 @@ public class BoatGroup extends RaceObject{ boatPoly.getLayoutX(), boatPoly.getLayoutY() ); - l.getStrokeDashArray().setAll(4d, 6d); + l.getStrokeDashArray().setAll(3d, 7d); l.setStroke(boatPoly.getFill()); lineGroup.getChildren().add(l); } - if (destinationSet){ + if (destinationSet){ //Only begin drawing after the first destination is set lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); } - if (lineGroup.getChildren().size() > 60) - lineGroup.getChildren().remove(0); } wake.updatePosition(timeInterval); } + /** + * Sets the destination of the boat and the headng it should have once it reaches + * @param newXValue + * @param newYValue + * @param rotation Rotation to move graphics to. + * @param raceIds RaceID of the object to move. + */ public void setDestination (double newXValue, double newYValue, double rotation, int... raceIds) { if (hasRaceId(raceIds)) { destinationSet = true; @@ -187,19 +192,37 @@ public class BoatGroup extends RaceObject{ pixelVelocityX = dx / expectedUpdateInterval; } double dy = newYValue - boatPoly.getLayoutY(); - if ((dy > 0 && pixelVelocityY < 0) || (dy < 0 && pixelVelocityY > 0)) { - pixelVelocityY = 0; - } else { - pixelVelocityY = dy / expectedUpdateInterval; + //Check movement is reasonable. Assumes a 1000 * 1000 canvas + if (Math.abs(dx) > 50 || Math.abs(dy) > 50) { +// System.out.println("dx = " + dx); +// System.out.println("dy = " + dy); + dx = 0; + dy = 0; + moveTo(newXValue, newYValue); } + //Slight delay on changing X/Y direction that could help jitter. Disabled since there was an issue with + //packets that might be causing it. +// if ((dx > 0 && pixelVelocityX < 0) || (dx < 0 && pixelVelocityX > 0)) { +// pixelVelocityX = 0; +// } else { +// pixelVelocityX = dx / expectedUpdateInterval; +// } +// if ((dy > 0 && pixelVelocityY < 0) || (dy < 0 && pixelVelocityY > 0)) { +// pixelVelocityY = 0; +// } else { +// pixelVelocityY = dy / expectedUpdateInterval; +// } + pixelVelocityX = dx / expectedUpdateInterval; + pixelVelocityY = dy / expectedUpdateInterval; rotationalGoal = rotation; calculateRotationalVelocity(); if (wakeGenerationDelay > 0) { wake.rotate(rotationalGoal); wakeGenerationDelay--; } else { - wake.setRotationalVelocity(rotationalVelocity, currentRotation, pixelVelocityX, pixelVelocityY); + wake.setRotationalVelocity(rotationalVelocity, currentRotation, boat.getVelocity()); } + velocityObject.setText(String.format("%.2f m/s", boat.getVelocity())); } } diff --git a/src/main/java/seng302/models/Wake.java b/src/main/java/seng302/models/Wake.java index a064dc7e..d389e8e7 100644 --- a/src/main/java/seng302/models/Wake.java +++ b/src/main/java/seng302/models/Wake.java @@ -15,12 +15,13 @@ import javafx.scene.transform.Rotate; */ class Wake extends Group { - final int numWakes = 5; + private int numWakes = 5; private double[] velocities = new double[13]; private Arc[] arcs = new Arc[numWakes]; private double[] rotations = new double[numWakes]; private int[] velocityIndices = new int[numWakes]; private double sum = 0; + private static double max; /** * Create a wake at the given location. @@ -46,15 +47,19 @@ class Wake extends Group { * 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. + * @param rotationGoal Where the wake will rotate to if the wake is calculated to be on a straight section. This is + * used to prevent desynchronisation with the Boat polygon. + * @param velocity The real world velocity of the boat in m/s. */ - void setRotationalVelocity (double rotationalVelocity, double rotationGoal, double velocityX, double velocityY) { - if (Math.abs(rotationalVelocity) > 0.5) { - rotationalVelocity = 0; - } + void setRotationalVelocity (double rotationalVelocity, double rotationGoal, double velocity) { +// if (Math.abs(rotationalVelocity) > 0.5) { +// rotationalVelocity = 0; +// } sum -= Math.abs(velocities[(velocityIndices[0] + 10) % 13]); sum += Math.abs(rotationalVelocity); - System.out.println("sum = " + sum); - if (sum < 0.9) +// System.out.println("sum = " + sum); + max = Math.max(max, rotationalVelocity); + if (sum < max) rotate (rotationGoal); //In relatively straight segments the wake snaps to match the boats current position. //This stops the wake from eventually becoming out of sync with the boat. @@ -65,14 +70,14 @@ class Wake extends Group { for (int i = 1; i < numWakes; i++) velocityIndices[i] = (velocityIndices[0] + 3 * i) % 13; - //Scale wakes based on velocity. Assumes boats are always moving at a decent pace. - double scaleFactor = Math.abs(Math.log10(Math.abs(velocityX) + Math.abs(velocityY))); - double baseRad = 25; + //Scale wakes based on velocity. + double baseRad = 20; + double rad; for (Arc arc :arcs) { - double rad = Math.min(baseRad + 5 * scaleFactor, baseRad + 15); + rad = baseRad + velocity; arc.setRadiusX(rad); arc.setRadiusY(rad); - baseRad += 10; + baseRad += 5 + (velocity / 2); } } diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java index 6448f2e2..b0ef4768 100644 --- a/src/main/java/seng302/models/mark/MarkGroup.java +++ b/src/main/java/seng302/models/mark/MarkGroup.java @@ -142,13 +142,13 @@ public class MarkGroup extends RaceObject { if (nodePixelVelocitiesX[0] > 0 && markCircle.getCenterX() > nodeDestinations[0].getX() || nodePixelVelocitiesX[0] < 0 && markCircle.getCenterX() < nodeDestinations[0].getY()) nodePixelVelocitiesX[0] = 0; - else + else if (nodePixelVelocitiesX[0] != 0) markCircle.setCenterX(markCircle.getCenterX() + nodePixelVelocitiesX[0] * timeInterval); if (nodePixelVelocitiesY[0] > 0 && markCircle.getCenterY() > nodeDestinations[0].getY() || nodePixelVelocitiesY[0] < 0 && markCircle.getCenterY() < nodeDestinations[0].getY()) nodePixelVelocitiesY[0] = 0; - else + else if (nodePixelVelocitiesY[0] != 0) markCircle.setCenterY(markCircle.getCenterY() + nodePixelVelocitiesY[0] * timeInterval); if (mainMark.getMarkType() != MarkType.SINGLE_MARK) { @@ -162,13 +162,13 @@ public class MarkGroup extends RaceObject { if (nodePixelVelocitiesX[1] > 0 && markCircle.getCenterX() >= nodeDestinations[1].getX() || nodePixelVelocitiesX[1] < 0 && markCircle.getCenterX() <= nodeDestinations[1].getX()) nodePixelVelocitiesX[1] = 0; - else + else if (nodePixelVelocitiesX[1] != 0) markCircle.setCenterX(markCircle.getCenterX() + nodePixelVelocitiesX[1] * timeInterval); if (nodePixelVelocitiesY[1] > 0 && markCircle.getCenterY() > nodeDestinations[1].getY() || nodePixelVelocitiesY[1] < 0 && markCircle.getCenterY() < nodeDestinations[1].getY()) nodePixelVelocitiesY[1] = 0; - else + else if (nodePixelVelocitiesY[1] != 0) markCircle.setCenterY(markCircle.getCenterY() + nodePixelVelocitiesY[1] * timeInterval); line.setEndX(markCircle.getCenterX()); line.setEndY(markCircle.getCenterY());