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 @@ - + + + + +