From 765f27f9877e1a9936084e2c7b655f3a486e346a Mon Sep 17 00:00:00 2001 From: Calum Date: Fri, 28 Apr 2017 17:08:08 +1200 Subject: [PATCH 1/5] Starting new wake implementation. --- src/main/java/seng302/models/BoatGroup.java | 97 +++++++++++-------- src/main/java/seng302/models/RaceObject.java | 2 +- src/main/java/seng302/models/Wake2.java | 86 ++++++++++++++++ .../java/seng302/models/mark/MarkGroup.java | 2 +- 4 files changed, 143 insertions(+), 44 deletions(-) create mode 100644 src/main/java/seng302/models/Wake2.java diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index 44f5aa1f..dd28b6b5 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -1,6 +1,7 @@ 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; @@ -24,20 +25,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 double expectedUpdateInterval = 200; private static int WAKE_FRAME_INTERVAL = 30; - private double framesForNewLine = 0; + private double framesForNewLine = LINE_INTERVAL; private Point2D lastPoint; private Boat boat; private int wakeCounter = WAKE_FRAME_INTERVAL; private List wakes = new ArrayList<>(); - private List lines = new ArrayList<>(); + //private List lines = new ArrayList<>(); + private Group lines = new Group(); private Polygon boatPoly; // private Polygon wakePoly; private Text teamNameObject; private Text velocityObject; + private Wake2 wake; public BoatGroup (Boat boat, Color color){ this.boat = boat; @@ -74,8 +78,9 @@ public class BoatGroup extends RaceObject{ velocityObject.setY(VELOCITY_Y_OFFSET); velocityObject.relocate(velocityObject.getX(), velocityObject.getY()); + wake = new Wake2(0, 0, 0); // super.getChildren().addAll(wakePoly, boatPoly, teamNameObject, velocityObject); - super.getChildren().addAll(teamNameObject, velocityObject, boatPoly); + super.getChildren().addAll(lines, wake, teamNameObject, velocityObject, boatPoly); } private void initChildren (Color color) { @@ -119,11 +124,12 @@ 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); } - public void updatePosition (double timeInterval) { + public void updatePosition (long timeInterval) { double dx = pixelVelocityX * timeInterval; double dy = pixelVelocityY * timeInterval; double rotation = 0d; @@ -139,44 +145,51 @@ public class BoatGroup extends RaceObject{ // 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); - } - - } +// 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) { - framesForNewLine = 121; + framesForNewLine = LINE_INTERVAL; if (lastPoint != null) { - Line l = new Line(lastPoint.getX(), lastPoint.getY(), boatPoly.getLayoutX(), boatPoly.getLayoutY()); - l.getStrokeDashArray().setAll(4d, 4d); - lines.add(l); - super.getChildren().add(l); + Line l = new Line( + lastPoint.getX(), + lastPoint.getY(), + boatPoly.getLayoutX(), + boatPoly.getLayoutY() + ); + l.getStrokeDashArray().setAll(4d, 6d); + l.setStroke(boatPoly.getFill()); + //lines.add(l); + lines.getChildren().add(l); } lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); } framesForNewLine -= 1; + wake.updatePosition(timeInterval); } public void setDestination (double newXValue, double newYValue, double rotation, int... raceIds) { @@ -187,6 +200,7 @@ public class BoatGroup extends RaceObject{ this.rotationalGoal = rotation; calculateRotationalVelocity(); rotateTo(rotation); + wake.setRotationalVelocity(rotationalVelocity); } } @@ -213,11 +227,12 @@ public class BoatGroup extends RaceObject{ } public void rotateTo (double rotation) { - if(rotation != 0) { - rotationalGoal = 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)); } @@ -232,9 +247,7 @@ public class BoatGroup extends RaceObject{ for (Wake wake : wakes) { wake.setVisible(!wake.isVisible()); } - for (Line line : lines) { - line.setVisible(!line.isVisible()); - } + lines.setVisible(!lines.isVisible()); } public Boat getBoat() { diff --git a/src/main/java/seng302/models/RaceObject.java b/src/main/java/seng302/models/RaceObject.java index a5dcf6f5..9ec22fd5 100644 --- a/src/main/java/seng302/models/RaceObject.java +++ b/src/main/java/seng302/models/RaceObject.java @@ -49,7 +49,7 @@ public abstract class RaceObject extends Group { public abstract void setDestination (double x, double y, int... raceIds); - public abstract void updatePosition (double timeInterval); + public abstract void updatePosition (long timeInterval); public abstract void moveTo (double x, double y, double rotation); diff --git a/src/main/java/seng302/models/Wake2.java b/src/main/java/seng302/models/Wake2.java new file mode 100644 index 00000000..98090a45 --- /dev/null +++ b/src/main/java/seng302/models/Wake2.java @@ -0,0 +1,86 @@ +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); + } +} diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java index eba7da82..caf260fe 100644 --- a/src/main/java/seng302/models/mark/MarkGroup.java +++ b/src/main/java/seng302/models/mark/MarkGroup.java @@ -156,7 +156,7 @@ public class MarkGroup extends RaceObject { super.getTransforms().add(new Rotate(rotation, 0 , 0)); } - public void updatePosition (double timeInterval) { + public void updatePosition (long timeInterval) { double x = pixelVelocityX * timeInterval; double y = pixelVelocityY * timeInterval; double rotation = rotationalVelocity * timeInterval; From 474f0ee4273cd382c7d03160c7f1ad3d671216a7 Mon Sep 17 00:00:00 2001 From: Calum Date: Fri, 28 Apr 2017 23:25:49 +1200 Subject: [PATCH 2/5] 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. --- .../seng302/controllers/CanvasController.java | 59 ++++++- .../controllers/RaceViewController.java | 1 + src/main/java/seng302/models/Boat.java | 1 + src/main/java/seng302/models/BoatGroup.java | 167 +++++++----------- src/main/java/seng302/models/RaceObject.java | 17 +- src/main/java/seng302/models/Wake.java | 115 ++++++++---- src/main/java/seng302/models/Wake2.java | 86 --------- .../seng302/models/parsers/StreamParser.java | 86 +++++++-- src/main/resources/config/teams.xml | 12 +- src/main/resources/views/RaceView.fxml | 19 +- 10 files changed, 296 insertions(+), 267 deletions(-) delete mode 100644 src/main/java/seng302/models/Wake2.java diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 37ef3193..8934a471 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -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 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 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); } 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 dd28b6b5..d7f023b1 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -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 wakes = new ArrayList<>(); - //private List 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; + } } diff --git a/src/main/java/seng302/models/RaceObject.java b/src/main/java/seng302/models/RaceObject.java index 9ec22fd5..450b11b8 100644 --- a/src/main/java/seng302/models/RaceObject.java +++ b/src/main/java/seng302/models/RaceObject.java @@ -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); diff --git a/src/main/java/seng302/models/Wake.java b/src/main/java/seng302/models/Wake.java index 92b2d30d..9a4e3d5d 100644 --- a/src/main/java/seng302/models/Wake.java +++ b/src/main/java/seng302/models/Wake.java @@ -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)); + } } } diff --git a/src/main/java/seng302/models/Wake2.java b/src/main/java/seng302/models/Wake2.java deleted file mode 100644 index 98090a45..00000000 --- a/src/main/java/seng302/models/Wake2.java +++ /dev/null @@ -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); - } -} diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/parsers/StreamParser.java index f49e4d31..766296a7 100644 --- a/src/main/java/seng302/models/parsers/StreamParser.java +++ b/src/main/java/seng302/models/parsers/StreamParser.java @@ -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 boatPositions = new ConcurrentHashMap<>(); - private static ArrayList boat_IDS = new ArrayList<>(); - private String threadName; + public static ConcurrentHashMap 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]; 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 @@ - - - - - - - + + + + + + + + + + - + From 6149f7be603b63b536fa56846f19e13ec8ec73c8 Mon Sep 17 00:00:00 2001 From: cir27 Date: Sat, 29 Apr 2017 02:14:55 +1200 Subject: [PATCH 3/5] Wakes no longer become out of sync with boats after extended periods of time. Added in a limit to the length of boat trails. #implement --- .../seng302/controllers/CanvasController.java | 292 ++---------------- .../controllers/RaceViewController.java | 12 +- src/main/java/seng302/models/BoatGroup.java | 20 +- src/main/java/seng302/models/RaceObject.java | 1 - src/main/java/seng302/models/Wake.java | 26 +- .../java/seng302/models/mark/MarkGroup.java | 40 +-- src/main/resources/views/RaceView.fxml | 4 +- 7 files changed, 59 insertions(+), 336 deletions(-) diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 8934a471..6c1723e4 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -6,7 +6,6 @@ import javafx.fxml.FXML; import javafx.geometry.Point2D; import javafx.geometry.Point3D; import javafx.scene.Group; -import javafx.scene.Node; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.layout.AnchorPane; @@ -14,16 +13,13 @@ 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; import seng302.models.BoatGroup; import seng302.models.Colors; import seng302.models.RaceObject; import seng302.models.mark.*; import seng302.models.parsers.StreamParser; -import seng302.models.parsers.StreamReceiver; -import java.sql.Time; import java.text.DecimalFormat; import java.util.*; @@ -42,14 +38,13 @@ public class CanvasController { private GraphicsContext gc; private final int MARK_SIZE = 10; - private final int BUFFER_SIZE = 100; + private final int BUFFER_SIZE = 150; private final int CANVAS_WIDTH = 1000; private final int CANVAS_HEIGHT = 1000; private final int LHS_BUFFER = BUFFER_SIZE; private final int RHS_BUFFER = BUFFER_SIZE + MARK_SIZE / 2; private final int TOP_BUFFER = BUFFER_SIZE; private final int BOT_BUFFER = TOP_BUFFER + MARK_SIZE / 2; - private final int FRAME_RATE = 60; private double distanceScaleFactor; private ScaleDirection scaleDirection; @@ -101,55 +96,9 @@ public class CanvasController { gc.setFill(Color.SKYBLUE); gc.fillRect(0,0, CANVAS_WIDTH, CANVAS_HEIGHT); gc.restore(); - drawCourse(); + fitMarksToCanvas(); drawBoats(); -// drawFps(12); -// // overriding the handle so that it can clean canvas and redraw boats and course marks -// AnimationTimer timer = new AnimationTimer() { -// private long lastUpdate = 0; -// private long lastFpsUpdate = 0; -// private int lastFpsCount = 0; -// private int fpsCount = 0; -// boolean done = true; -// -// @Override -// public void handle(long now) { -// if (true){ //if statement for limiting refresh rate if needed -//// gc.clearRect(0, 0, canvas.getWidth(),canvas.getHeight()); -//// gc.setFill(Color.SKYBLUE); -//// gc.fillRect(0,0,canvas.getWidth(),canvas.getHeight()); -// -// -// // If race has started, draw the boats and play the timeline -// if (raceViewController.getRace().getRaceTime() > 1) { -// raceViewController.playTimelines(); -// } -// // Race has not started, pause the timelines -// else { -// raceViewController.pauseTimelines(); -// } -// lastUpdate = now; -// fpsCount ++; -// if (now - lastFpsUpdate >= 1000000000){ -// lastFpsCount = fpsCount; -// fpsCount = 0; -// lastFpsUpdate = now; -// } -// } -// } -// }; -// timer.start(); - //try { - // Thread.sleep(10000); - //}catch (Exception e) { - // e.printStackTrace(); - //} - - - timer = new AnimationTimer() { - - private int countdown = 60; private int[] currentRaceMarker = {1, 1, 1, 1, 1, 1}; List marks = raceViewController.getRace().getCourse(); @@ -175,97 +124,18 @@ public class CanvasController { Double frameRate = 1_000_000_000.0 / elapsedNanosPerFrame ; drawFps(frameRate.intValue()); } - - //if (countdown == 0) { - //System.out.println("called the at"); - for (RaceObject raceObject : raceObjects) { - //if (currentRaceMarker[boatIndex] < marks.size()) { - //if (currentRaceMarker[boatIndex] == 6) { - // int debugLine = 4; - //} - //double xb4 = boatGroup.getLayoutX(); - //double yb4 = boatGroup.getLayoutY(); - //nextMark = marks.get(currentRaceMarker[boatIndex]); - - //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) { - if (StreamParser.boatPositions.containsKey((long) id)) { - Point3D p = StreamParser.boatPositions.get((long) id); - Point2D p2d = latLonToXY(p.getX(), p.getY()); - //System.out.println("p2d = " + p2d); - //System.out.println("p.toString() = " + p.toString()); - double heading = 360.0 / 0xffff * p.getZ(); - //System.out.println("heading = " + heading); - - raceObject.setDestination(p2d.getX(), p2d.getY(), heading, id); - - //raceObject.setDestination(p2d.getX(), p2d.getY(), id); - } - StreamParser.boatPositions.remove((long) id); - } - } - //Point3D p = StreamParser.boatPositions.get((long) raceObject.getRaceIds()[0]); - //System.out.println("boatGroup = " + boatGroup.getBoat().getId()); - //System.out.println("StreamParser.boatPositions.toString() = " + StreamParser.boatPositions.toString()); -// if (p != null) { -// Point2D p2d = latLonToXY(p.getX(), p.getY()); -// //System.out.println("p2d = " + p2d); -// if (!boatGroup.isSamePos(p2d)) { -// //System.out.println("p.toString() = " + p.toString()); -// double heading = 360.0 / 0xffff * p.getZ(); -// //System.out.println("heading = " + heading); -// -// -// -// boatGroup.setDestination(p2d.getX(), p2d.getY(), heading, boatGroup.getRaceIds()[0]); - - - - //boatGroup.setDestination(p2d.getX(), p2d.getY()); - // } - //} - -// if (descending && nextMark.getY() < boatGroup.getLayoutY()) { -// currentRaceMarker[boatIndex]++; -// boatGroup.setDestination( -// marks.get(currentRaceMarker[boatIndex]).getX(), marks.get(currentRaceMarker[boatIndex]).getY() -// ); -// } else if (!descending && nextMark.getY() > boatGroup.getLayoutY()) { -// currentRaceMarker[boatIndex]++; -// boatGroup.setDestination( -// marks.get(currentRaceMarker[boatIndex]).getX(), marks.get(currentRaceMarker[boatIndex]).getY() -// ); -// } else if (leftToRight && nextMark.getX() > boatGroup.getLayoutX()) { -// currentRaceMarker[boatIndex]++; -// boatGroup.setDestination( -// marks.get(currentRaceMarker[boatIndex]).getX(), marks.get(currentRaceMarker[boatIndex]).getY() -// ); -// } else if (!leftToRight && nextMark.getX() < boatGroup.getLayoutX()) { -// currentRaceMarker[boatIndex]++; -// boatGroup.setDestination( -// marks.get(currentRaceMarker[boatIndex]).getX(), marks.get(currentRaceMarker[boatIndex]).getY() -// ); - - -// double xnew = boatGroup.getLayoutX(); -// double ynew = boatGroup.getLayoutY(); -// double dx = xnew - xb4; -// double dy = ynew -yb4; -// raceFinished = false; -// boatIndex++; + for (RaceObject raceObject : raceObjects) { + raceObject.updatePosition(1000 / 60); + for (int id : raceObject.getRaceIds()) { + if (StreamParser.boatPositions.containsKey((long) id)) { + Point3D p = StreamParser.boatPositions.get((long) id); + Point2D p2d = latLonToXY(p.getX(), p.getY()); + double heading = 360.0 / 0xffff * p.getZ(); + raceObject.setDestination(p2d.getX(), p2d.getY(), heading, id); } - //} - //if (raceFinished) { - // System.out.println("DONZEO LADS"); - // this.stop(); - //} - //} else { - // countdown--; - //} + StreamParser.boatPositions.remove((long) id); + } + } } }; for (Mark m : raceViewController.getRace().getCourse()) { @@ -377,77 +247,10 @@ public class CanvasController { } } - /** - * Draws the course. - */ - private void drawCourse() { - fitToCanvas(); -// for (Mark mark : raceViewController.getRace().getCourse()) { -// if (mark.getMarkType() == MarkType.SINGLE_MARK) { -// drawSingleMark((SingleMark) mark, Color.BLACK); -// } else { -// drawGateMark((GateMark) mark); -// } -// } -// System.out.println("MIN/MAX POINTS"); -// System.out.println(minLatPoint.getName() + " " + minLatPoint.getX() + " " + minLatPoint.getY()); -// System.out.println(maxLatPoint.getName() + " " + maxLatPoint.getX() + " " + maxLatPoint.getY()); -// System.out.println(minLonPoint.getName() + " " + minLonPoint.getX() + " " + minLonPoint.getY()); -// System.out.println(maxLonPoint.getName() + " " + maxLonPoint.getX() + " " + maxLonPoint.getY()); -// System.out.println(referencePointX); -// System.out.println(referencePointY); - } - -// /** -// * Draw a given mark on canvas -// * -// * @param singleMark -// */ -// private void drawSingleMark(SingleMark singleMark, Color color) { -// gc.setFill(color); -// System.out.println("DRAWING " + singleMark.getName() + " at " + singleMark.getX() + ", " + singleMark.getY()); -// gc.fillOval(singleMark.getX(), singleMark.getY(),MARK_SIZE,MARK_SIZE); -// } -// -// /** -// * Draw a gate mark which contains two single marks -// * -// * @param gateMark -// */ -// private void drawGateMark(GateMark gateMark) { -// Color color = Color.BLUE; -// -// if (gateMark.getName().equals("Start")){ -// color = Color.GREEN; -// } -// -// if (gateMark.getName().equals("Finish")){ -// color = Color.RED; -// } -// -// drawSingleMark(gateMark.getSingleMark1(), color); -// drawSingleMark(gateMark.getSingleMark2(), color); -// -// GraphicsContext gc = canvas.getGraphicsContext2D(); -// gc.save(); -// gc.setStroke(color); -// if (gateMark.getMarkType() == MarkType.OPEN_GATE) -// gc.setLineDashes(3, 5); -// -// gc.setLineWidth(2); -// gc.strokeLine( -// gateMark.getSingleMark1().getX() + MARK_SIZE / 2, -// gateMark.getSingleMark1().getY() + MARK_SIZE / 2, -// gateMark.getSingleMark2().getX() + MARK_SIZE / 2, -// gateMark.getSingleMark2().getY() + MARK_SIZE / 2 -// ); -// gc.restore(); -// } - /** * Calculates x and y location for every marker that fits it to the canvas the race will be drawn on. */ - private void fitToCanvas() { + private void fitMarksToCanvas() { findMinMaxPoint(); double minLonToMaxLon = scaleRaceExtremities(); calculateReferencePointLocation(minLonToMaxLon); @@ -482,16 +285,6 @@ public class CanvasController { Collections.reverse(sortedPoints); minLonPoint = sortedPoints.get(0); maxLonPoint = sortedPoints.get(sortedPoints.size()-1); - System.out.println("ALL POINTS"); - for (Mark m : sortedPoints) - { - System.out.println(m.getName() + " " + m.getLatitude() + " " + m.getLongitude()); - } - System.out.println("MIN/MAX POINTS"); - System.out.println(minLatPoint.getName() + " " + minLatPoint.getLatitude() + " " + minLatPoint.getLongitude()); - System.out.println(maxLatPoint.getName() + " " + maxLatPoint.getLatitude() + " " + maxLatPoint.getLongitude()); - System.out.println(minLonPoint.getName() + " " + minLonPoint.getLatitude() + " " + minLonPoint.getLongitude()); - System.out.println(maxLonPoint.getName() + " " + maxLonPoint.getLatitude() + " " + maxLonPoint.getLongitude()); } /** @@ -503,20 +296,11 @@ public class CanvasController { private void calculateReferencePointLocation (double minLonToMaxLon) { Mark referencePoint = minLatPoint; double referenceAngle; - //double mapWidth = canvas.getWidth(); - //double mapHeight = canvas.getHeight(); if (scaleDirection == ScaleDirection.HORIZONTAL) { - System.out.println("HORIZONTAL"); - System.out.println("ref angle " + Mark.calculateHeadingRad(referencePoint, minLonPoint)); - //referenceAngle = Mark.calculateHeadingRad(referencePoint, minLonPoint) - (Math.PI * (3/4)); referenceAngle = Math.abs(Mark.calculateHeadingRad(referencePoint, minLonPoint)); referencePointX = LHS_BUFFER + distanceScaleFactor * Math.sin(referenceAngle) * Mark.calculateDistance(referencePoint, minLonPoint); - //referenceAngle = Mark.calculateHeadingRad(referencePoint, maxLatPoint); - //if (referenceAngle > Math.PI) { - // referenceAngle = (Math.PI * 2) - referenceAngle; - //} referenceAngle = Math.abs(Mark.calculateHeadingRad(referencePoint, maxLatPoint)); referencePointY = CANVAS_HEIGHT - (TOP_BUFFER + BOT_BUFFER); referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * Mark.calculateDistance(referencePoint, maxLatPoint); @@ -527,17 +311,11 @@ public class CanvasController { System.out.println("VERTICAL"); referencePointY = CANVAS_HEIGHT - BOT_BUFFER; - //referenceAngle = (Math.PI * 2) - Mark.calculateHeadingRad(referencePoint, minLonPoint); referenceAngle = Math.abs(Mark.calculateHeadingRad(referencePoint, minLonPoint)); - referencePointX = LHS_BUFFER; referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * Mark.calculateDistance(referencePoint, minLonPoint); referencePointX += ((CANVAS_WIDTH - (LHS_BUFFER + RHS_BUFFER)) - (minLonToMaxLon * distanceScaleFactor)) / 2; } - referencePointX = Math.round(referencePointX); - referencePointY = Math.round(referencePointY); -// referencePoint.setX((int) referencePointX); -// referencePoint.setY((int) referencePointY); } /** @@ -545,13 +323,11 @@ public class CanvasController { * Returns the max horizontal distance of the map. */ private double scaleRaceExtremities () { - //double vertAngle = Mark.calculateHeadingRad(minLatPoint, maxLatPoint); - double vertAngle = Math.abs(Mark.calculateHeadingRad(minLatPoint, maxLatPoint)); -// if (vertAngle > Math.PI) -// vertAngle = (2 * Math.PI) - vertAngle; - double vertDistance = Math.cos(vertAngle) * Mark.calculateDistance(minLatPoint, maxLatPoint); + double vertAngle = Math.abs(Mark.calculateHeadingRad(minLatPoint, maxLatPoint)); + double vertDistance = Math.cos(vertAngle) * Mark.calculateDistance(minLatPoint, maxLatPoint); double horiAngle = Mark.calculateHeadingRad(minLonPoint, maxLonPoint); + if (horiAngle <= (Math.PI / 2)) horiAngle = (Math.PI / 2) - horiAngle; else @@ -575,35 +351,19 @@ public class CanvasController { * are scaled according to the distanceScaleFactor variable. */ private void givePointsXY() { - //Point2D canvasLocation; List allPoints = new ArrayList<>(raceViewController.getRace().getCourse()); List processed = new ArrayList<>(); - //Set unqiuePoints = new HashSet<>(raceViewController.getRace().getCourse()); - //System.out.println("unqiuePoints = " + unqiuePoints); RaceObject markGroup; for (Mark mark : allPoints) { if (!processed.contains(mark)) { if (mark.getMarkType() != MarkType.SINGLE_MARK) { GateMark gateMark = (GateMark) mark; -// canvasLocation = findScaledXY(gateMark.getSingleMark1()); -// gateMark.getSingleMark1().setX((int) canvasLocation.getX()); -// gateMark.getSingleMark1().setY((int) canvasLocation.getY()); -// -// canvasLocation = findScaledXY(gateMark.getSingleMark2()); -// gateMark.getSingleMark2().setX((int) canvasLocation.getX()); -// gateMark.getSingleMark2().setY((int) canvasLocation.getY()); - markGroup = new MarkGroup(mark, findScaledXY(gateMark.getSingleMark1()), findScaledXY(gateMark.getSingleMark2())); - //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)); raceObjects.add(markGroup); - //group.getChildren().add(markGroup); } processed.add(mark); } @@ -625,35 +385,21 @@ public class CanvasController { angleFromReference = Mark.calculateHeadingRad(latA, lonA, latB, lonB); distanceFromReference = Mark.calculateDistance(latA, lonA, latB, lonB); if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) { - //System.out.println("1"); xAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); yAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); } else if (angleFromReference >= 0) { - //System.out.println("2"); angleFromReference = angleFromReference - Math.PI / 2; xAxisLocation += (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); yAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); } else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) { - //System.out.println("3"); -// System.out.println(distanceFromReference); angleFromReference = Math.abs(angleFromReference); -// System.out.println(Math.cos(angleFromReference) * distanceFromReference); xAxisLocation -= (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); yAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); } else { - //System.out.println("4"); angleFromReference = Math.abs(angleFromReference) - Math.PI / 2; xAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); yAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); } -// if (angleFromReference > (Math.PI / 2)) { -// angleFromReference = (Math.PI * 2) - angleFromReference; -// xAxisLocation -= (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); -// } else { -// xAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); -// } -// yAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); - //System.out.println(xAxisLocation + " *** " + yAxisLocation); return new Point2D(xAxisLocation, yAxisLocation); } @@ -690,4 +436,8 @@ public class CanvasController { private Point2D latLonToXY (double latitude, double longitude) { return findScaledXY(minLatPoint.getLatitude(), minLatPoint.getLongitude(), latitude, longitude); } + + List getRaceObjects() { + return raceObjects; + } } \ No newline at end of file diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 214cf02f..c064d225 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -14,10 +14,7 @@ import javafx.scene.layout.Pane; import javafx.scene.layout.VBox; import javafx.scene.text.Text; import javafx.util.Duration; -import seng302.models.Boat; -import seng302.models.Event; -import seng302.models.Race; -import seng302.models.TimelineInfo; +import seng302.models.*; import seng302.models.parsers.ConfigParser; import java.io.IOException; @@ -288,4 +285,11 @@ public class RaceViewController extends Thread{ public ArrayList getStartingBoats(){ return startingBoats; } + + @FXML + private void toggleAnnotations () { + for (RaceObject ro : includedCanvasController.getRaceObjects()) { + ro.toggleAnnotations(); + } + } } \ 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 d7f023b1..a262e61d 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -14,10 +14,10 @@ import seng302.models.parsers.StreamParser; */ public class BoatGroup extends RaceObject{ - private static final double TEAMNAME_X_OFFSET = 15d; - private static final double TEAMNAME_Y_OFFSET = -20d; - private static final double VELOCITY_X_OFFSET = 15d; - private static final double VELOCITY_Y_OFFSET = -10d; + private static final double TEAMNAME_X_OFFSET = 10d; + private static final double TEAMNAME_Y_OFFSET = -15d; + private static final double VELOCITY_X_OFFSET = 10d; + private static final double VELOCITY_Y_OFFSET = -5d; private static final double VELOCITY_WAKE_RATIO = 2d; private static final double BOAT_HEIGHT = 15d; private static final double BOAT_WIDTH = 10d; @@ -72,9 +72,9 @@ public class BoatGroup extends RaceObject{ private void initChildren (Color color) { initChildren(color, - BOAT_WIDTH / 2, 0.0, - BOAT_WIDTH, BOAT_HEIGHT, - 0.0, BOAT_HEIGHT); + -BOAT_WIDTH / 2, BOAT_HEIGHT, + 0.0, 0.0, + BOAT_WIDTH / 2, BOAT_HEIGHT); } /** @@ -111,7 +111,7 @@ public class BoatGroup extends RaceObject{ teamNameObject.setLayoutY(y); velocityObject.setLayoutX(x); velocityObject.setLayoutY(y); - wake.setLayoutX(x + BOAT_WIDTH / 2); + wake.setLayoutX(x); wake.setLayoutY(y); wake.rotate(currentRotation); } @@ -158,7 +158,7 @@ public class BoatGroup extends RaceObject{ wake.rotate(rotationalGoal); wakeGenerationDelay--; } else { - wake.setRotationalVelocity(rotationalVelocity); + wake.setRotationalVelocity(rotationalVelocity, rotationalGoal, pixelVelocityX, pixelVelocityY); } } } @@ -196,7 +196,7 @@ public class BoatGroup extends RaceObject{ public void rotateTo (double rotation) { currentRotation = rotation; boatPoly.getTransforms().clear(); - boatPoly.getTransforms().add(new Rotate(rotation, BOAT_WIDTH / 2, 0)); + boatPoly.getTransforms().add(new Rotate(rotation)); } diff --git a/src/main/java/seng302/models/RaceObject.java b/src/main/java/seng302/models/RaceObject.java index 450b11b8..30ca28f3 100644 --- a/src/main/java/seng302/models/RaceObject.java +++ b/src/main/java/seng302/models/RaceObject.java @@ -69,5 +69,4 @@ public abstract class RaceObject extends Group { public abstract int[] getRaceIds (); public abstract void toggleAnnotations (); - } diff --git a/src/main/java/seng302/models/Wake.java b/src/main/java/seng302/models/Wake.java index 9a4e3d5d..d9cf60b9 100644 --- a/src/main/java/seng302/models/Wake.java +++ b/src/main/java/seng302/models/Wake.java @@ -21,6 +21,7 @@ class Wake extends Group { private Arc[] arcs = new Arc[numWakes]; private double[] rotations = new double[numWakes]; private int velocitiesIndex = 0; + private double sum = 0; /** * Create a wake at the given location. @@ -32,14 +33,7 @@ class Wake extends Group { 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 = new Arc(0,0,0,0,-110,40); arc.setFill(new Color(0.18, 0.7, 1.0, 0.50 + OPACITY_INCREASE * i)); arc.setType(ArcType.ROUND); arcs[i] = arc; @@ -52,9 +46,23 @@ class Wake extends Group { * the latest given velocity. * @param rotationalVelocity The rotationalVelocity the wake should move at. */ - void setRotationalVelocity (double rotationalVelocity) { + void setRotationalVelocity (double rotationalVelocity, double rotationGoal, double velocityX, double velocityY) { + sum -= Math.abs(velocities[velocitiesIndex]); + sum += Math.abs(rotationalVelocity); + if (sum < 0.0001) + 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. velocitiesIndex = (velocitiesIndex + 1) % 14; velocities[velocitiesIndex] = rotationalVelocity; + + double scaleFactor = Math.abs(Math.log10(Math.abs(velocityX) + Math.abs(velocityY))); + double baseRad = 30; + for (Arc arc :arcs) { + double rad = baseRad + 5 * scaleFactor; + arc.setRadiusX(rad); + arc.setRadiusY(rad); + baseRad += RADIUS_INCREASE; + } } /** diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java index caf260fe..ab189034 100644 --- a/src/main/java/seng302/models/mark/MarkGroup.java +++ b/src/main/java/seng302/models/mark/MarkGroup.java @@ -28,10 +28,6 @@ public class MarkGroup extends RaceObject { private Point2D[] nodeDestinations; public MarkGroup (Mark mark, Point2D... points) { -// for (Point2D p : points) { -// System.out.println("p.getX() = " + p.getX()); -// System.out.println("p.getY() = " + p.getY()); -// } marks.add(mark); mainMark = mark; Color color = Color.BLACK; @@ -43,9 +39,6 @@ public class MarkGroup extends RaceObject { System.out.println("HERE ARE THE CHILDREN LOL"); if (mark.getMarkType() == MarkType.SINGLE_MARK) { super.getChildren().add(new Circle(0, 0, MARK_RADIUS, color)); -// System.out.println("SingleMark?"); -// System.out.println("super.getChildren().get(0).getLayoutX() = " + super.getChildren().get(0).getLayoutX()); -// System.out.println("super.getChildren().get(0).getLayoutY() = " + super.getChildren().get(0).getLayoutY()); } else { marks.add(((GateMark) mark).getSingleMark1()); marks.add(((GateMark) mark).getSingleMark2()); @@ -57,13 +50,6 @@ public class MarkGroup extends RaceObject { color ) ); -// super.getChildren().add(new Circle(0, 0, MARK_RADIUS, color)); -// super.getChildren().get(0).setLayoutX((points[1].getX() - points[0].getX()) / 2d); -// super.getChildren().get(0).setLayoutY((points[1].getY() - points[0].getY()) / 2d); -// System.out.println("!!!!!!!!!!!!!!!!!"); -// System.out.println((points[1].getX() - points[0].getX()) / 2d); -// System.out.println((points[1].getY() - points[0].getY()) / 2d); -// System.out.println(super.getChildren().get(0)); super.getChildren().add( new Circle( -(points[1].getX() - points[0].getX()) / 2d, @@ -72,9 +58,6 @@ public class MarkGroup extends RaceObject { color ) ); -// super.getChildren().add(new Circle(0, 0, MARK_RADIUS, color)); -// super.getChildren().get(1).setLayoutX(-(points[1].getX() - points[0].getX()) / 2d); -// super.getChildren().get(1).setLayoutY(-(points[1].getY() - points[0].getY()) / 2d); Line line = new Line( (points[1].getX() - points[0].getX()) / 2d, (points[1].getY() - points[0].getY()) / 2d, @@ -93,22 +76,8 @@ public class MarkGroup extends RaceObject { new Point2D(super.getChildren().get(0).getLayoutX(), super.getChildren().get(0).getLayoutY()), new Point2D(super.getChildren().get(1).getLayoutX(), super.getChildren().get(1).getLayoutY()) }; -// nodeDestinations = new Point2D[]{new Point2D(0,0), new Point2D(0,0)}; -// System.out.println("super.getChildren().get(0).getLayoutX() = " + super.getChildren().get(0).getLayoutX()); -// System.out.println("super.getChildren().get(0).getLayoutY() = " + super.getChildren().get(0).getLayoutY()); -// System.out.println("super.getChildren().get(1).getLayoutX() = " + super.getChildren().get(1).getLayoutX()); -// System.out.println("super.getChildren().get(1).getLayoutY() = " + super.getChildren().get(1).getLayoutY()); } moveTo(points[0].getX(), points[0].getY()); -// System.out.println("OKAY HERE IS A MARK"); -// System.out.println("super.getLayoutX() = " + super.getLayoutX()); -// System.out.println("super.getLayoutY() = " + super.getLayoutY()); -// System.out.println("super.getChildren().get(0).getLayoutX() = " + super.getChildren().get(0).getLayoutX()); -// System.out.println("super.getChildren().get(0).getLayoutY() = " + super.getChildren().get(0).getLayoutY()); -// pixelVelocityX = 0; -// pixelVelocityY = 0; -// rotationalVelocity = 0; -// rotationalGoal = 0; } public void setDestination (double x, double y, double rotation, int... raceIds) { @@ -146,14 +115,7 @@ public class MarkGroup extends RaceObject { public void rotateTo (double rotation) { super.getTransforms().clear(); -// super.getTransforms().add( -// new Rotate( -// rotation, -// super.getChildren().get(1).getLayoutX() - super.getChildren().get(0).getLayoutX(), -// super.getChildren().get(1).getLayoutY() - super.getChildren().get(0).getLayoutY() -// ) -// ); - super.getTransforms().add(new Rotate(rotation, 0 , 0)); + super.getTransforms().add(new Rotate(rotation)); } public void updatePosition (long timeInterval) { diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index 2cae1326..6de979d4 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -12,7 +12,7 @@ - + @@ -40,7 +40,7 @@ - + From 80409c08a640f2624fe5a3987b82cb2e8e5da36f Mon Sep 17 00:00:00 2001 From: cir27 Date: Sat, 29 Apr 2017 02:17:55 +1200 Subject: [PATCH 4/5] Removed a graphical object added for testing. --- .../java/seng302/controllers/CanvasController.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 6c1723e4..fda4eed2 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -196,23 +196,12 @@ public class CanvasController { private void drawBoats() { // Map timelineInfos = raceViewController.getTimelineInfos(); List boats = raceViewController.getStartingBoats(); - System.out.println("raceObjects " + raceObjects); Double startingX = raceObjects.get(0).getLayoutX(); 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.forceRotation(); @@ -308,7 +297,6 @@ public class CanvasController { referencePointY += TOP_BUFFER; referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * Mark.calculateDistance(referencePoint, maxLatPoint); } else { - System.out.println("VERTICAL"); referencePointY = CANVAS_HEIGHT - BOT_BUFFER; referenceAngle = Math.abs(Mark.calculateHeadingRad(referencePoint, minLonPoint)); @@ -371,7 +359,6 @@ public class CanvasController { } private Point2D findScaledXY (Mark unscaled) { - System.out.println("unscaled.getName() = " + unscaled.getName()); return findScaledXY (minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaled.getLatitude(), unscaled.getLongitude()); } From 02a35b4c02ee772b1f4703574167c2bc3ca2eabe Mon Sep 17 00:00:00 2001 From: cir27 Date: Sat, 29 Apr 2017 14:20:52 +1200 Subject: [PATCH 5/5] Reduced the cost of updating wakes. Improved the logic for controlling indices. #implement #story[820] --- src/main/java/seng302/models/Wake.java | 59 ++++++++++++-------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/src/main/java/seng302/models/Wake.java b/src/main/java/seng302/models/Wake.java index d9cf60b9..3c79d3a9 100644 --- a/src/main/java/seng302/models/Wake.java +++ b/src/main/java/seng302/models/Wake.java @@ -14,13 +14,12 @@ import javafx.scene.transform.Rotate; * to be populated for the class to work as expected. */ 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 double[] velocities = new double[13]; private Arc[] arcs = new Arc[numWakes]; private double[] rotations = new double[numWakes]; - private int velocitiesIndex = 0; + private int[] velocityIndices = new int[numWakes]; private double sum = 0; /** @@ -33,10 +32,12 @@ class Wake extends Group { super.setLayoutY(startingY); Arc arc; for (int i = 0; i < numWakes; i++) { - arc = new Arc(0,0,0,0,-110,40); - arc.setFill(new Color(0.18, 0.7, 1.0, 0.50 + OPACITY_INCREASE * i)); - arc.setType(ArcType.ROUND); - arcs[i] = arc; + //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); + //Opacity increases from 0.5 -> 0 evenly over the 5 wake arcs. + arc.setFill(new Color(0.18, 0.7, 1.0, 0.50 + -0.1 * i)); + arc.setType(ArcType.ROUND); + arcs[i] = arc; } super.getChildren().addAll(arcs); } @@ -47,21 +48,27 @@ class Wake extends Group { * @param rotationalVelocity The rotationalVelocity the wake should move at. */ void setRotationalVelocity (double rotationalVelocity, double rotationGoal, double velocityX, double velocityY) { - sum -= Math.abs(velocities[velocitiesIndex]); + sum -= Math.abs(velocities[velocityIndices[0]]); sum += Math.abs(rotationalVelocity); if (sum < 0.0001) 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. - velocitiesIndex = (velocitiesIndex + 1) % 14; - velocities[velocitiesIndex] = rotationalVelocity; + //Update the index of the array of recent velocities that each wake uses. Each wake is 3 velocities behind the + //next smallest wake. + velocityIndices[0] = (13 + (velocityIndices[0] - 1) % 13) % 13; + velocities[velocityIndices[0]] = rotationalVelocity; + 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 = 30; for (Arc arc :arcs) { - double rad = baseRad + 5 * scaleFactor; + double rad = Math.min(baseRad + 4 * scaleFactor, baseRad + 12); arc.setRadiusX(rad); arc.setRadiusY(rad); - baseRad += RADIUS_INCREASE; + baseRad += 10; } } @@ -70,28 +77,16 @@ class Wake extends Group { * @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); + for (int i = 0; i < numWakes; i++) { + rotations[i] = rotations[i] + velocities[velocityIndices[i]] * timeInterval; + arcs[i].getTransforms().setAll(new Rotate(rotations[i])); } } + /** + * Rotate all wakes to the given rotation. + * @param rotation the from north angle in degrees to rotate to. + */ void rotate (double rotation) { for (int i = 0; i < arcs.length; i++) { rotations[i] = rotation;