From a56e55ae70fd9945812b60b845b3b714c1b6de43 Mon Sep 17 00:00:00 2001 From: Calum Date: Thu, 4 May 2017 10:38:04 +1200 Subject: [PATCH] Fix for some movement on racestart issues #bug --- src/main/java/seng302/App.java | 12 +- .../seng302/controllers/CanvasController.java | 5 +- .../java/seng302/controllers/Controller.java | 12 +- .../controllers/RaceViewController.java | 9 + src/main/java/seng302/models/BoatGroup.java | 177 ++++++++++++------ src/main/java/seng302/models/Wake.java | 12 +- 6 files changed, 157 insertions(+), 70 deletions(-) diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index c86ef5d1..c2a6b129 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -5,6 +5,7 @@ import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; +import seng302.controllers.Controller; import seng302.models.parsers.StreamParser; import seng302.models.parsers.StreamReceiver; import seng302.server.ServerThread; @@ -13,10 +14,11 @@ public class App extends Application { @Override public void start(Stage primaryStage) throws Exception { - Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml")); +// Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml")); + FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/MainView.fxml")); primaryStage.setTitle("RaceVision"); - primaryStage.setScene(new Scene(root)); - + primaryStage.setScene(new Scene(loader.load())); + ((Controller) loader.getController()).setStage(primaryStage); primaryStage.show(); } @@ -34,8 +36,8 @@ public class App extends Application sr = new StreamReceiver("localhost", 8085, "RaceStream"); } else{ -// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941,"RaceStream"); - sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream"); + sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941,"RaceStream"); +// sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream"); // sr = new StreamReceiver("localhost", 8085, "RaceStream"); } diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index a6ceda69..6fd7d592 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -5,11 +5,14 @@ import javafx.beans.property.SimpleDoubleProperty; import javafx.fxml.FXML; import javafx.geometry.Point2D; import javafx.scene.Group; +import javafx.scene.Node; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.text.Font; +import javafx.stage.Stage; import seng302.models.Boat; import seng302.models.BoatGroup; import seng302.models.Colors; @@ -326,7 +329,7 @@ public class CanvasController { for (Boat boat : boats) { BoatGroup boatGroup = new BoatGroup(boat, Colors.getColor()); boatGroup.moveTo(startingX, startingY, 0d); - boatGroup.forceRotation(); + boatGroup.setStage(raceViewController.getStage()); raceObjects.add(boatGroup); boatAnnotations.getChildren().add(boatGroup.getLowPriorityAnnotations()); } diff --git a/src/main/java/seng302/controllers/Controller.java b/src/main/java/seng302/controllers/Controller.java index 228369c0..1a4adecd 100644 --- a/src/main/java/seng302/controllers/Controller.java +++ b/src/main/java/seng302/controllers/Controller.java @@ -7,6 +7,7 @@ import javafx.concurrent.Task; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; +import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; @@ -15,6 +16,7 @@ import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; +import javafx.stage.Stage; import seng302.models.parsers.StreamParser; import seng302.models.parsers.XMLParser; @@ -48,12 +50,17 @@ public class Controller implements Initializable { private TableColumn countryCol; @FXML private Label realTime; + private Stage stage; private void setContentPane(String jfxUrl){ try{ contentPane.getChildren().removeAll(); contentPane.getChildren().clear(); - contentPane.getChildren().addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl))); +// contentPane.getChildren().addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl))); + FXMLLoader loader = new FXMLLoader(getClass().getResource(jfxUrl)); + contentPane.getChildren().addAll((Node) loader.load()); + RaceViewController r = (RaceViewController) loader.getController(); + //((RaceViewController) loader.getController()).setStage(stage); } catch(javafx.fxml.LoadException e){ System.err.println(e.getCause()); @@ -145,4 +152,7 @@ public class Controller implements Initializable { data.add(boat); } } + public void setStage (Stage stage) { + this.stage = stage; + } } diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 6112be11..e38c9454 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -15,6 +15,7 @@ import javafx.scene.layout.Pane; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.text.Text; +import javafx.stage.Stage; import javafx.util.Duration; import javafx.util.StringConverter; import seng302.models.*; @@ -49,6 +50,7 @@ public class RaceViewController extends Thread{ private Map timelineInfos = new HashMap<>(); private ArrayList boatOrder = new ArrayList<>(); private Race race; + private Stage stage; public void initialize() { @@ -397,4 +399,11 @@ public class RaceViewController extends Thread{ } } + void setStage (Stage stage) { + this.stage = stage; + } + + Stage getStage () { + return stage; + } } \ 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 693087e1..2f923318 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -7,33 +7,46 @@ import javafx.scene.shape.Line; import javafx.scene.shape.Polygon; import javafx.scene.text.Text; import javafx.scene.transform.Rotate; -import seng302.models.parsers.StreamParser; +import javafx.stage.Stage; + +import java.util.ArrayList; +import java.util.List; /** * BoatGroup is a javafx group that by default contains a graphical objects for representing a 2 dimensional boat. * It contains a single polygon for the boat, a group of lines to show it's path, a wake object and two text labels to - * annotate the boat teams name and the boats velocity. + * annotate the boat teams name and the boats velocity. The boat will update it's position onscreen everytime + * UpdatePosition is called unless the window is minimized in which case it attempts to store animations and apply them + * when the window is maximised. */ public class BoatGroup extends RaceObject{ + //Constants for drawing 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 BOAT_HEIGHT = 15d; private static final double BOAT_WIDTH = 10d; - private static double expectedUpdateInterval = 200; - private boolean destinationSet; + //Variables for boat logic. private Point2D lastPoint; private int wakeGenerationDelay = 10; private double distanceTravelled; - + //Graphical objects private Boat boat; private Group lineGroup = new Group(); private Polygon boatPoly; private Text teamNameObject; private Text velocityObject; private Wake wake; + //Handles boat moving when connecting to a stream + private boolean setToInitialLocation = false; + private boolean destinationSet; + //Variables for handling minimization + private Stage stage; + private boolean isMaximized= true; + private List lineStorage = new ArrayList<>(); + private int setCallCount = 5; /** * Creates a BoatGroup with the default triangular boat polygon. @@ -146,62 +159,65 @@ public class BoatGroup extends RaceObject{ */ public void updatePosition (long timeInterval) { //Calculate the movement of the boat. - double dx = pixelVelocityX * timeInterval; - double dy = pixelVelocityY * timeInterval; - double rotation = rotationalVelocity * timeInterval; - distanceTravelled += Math.abs(dx) + Math.abs(dy); - moveGroupBy(dx, dy, rotation); - //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(), - lastPoint.getY(), - boatPoly.getLayoutX(), - boatPoly.getLayoutY() - ); - l.getStrokeDashArray().setAll(3d, 7d); - l.setStroke(boatPoly.getFill()); - lineGroup.getChildren().add(l); - } - if (destinationSet){ //Only begin drawing after the first destination is set - lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); + if (isMaximized) { + double dx = pixelVelocityX * timeInterval; + double dy = pixelVelocityY * timeInterval; + double rotation = rotationalVelocity * timeInterval; + distanceTravelled += Math.abs(dx) + Math.abs(dy); + moveGroupBy(dx, dy, rotation); + //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(), + lastPoint.getY(), + boatPoly.getLayoutX(), + boatPoly.getLayoutY() + ); + l.getStrokeDashArray().setAll(3d, 7d); + l.setStroke(boatPoly.getFill()); + lineGroup.getChildren().add(l); + } + if (destinationSet) { //Only begin drawing after the first destination is set + lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); + } } + wake.updatePosition(timeInterval); } - wake.updatePosition(timeInterval); } /** * Sets the destination of the boat and the headng it should have once it reaches - * @param newXValue - * @param newYValue + * @param newXValue The X co-ordinate the boat needs to move to. + * @param newYValue The Y co-ordinate the boat needs to move to. * @param rotation Rotation to move graphics to. * @param raceIds RaceID of the object to move. */ public void setDestination (double newXValue, double newYValue, double rotation, double groundSpeed, int... raceIds) { if (hasRaceId(raceIds)) { - destinationSet = true; - boat.setVelocity(groundSpeed); - if (currentRotation < 0) - currentRotation = 360 - currentRotation; - double dx = newXValue - boatPoly.getLayoutX(); - if ((dx > 0 && pixelVelocityX < 0) || (dx < 0 && pixelVelocityX > 0)) { - pixelVelocityX = 0; - } else { - pixelVelocityX = dx / expectedUpdateInterval; - } - double dy = newYValue - boatPoly.getLayoutY(); - //Check movement is reasonable. Assumes a 1000 * 1000 canvas - if (Math.abs(dx) > 50 || Math.abs(dy) > 50) { + if (setToInitialLocation) { + destinationSet = true; + boat.setVelocity(groundSpeed); + if (currentRotation < 0) + currentRotation = 360 - currentRotation; + double dx = newXValue - boatPoly.getLayoutX(); +// if ((dx > 0 && pixelVelocityX < 0) || (dx < 0 && pixelVelocityX > 0)) { +// pixelVelocityX = 0; +// } else { + pixelVelocityX = dx / expectedUpdateInterval; +// } + double dy = newYValue - boatPoly.getLayoutY(); + //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. + 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 { @@ -212,17 +228,46 @@ public class BoatGroup extends RaceObject{ // } else { // pixelVelocityY = dy / expectedUpdateInterval; // } - pixelVelocityX = dx / expectedUpdateInterval; - pixelVelocityY = dy / expectedUpdateInterval; - rotationalGoal = rotation; - calculateRotationalVelocity(); - if (wakeGenerationDelay > 0) { - wake.rotate(rotationalGoal); - wakeGenerationDelay--; + pixelVelocityX = dx / expectedUpdateInterval; + pixelVelocityY = dy / expectedUpdateInterval; + rotationalGoal = rotation; + calculateRotationalVelocity(); + if (wakeGenerationDelay > 0) { + wake.rotate(rotationalGoal); + rotateTo(rotationalGoal); + rotationalVelocity = 0; + wakeGenerationDelay--; + } else { + wake.setRotationalVelocity(rotationalVelocity, currentRotation, boat.getVelocity()); + } + velocityObject.setText(String.format("%.2f m/s", boat.getVelocity())); } else { - wake.setRotationalVelocity(rotationalVelocity, currentRotation, boat.getVelocity()); + setToInitialLocation = true; + rotationalGoal = rotation; + moveTo(newXValue, newYValue, rotation); + } + } + //If minimized generate lines every 5 calls to set destination. + if (!isMaximized) { + setToInitialLocation = false; + wakeGenerationDelay = 2; + if(setCallCount-- == 0) { + setCallCount = 5; + if (lastPoint != null) { + Line l = new Line( + lastPoint.getX(), + lastPoint.getY(), + newXValue, + newYValue + ); + l.getStrokeDashArray().setAll(3d, 7d); + l.setStroke(boatPoly.getFill()); + lineStorage.add(l); + } + if (destinationSet) { //Only begin drawing after the first destination is set + lastPoint = new Point2D(newXValue, newYValue); + } } - velocityObject.setText(String.format("%.2f m/s", boat.getVelocity())); } } @@ -307,4 +352,22 @@ public class BoatGroup extends RaceObject{ group.getChildren().addAll(wake, lineGroup); return group; } + + /** + * Use this function to let the BoatGroup know about the stage it is in. If it knows about it's stage then it will + * listen to the iconified property of that stage and change it's behaviour upon minimization. Without setting the + * Stage there is guarantee that the BoatGroup will draw properly when the stage is minimized. + * + * @param stage The stage that the BoatGroup is added to. + */ + public void setStage (Stage stage) { + this.stage = stage; + this.stage.iconifiedProperty().addListener(e -> { + isMaximized = !stage.isIconified(); + if (!lineStorage.isEmpty()) { + lineGroup.getChildren().addAll(lineStorage); + lineStorage.clear(); + } + }); + } } diff --git a/src/main/java/seng302/models/Wake.java b/src/main/java/seng302/models/Wake.java index d389e8e7..19df8c8e 100644 --- a/src/main/java/seng302/models/Wake.java +++ b/src/main/java/seng302/models/Wake.java @@ -36,7 +36,7 @@ class Wake extends Group { //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.setFill(new Color(0.18, 0.7, 1.0, 1.0 + -0.175 * i)); arc.setType(ArcType.ROUND); arcs[i] = arc; } @@ -52,17 +52,17 @@ class Wake extends Group { * @param velocity The real world velocity of the boat in m/s. */ 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); max = Math.max(max, rotationalVelocity); - if (sum < max) +// System.out.println("max = " + max); + if (sum < (max / 3)) 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. - +// if (Math.abs(rotationalVelocity) > 0.5) { +// rotationalVelocity = 0; +// } //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;