From 9889a474ee59159a2e550f02fce3daab08022b82 Mon Sep 17 00:00:00 2001 From: fjc40 Date: Wed, 2 Aug 2017 18:24:43 +1200 Subject: [PATCH 1/4] Added ArrowController. This is the controller for arrow.fxml. Removed arrow control stuff from ResizableRaceCanvas. Added a wind speed label to arrow.fxml. Also created an outer GridPane to lay things out. #story[1093] --- .../Controllers/ArrowController.java | 152 ++++++++++++++++++ .../Controllers/RaceController.java | 34 ++-- .../visualiser/model/ResizableRaceCanvas.java | 38 +---- .../resources/visualiser/scenes/arrow.fxml | 70 +++++--- .../resources/visualiser/scenes/race.fxml | 7 +- 5 files changed, 228 insertions(+), 73 deletions(-) create mode 100644 racevisionGame/src/main/java/visualiser/Controllers/ArrowController.java diff --git a/racevisionGame/src/main/java/visualiser/Controllers/ArrowController.java b/racevisionGame/src/main/java/visualiser/Controllers/ArrowController.java new file mode 100644 index 00000000..8ef783a8 --- /dev/null +++ b/racevisionGame/src/main/java/visualiser/Controllers/ArrowController.java @@ -0,0 +1,152 @@ +package visualiser.Controllers; + + +import javafx.application.Platform; +import javafx.beans.property.Property; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.ImageView; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import javafx.scene.shape.Circle; +import shared.model.Bearing; +import shared.model.Wind; +import visualiser.model.VisualiserRace; + +/** + * Controller for the arrow.fxml view. + */ +public class ArrowController { + + + @FXML + private Pane compass; + + @FXML + private StackPane arrowStackPane; + + @FXML + private ImageView arrowImage; + + @FXML + private Circle circle; + + @FXML + private Label northLabel; + + @FXML + private Label windLabel; + + @FXML + private Label speedLabel; + + + /** + * This is the property our arrow control binds to. + */ + private Property wind; + + + /** + * Constructor. + */ + public ArrowController() { + } + + + /** + * Sets which wind property the arrow control should bind to. + * @param wind The wind property to bind to. + */ + public void setWindProperty(Property wind) { + this.wind = wind; + + wind.addListener((observable, oldValue, newValue) -> { + if (newValue != null) { + Platform.runLater(() -> updateWind(newValue)); + } + }); + } + + + /** + * Updates the control to use the new wind value. + * This updates the arrow direction (due to bearing), arrow length (due to speed), and label (due to speed). + * @param wind The wind value to use. + */ + private void updateWind(Wind wind) { + updateWindBearing(wind.getWindDirection()); + updateWindSpeed(wind.getWindSpeed()); + } + + + /** + * Updates the control to account for the new wind speed. + * This changes the length (height) of the wind arrow, and updates the speed label. + * @param speedKnots The new wind speed, in knots. + */ + private void updateWindSpeed(double speedKnots) { + updateWindArrowLength(speedKnots); + updateWindSpeedLabel(speedKnots); + } + + /** + * Updates the length of the wind arrow according to the specified wind speed. + * @param speedKnots Wind speed, in knots. + */ + private void updateWindArrowLength(double speedKnots) { + + //At 2 knots, the arrow reaches its minimum height, and at 30 knots it reaches its maximum height. + double minKnots = 2; + double maxKnots = 30; + double deltaKnots = maxKnots - minKnots; + + double minHeight = 25; + double maxHeight = 75; + double deltaHeight = maxHeight - minHeight; + + //Clamp speed. + if (speedKnots > maxKnots) { + speedKnots = maxKnots; + } else if (speedKnots < minKnots) { + speedKnots = minKnots; + } + + //How far between the knots bounds is the current speed? + double currentDeltaKnots = speedKnots - minKnots; + double currentKnotsScalar = currentDeltaKnots / deltaKnots; + + //Thus, how far between the pixel height bounds should the arrow height be? + double newHeight = minHeight + (currentKnotsScalar * deltaHeight); + + arrowImage.setFitHeight(newHeight); + } + + /** + * Updates the wind speed label according to the specified wind speed. + * @param speedKnots Wind speed, in knots. + */ + private void updateWindSpeedLabel(double speedKnots) { + speedLabel.setText(String.format("%.1fkn", speedKnots)); + } + + + /** + * Updates the control to account for a new wind bearing. + * This rotates the arrow according to the bearing. + * @param bearing The bearing to use to rotate arrow. + */ + private void updateWindBearing(Bearing bearing) { + + //We need to display wind-from, so add 180 degrees. + Bearing fromBearing = Bearing.fromDegrees(bearing.degrees() + 180d); + + //Rotate the wind arrow. + arrowStackPane.setRotate(fromBearing.degrees()); + } + + + + +} diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index 5777c06e..7e4cb945 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -5,7 +5,6 @@ import javafx.animation.AnimationTimer; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.scene.chart.LineChart; import javafx.scene.control.*; @@ -24,10 +23,8 @@ import visualiser.gameController.Keys.ControlKey; import visualiser.gameController.Keys.KeyFactory; import visualiser.model.*; -import java.awt.*; import java.io.IOException; import java.net.URL; -import java.text.DecimalFormat; import java.util.ResourceBundle; /** @@ -60,6 +57,11 @@ public class RaceController extends Controller { */ private Sparkline sparkline; + /** + * The arrow controller. + */ + @FXML private ArrowController arrowController; + /** * Service for sending keystrokes to server */ @@ -67,8 +69,18 @@ public class RaceController extends Controller { @FXML private GridPane canvasBase; - @FXML private Pane arrow; + + @FXML private SplitPane race; + + /** + * This is the root node of the arrow control. + */ + @FXML private Pane arrow; + + /** + * This is the pane we place the actual arrow control inside of. + */ @FXML private StackPane arrowPane; @FXML private Label timer; @FXML private Label FPS; @@ -118,15 +130,15 @@ public class RaceController extends Controller { //Fps display. initialiseFps(this.visualiserRace); - //Need to add the included arrow pane to the arrowPane container. - initialiseArrow(); - //Information table. initialiseInfoTable(this.visualiserRace); //Sparkline. initialiseSparkline(this.visualiserRace); + //Arrow. + initialiseArrow(this.visualiserRace); + //Race canvas. initialiseRaceCanvas(this.visualiserRace); @@ -294,7 +306,7 @@ public class RaceController extends Controller { private void initialiseRaceCanvas(VisualiserRace race) { //Create canvas. - raceCanvas = new ResizableRaceCanvas(race, arrow.getChildren().get(0)); + raceCanvas = new ResizableRaceCanvas(race); //Set properties. raceCanvas.setMouseTransparent(true); @@ -367,10 +379,10 @@ public class RaceController extends Controller { /** - * Adds the included arrow pane (see arrow.fxml) to the arrowPane (see race.fxml). + * Initialises the arrow controller with data from the race to observe. */ - private void initialiseArrow() { - arrowPane.getChildren().add(arrow); + private void initialiseArrow(VisualiserRace race) { + arrowController.setWindProperty(race.windProperty()); } diff --git a/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java b/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java index 7664f854..adbd4840 100644 --- a/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java +++ b/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java @@ -54,22 +54,15 @@ public class ResizableRaceCanvas extends ResizableCanvas { private boolean annoTimeSinceLastMark = true; - /** - * The wind arrow node. - */ - private Node arrow; - /** * Constructs a {@link ResizableRaceCanvas} using a given {@link VisualiserRace}. * @param visualiserRace The race that data is read from in order to be drawn. - * @param arrow The wind arrow's node. */ - public ResizableRaceCanvas(VisualiserRace visualiserRace, Node arrow) { + public ResizableRaceCanvas(VisualiserRace visualiserRace) { super(); this.visualiserRace = visualiserRace; - this.arrow = arrow; RaceDataSource raceData = visualiserRace.getRaceDataSource(); @@ -375,32 +368,6 @@ public class ResizableRaceCanvas extends ResizableCanvas { - /** - * Displays an arrow representing wind direction on the Canvas. - * This function accepts a wind-to bearing, but displays a wind-from bearing. - * - * @param angle Angle that the arrow is to be facing in degrees 0 degrees = North (Up). - * @see GraphCoordinate - */ - private void displayWindArrow(double angle) { - - //We need to display wind-from, so add 180 degrees. - angle += 180d; - - //Get it within [0, 360). - while (angle >= 360d) { - angle -= 360d; - } - - //Rotate the wind arrow. - if (arrow != null && arrow.getRotate() != angle) { - arrow.setRotate(angle); - } - } - - - - /** * Draws all of the {@link Mark}s on the canvas. */ @@ -511,9 +478,6 @@ public class ResizableRaceCanvas extends ResizableCanvas { //Marks. drawMarks(); - //Wind arrow. This rotates the wind arrow node. - displayWindArrow(this.visualiserRace.getWindDirection().degrees()); - } diff --git a/racevisionGame/src/main/resources/visualiser/scenes/arrow.fxml b/racevisionGame/src/main/resources/visualiser/scenes/arrow.fxml index 6e8a88b5..4057753d 100644 --- a/racevisionGame/src/main/resources/visualiser/scenes/arrow.fxml +++ b/racevisionGame/src/main/resources/visualiser/scenes/arrow.fxml @@ -1,34 +1,58 @@ - - - - - - - + + + + + + + + + + + - + + + + + + + + + - + - - - - - + + + + + + + + + + + + - - - - + - + diff --git a/racevisionGame/src/main/resources/visualiser/scenes/race.fxml b/racevisionGame/src/main/resources/visualiser/scenes/race.fxml index 76da5379..159d725c 100644 --- a/racevisionGame/src/main/resources/visualiser/scenes/race.fxml +++ b/racevisionGame/src/main/resources/visualiser/scenes/race.fxml @@ -16,7 +16,6 @@ - @@ -76,7 +75,11 @@ - + + + + + From e1905e9e36154f8bdd0cf37942aefe0eb2e4bacc Mon Sep 17 00:00:00 2001 From: fjc40 Date: Wed, 2 Aug 2017 18:27:40 +1200 Subject: [PATCH 2/4] javadoc fix. --- .../src/main/java/visualiser/Controllers/ArrowController.java | 2 +- .../src/main/java/visualiser/Controllers/RaceController.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/racevisionGame/src/main/java/visualiser/Controllers/ArrowController.java b/racevisionGame/src/main/java/visualiser/Controllers/ArrowController.java index 8ef783a8..3e81cd16 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/ArrowController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/ArrowController.java @@ -141,7 +141,7 @@ public class ArrowController { //We need to display wind-from, so add 180 degrees. Bearing fromBearing = Bearing.fromDegrees(bearing.degrees() + 180d); - + //Rotate the wind arrow. arrowStackPane.setRotate(fromBearing.degrees()); } diff --git a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java index 7e4cb945..f34c57a8 100644 --- a/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java +++ b/racevisionGame/src/main/java/visualiser/Controllers/RaceController.java @@ -380,6 +380,7 @@ public class RaceController extends Controller { /** * Initialises the arrow controller with data from the race to observe. + * @param race The race to observe. */ private void initialiseArrow(VisualiserRace race) { arrowController.setWindProperty(race.windProperty()); From e4999a3c937d4765362317711e9cc7795f571b9c Mon Sep 17 00:00:00 2001 From: fjc40 Date: Thu, 3 Aug 2017 12:56:15 +1200 Subject: [PATCH 3/4] Added a light variant of the arrow image. nightMode.css loads this instead of the regular dark arrow. ResizableRaceCanvas doesn't cache the race boundary background, as it was unneccessary and stopped the canvas from being transparent. #story[1093] --- .../visualiser/model/ResizableRaceCanvas.java | 29 ++---------------- .../src/main/resources/css/dayMode.css | 4 ++- .../src/main/resources/css/nightMode.css | 7 ++++- .../visualiser/images/arrowLight.png | Bin 0 -> 23441 bytes 4 files changed, 11 insertions(+), 29 deletions(-) create mode 100644 racevisionGame/src/main/resources/visualiser/images/arrowLight.png diff --git a/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java b/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java index adbd4840..e9b4c31c 100644 --- a/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java +++ b/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java @@ -12,7 +12,6 @@ import shared.model.GPSCoordinate; import shared.model.Mark; import shared.model.RaceClock; -import java.time.Duration; import java.util.List; /** @@ -39,12 +38,6 @@ public class ResizableRaceCanvas extends ResizableCanvas { */ private VisualiserRace visualiserRace; - /** - * The background of the race. - * We render the background whenever the race boundary changes, or the screen size changes. - */ - private Image background; - private boolean annoName = true; private boolean annoAbbrev = true; @@ -407,13 +400,9 @@ public class ResizableRaceCanvas extends ResizableCanvas { this.map.setWidth((int) getWidth()); this.map.setHeight((int) getHeight()); - //Redraw the boundary. - redrawBoundaryImage(); - //Draw the race. drawRace(); - } @@ -426,15 +415,13 @@ public class ResizableRaceCanvas extends ResizableCanvas { /** - * Draws the race boundary, and saves the image to {@link #background}. - * You should call {@link #clear()} before calling this. + * Draws the race boundary. */ - private void redrawBoundaryImage() { + private void drawBoundary() { //Prepare to draw. gc.setLineWidth(1); gc.setFill(Color.AQUA); - gc.drawImage(new Image(getClass().getClassLoader().getResourceAsStream("images/WaterBackground.png")), 0, 0); //Calculate the screen coordinates of the boundary. @@ -454,9 +441,6 @@ public class ResizableRaceCanvas extends ResizableCanvas { //Draw the boundary. gc.fillPolygon(xpoints, ypoints, xpoints.length); - //Render boundary to image. - this.background = snapshot(null, null); - } /** @@ -481,15 +465,6 @@ public class ResizableRaceCanvas extends ResizableCanvas { } - /** - * Draws the race boundary image onto the canvas. - * See {@link #background}. - */ - private void drawBoundary() { - gc.drawImage(this.background, 0, 0); - } - - diff --git a/racevisionGame/src/main/resources/css/dayMode.css b/racevisionGame/src/main/resources/css/dayMode.css index b15f242b..d0f62fb7 100644 --- a/racevisionGame/src/main/resources/css/dayMode.css +++ b/racevisionGame/src/main/resources/css/dayMode.css @@ -50,4 +50,6 @@ .scroll-bar > .increment-button:pressed > .increment-arrow, .scroll-bar > .decrement-button:pressed > .decrement-arrow { -fx-background-color: -fx-mark-highlight-color, rgb(255, 255, 255); -} \ No newline at end of file +} + + diff --git a/racevisionGame/src/main/resources/css/nightMode.css b/racevisionGame/src/main/resources/css/nightMode.css index 406cc60b..7fe6a67b 100644 --- a/racevisionGame/src/main/resources/css/nightMode.css +++ b/racevisionGame/src/main/resources/css/nightMode.css @@ -51,4 +51,9 @@ .scroll-bar > .increment-button:pressed > .increment-arrow, .scroll-bar > .decrement-button:pressed > .decrement-arrow { -fx-background-color: -fx-mark-highlight-color, rgb(255, 255, 255); -} \ No newline at end of file +} + + +#arrowImage { + -fx-image: url("/visualiser/images/arrowLight.png"); +} diff --git a/racevisionGame/src/main/resources/visualiser/images/arrowLight.png b/racevisionGame/src/main/resources/visualiser/images/arrowLight.png new file mode 100644 index 0000000000000000000000000000000000000000..7a80459d956a2dcb4a2ab5aa4d5704f7f8a23f8f GIT binary patch literal 23441 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 NIImcA!ATbLe*l%T0dD{R literal 0 HcmV?d00001 From 2e325d5177ec4bb4147086dc6fc6cfc876bb55d5 Mon Sep 17 00:00:00 2001 From: fjc40 Date: Thu, 3 Aug 2017 13:21:53 +1200 Subject: [PATCH 4/4] Added WindGeneratorTest. --- .../java/mock/model/WindGeneratorTest.java | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 racevisionGame/src/test/java/mock/model/WindGeneratorTest.java diff --git a/racevisionGame/src/test/java/mock/model/WindGeneratorTest.java b/racevisionGame/src/test/java/mock/model/WindGeneratorTest.java new file mode 100644 index 00000000..74ad7421 --- /dev/null +++ b/racevisionGame/src/test/java/mock/model/WindGeneratorTest.java @@ -0,0 +1,111 @@ +package mock.model; + +import org.junit.Before; +import org.junit.Test; +import shared.model.Bearing; +import shared.model.Wind; + +import static org.junit.Assert.*; + + +public class WindGeneratorTest { + + + private WindGenerator windGenerator; + + private Bearing windBaselineBearing; + private Bearing windBearingLowerBound; + private Bearing windBearingUpperBound; + private double windBaselineSpeed; + private double windSpeedLowerBound; + private double windSpeedUpperBound; + + private double speedKnotsEpsilon; + private double bearingDegreeEpsilon; + + + @Before + public void setUp() throws Exception { + + + //Bounds. + this.windBaselineBearing = Bearing.fromDegrees(88.3); + this.windBearingLowerBound = Bearing.fromDegrees(66.5); + this.windBearingUpperBound = Bearing.fromDegrees(248.6); + this.windBaselineSpeed = 13; + this.windSpeedLowerBound = 7; + this.windSpeedUpperBound = 20; + + this.windGenerator = new WindGenerator( + windBaselineBearing, + windBearingLowerBound, + windBearingUpperBound, + windBaselineSpeed, + windSpeedLowerBound, + windSpeedUpperBound ); + + this.bearingDegreeEpsilon = 0.001; + this.speedKnotsEpsilon = 0.001; + } + + + /** + * Tests if the baseline wind generated it accurate. + */ + @Test + public void generateBaselineWindTest() { + + Wind wind = windGenerator.generateBaselineWind(); + + assertEquals(windBaselineSpeed, wind.getWindSpeed(), speedKnotsEpsilon); + assertEquals(windBaselineBearing.degrees(), wind.getWindDirection().degrees(), bearingDegreeEpsilon); + + } + + /** + * Tests if the random wind generated is inside the bounds. + */ + @Test + public void generateRandomWindTest() { + + int randomWindCount = 1000; + + for (int i = 0; i < randomWindCount; i++) { + + Wind wind = windGenerator.generateRandomWind(); + + assertTrue(wind.getWindSpeed() >= windSpeedLowerBound); + assertTrue(wind.getWindSpeed() <= windSpeedUpperBound); + + assertTrue(wind.getWindDirection().degrees() >= windBearingLowerBound.degrees()); + assertTrue(wind.getWindDirection().degrees() <= windBearingUpperBound.degrees()); + + } + + } + + + /** + * Tests if the next wind generated is inside the bounds. + */ + @Test + public void generateNextWindTest() { + + Wind wind = windGenerator.generateBaselineWind(); + + int randomWindCount = 1000; + + for (int i = 0; i < randomWindCount; i++) { + + wind = windGenerator.generateNextWind(wind); + + assertTrue(wind.getWindSpeed() >= windSpeedLowerBound); + assertTrue(wind.getWindSpeed() <= windSpeedUpperBound); + + assertTrue(wind.getWindDirection().degrees() >= windBearingLowerBound.degrees()); + assertTrue(wind.getWindDirection().degrees() <= windBearingUpperBound.degrees()); + + } + + } +}