Merge branch 'master' into MapWindRotation
# Conflicts: # racevisionGame/src/main/java/mock/app/Event.java # racevisionGame/src/main/java/mock/model/MockRace.java # racevisionGame/src/main/java/visualiser/app/VisualiserInput.java # racevisionGame/src/main/java/visualiser/model/VisualiserRace.java
This commit is contained in:
@@ -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> 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> 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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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,11 @@ 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.
|
||||
* @param race The race to observe.
|
||||
*/
|
||||
private void initialiseArrow() {
|
||||
arrowPane.getChildren().add(arrow);
|
||||
private void initialiseArrow(VisualiserRace race) {
|
||||
arrowController.setWindProperty(race.windProperty());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ public class App extends Application {
|
||||
|
||||
/**
|
||||
* Method that displays the visualiser, starting with the title screen.
|
||||
* @param stage the stage to be displayed
|
||||
* @throws Exception if something wrong with title screen
|
||||
* @param stage the stage to be displayed.
|
||||
* @throws Exception if something wrong with title screen.
|
||||
*/
|
||||
public void start(Stage stage) throws Exception {
|
||||
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
|
||||
|
||||
@@ -201,12 +201,12 @@ public class VisualiserInput implements Runnable {
|
||||
|
||||
//Heartbeat.
|
||||
case HEARTBEAT: {
|
||||
Heartbeat heartbeat = (Heartbeat) message;
|
||||
HeartBeat heartBeat = (HeartBeat) message;
|
||||
|
||||
//Check that the heartbeat number is greater than the previous value, and then set the last heartbeat time.
|
||||
if (heartbeat.getSequenceNumber() > this.lastHeartbeatSequenceNum) {
|
||||
if (heartBeat.getSequenceNumber() > this.lastHeartbeatSequenceNum) {
|
||||
lastHeartbeatTime = System.currentTimeMillis();
|
||||
lastHeartbeatSequenceNum = heartbeat.getSequenceNumber();
|
||||
lastHeartbeatSequenceNum = heartBeat.getSequenceNumber();
|
||||
//System.out.println("HeartBeat Message! " + lastHeartbeatSequenceNum);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package visualiser.gameController;
|
||||
|
||||
import network.BinaryMessageEncoder;
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.MessageEncoders.RaceVisionByteEncoder;
|
||||
import network.Messages.BoatAction;
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
@@ -12,6 +13,8 @@ import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Basic service for sending key presses to game server
|
||||
@@ -47,22 +50,27 @@ public class ControllerClient {
|
||||
* @throws IOException if socket write fails
|
||||
*/
|
||||
public void sendKey(ControlKey key) throws IOException {
|
||||
int protocolCode = key.getProtocolCode();
|
||||
if(protocolCode > -1) {
|
||||
BoatActionEnum protocolCode = key.getProtocolCode();
|
||||
if(protocolCode != BoatActionEnum.NOT_A_STATUS) {
|
||||
|
||||
byte[] bytes = new byte[4];
|
||||
ByteBuffer.wrap(bytes).putInt(key.getProtocolCode());
|
||||
BoatActionEnum boatActionEnum = BoatActionEnum.fromByte(bytes[3]);
|
||||
BoatAction boatAction = new BoatAction(protocolCode);
|
||||
|
||||
BoatAction boatAction = new BoatAction(boatActionEnum);
|
||||
//Encode BoatAction.
|
||||
try {
|
||||
byte[] encodedBoatAction = RaceVisionByteEncoder.encode(boatAction);
|
||||
|
||||
byte[] encodedBoatAction = RaceVisionByteEncoder.boatActionMessage(boatAction);
|
||||
BinaryMessageEncoder binaryMessage = new BinaryMessageEncoder(MessageType.BOATACTION, System.currentTimeMillis(), 0,
|
||||
(short) encodedBoatAction.length, encodedBoatAction);
|
||||
|
||||
System.out.println("Sending out key: " + protocolCode);
|
||||
outputStream.write(binaryMessage.getFullMessage());
|
||||
|
||||
} catch (InvalidMessageException e) {
|
||||
Logger.getGlobal().log(Level.WARNING, "Could not encode BoatAction: " + boatAction, e);
|
||||
|
||||
}
|
||||
|
||||
BinaryMessageEncoder binaryMessage = new BinaryMessageEncoder(MessageType.BOATACTION, System.currentTimeMillis(), 0,
|
||||
(short) encodedBoatAction.length, encodedBoatAction);
|
||||
|
||||
System.out.println("Sending out key: " + boatActionEnum);
|
||||
outputStream.write(binaryMessage.getFullMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
package visualiser.gameController;
|
||||
|
||||
import mock.model.RaceLogic;
|
||||
import network.BinaryMessageDecoder;
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.MessageDecoders.BoatActionDecoder;
|
||||
import network.Messages.BoatAction;
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
import visualiser.gameController.Keys.ControlKey;
|
||||
import visualiser.gameController.Keys.KeyFactory;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.util.logging.Level;
|
||||
import java.util.Observable;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Service for dispatching key press data to race from client
|
||||
*/
|
||||
public class ControllerServer implements Runnable {
|
||||
public class ControllerServer extends Observable implements Runnable {
|
||||
/**
|
||||
* Socket to client
|
||||
*/
|
||||
@@ -22,13 +26,23 @@ public class ControllerServer implements Runnable {
|
||||
* Wrapper for input from client
|
||||
*/
|
||||
private DataInputStream inputStream;
|
||||
/**
|
||||
* Last received boat action
|
||||
*/
|
||||
private BoatActionEnum action;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private RaceLogic rc;
|
||||
|
||||
/**
|
||||
* Initialise server-side controller with live client socket
|
||||
* @param socket to client
|
||||
*/
|
||||
public ControllerServer(Socket socket) {
|
||||
public ControllerServer(Socket socket, RaceLogic rc) {
|
||||
this.socket = socket;
|
||||
this.rc = rc;
|
||||
this.addObserver(rc);
|
||||
try {
|
||||
this.inputStream = new DataInputStream(this.socket.getInputStream());
|
||||
} catch (IOException e) {
|
||||
@@ -36,6 +50,10 @@ public class ControllerServer implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public BoatActionEnum getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for controller key input from client and loop.
|
||||
*/
|
||||
@@ -45,11 +63,26 @@ public class ControllerServer implements Runnable {
|
||||
byte[] message = new byte[20];
|
||||
try {
|
||||
if (inputStream.available() > 0) {
|
||||
|
||||
inputStream.read(message);
|
||||
|
||||
BinaryMessageDecoder encodedMessage = new BinaryMessageDecoder(message);
|
||||
BoatActionDecoder boatActionDecoder = new BoatActionDecoder(encodedMessage.getMessageBody());
|
||||
BoatActionEnum decodedMessage = boatActionDecoder.getBoatAction();
|
||||
System.out.println("Received key: " + decodedMessage);
|
||||
BoatActionDecoder boatActionDecoder = new BoatActionDecoder();
|
||||
|
||||
try {
|
||||
boatActionDecoder.decode(encodedMessage.getMessageBody());
|
||||
BoatAction boatAction = boatActionDecoder.getMessage();
|
||||
action = boatAction.getBoatAction();
|
||||
|
||||
// Notify observers of most recent action
|
||||
this.notifyObservers();
|
||||
this.setChanged();
|
||||
|
||||
} catch (InvalidMessageException e) {
|
||||
Logger.getGlobal().log(Level.WARNING, "Could not decode BoatAction message.", e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package visualiser.gameController.Keys;
|
||||
|
||||
import javafx.scene.input.KeyCode;
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
|
||||
/**
|
||||
* Key for the controller, part of the abstract factory KeyFactory
|
||||
@@ -8,14 +9,14 @@ import javafx.scene.input.KeyCode;
|
||||
public abstract class ControlKey {
|
||||
|
||||
private String name;
|
||||
protected int protocolCode;
|
||||
protected BoatActionEnum protocolCode;
|
||||
|
||||
/**
|
||||
* Constructor for key state with specified protocol code
|
||||
* @param name of action
|
||||
* @param protocolCode -1 if not sent
|
||||
* @param protocolCode NOT_A_STATUS if not sent
|
||||
*/
|
||||
public ControlKey(String name, int protocolCode) {
|
||||
public ControlKey(String name, BoatActionEnum protocolCode) {
|
||||
this.name = name;
|
||||
this.protocolCode = protocolCode;
|
||||
}
|
||||
@@ -26,10 +27,10 @@ public abstract class ControlKey {
|
||||
*/
|
||||
public ControlKey(String name){
|
||||
this.name = name;
|
||||
this.protocolCode = -1;
|
||||
this.protocolCode = BoatActionEnum.NOT_A_STATUS;
|
||||
}
|
||||
|
||||
public int getProtocolCode() {
|
||||
public BoatActionEnum getProtocolCode() {
|
||||
return protocolCode;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package visualiser.gameController.Keys;
|
||||
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
|
||||
/**
|
||||
* Key to send downwind packet to server
|
||||
*/
|
||||
@@ -11,7 +13,7 @@ public class DownWindKey extends ControlKey {
|
||||
*
|
||||
*/
|
||||
public DownWindKey(String name) {
|
||||
super(name, 6);
|
||||
super(name, BoatActionEnum.DOWNWIND);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,8 +27,8 @@ public class KeyFactory {
|
||||
keyState.put("SPACE", new VMGKey("VMG"));
|
||||
keyState.put("SHIFT", new SailsToggleKey("Toggle Sails"));
|
||||
keyState.put("ENTER", new TackGybeKey("Tack/Gybe"));
|
||||
keyState.put("PAGE_UP", new UpWindKey("Upwind"));
|
||||
keyState.put("PAGE_DOWN", new DownWindKey("Downwind"));
|
||||
keyState.put("UP", new UpWindKey("Upwind"));
|
||||
keyState.put("DOWN", new DownWindKey("Downwind"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package visualiser.gameController.Keys;
|
||||
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
|
||||
/**
|
||||
* Key to toggle the sails
|
||||
*/
|
||||
@@ -11,7 +13,7 @@ public class SailsToggleKey extends ControlKey {
|
||||
*
|
||||
*/
|
||||
public SailsToggleKey(String name) {
|
||||
super(name, 2);
|
||||
super(name, BoatActionEnum.SAILS_IN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -19,7 +21,11 @@ public class SailsToggleKey extends ControlKey {
|
||||
*/
|
||||
@Override
|
||||
public void onAction() {
|
||||
protocolCode = protocolCode == 2? 3 : 2;
|
||||
if(protocolCode == BoatActionEnum.SAILS_IN) {
|
||||
protocolCode = BoatActionEnum.SAILS_OUT;
|
||||
} else {
|
||||
protocolCode = BoatActionEnum.SAILS_IN;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package visualiser.gameController.Keys;
|
||||
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
|
||||
/**
|
||||
* key to toggle between tacking and gybing
|
||||
*/
|
||||
@@ -11,7 +13,7 @@ public class TackGybeKey extends ControlKey {
|
||||
*
|
||||
*/
|
||||
public TackGybeKey(String name) {
|
||||
super(name, 4);
|
||||
super(name, BoatActionEnum.TACK_GYBE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package visualiser.gameController.Keys;
|
||||
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
|
||||
/**
|
||||
* Key to go upwind
|
||||
*/
|
||||
@@ -11,7 +13,7 @@ public class UpWindKey extends ControlKey {
|
||||
*
|
||||
*/
|
||||
public UpWindKey(String name) {
|
||||
super(name, 5);
|
||||
super(name, BoatActionEnum.UPWIND);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package visualiser.gameController.Keys;
|
||||
|
||||
import javafx.scene.input.KeyCode;
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
|
||||
/**
|
||||
* Key to trigger auto VMG
|
||||
@@ -13,7 +14,7 @@ public class VMGKey extends ControlKey{
|
||||
* @param name name of the key
|
||||
*/
|
||||
public VMGKey(String name) {
|
||||
super(name, 1);
|
||||
super(name, BoatActionEnum.AUTO_PILOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
@@ -54,22 +47,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 +361,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.
|
||||
*/
|
||||
@@ -440,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();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -459,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.
|
||||
@@ -487,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);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -511,18 +462,6 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
||||
//Marks.
|
||||
drawMarks();
|
||||
|
||||
//Wind arrow. This rotates the wind arrow node.
|
||||
displayWindArrow(this.visualiserRace.getWindDirection().degrees());
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draws the race boundary image onto the canvas.
|
||||
* See {@link #background}.
|
||||
*/
|
||||
private void drawBoundary() {
|
||||
gc.drawImage(this.background, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import shared.dataInput.RegattaDataSource;
|
||||
import shared.model.*;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -27,7 +26,7 @@ import java.util.Map;
|
||||
* Has a course, boats, boundaries, etc...
|
||||
* Observes LatestMessages and updates its state based on new messages.
|
||||
*/
|
||||
public class VisualiserRace extends Race {
|
||||
public class VisualiserRace extends Race implements Runnable {
|
||||
|
||||
|
||||
/**
|
||||
@@ -198,24 +197,24 @@ public class VisualiserRace extends Race {
|
||||
if (boatLocation != null && boatStatus != null) {
|
||||
|
||||
//Get the new position.
|
||||
double latitude = boatLocation.getLatitudeDouble();
|
||||
double longitude = boatLocation.getLongitudeDouble();
|
||||
double latitude = boatLocation.getLatitude();
|
||||
double longitude = boatLocation.getLongitude();
|
||||
GPSCoordinate gpsCoordinate = new GPSCoordinate(latitude, longitude);
|
||||
|
||||
boat.setCurrentPosition(gpsCoordinate);
|
||||
|
||||
//Bearing.
|
||||
boat.setBearing(Bearing.fromDegrees(boatLocation.getHeadingDegrees()));
|
||||
boat.setBearing(boatLocation.getHeading());
|
||||
|
||||
//Time until next mark.
|
||||
boat.setEstimatedTimeAtNextMark(raceClock.getLocalTime(boatStatus.getEstTimeAtNextMark()));
|
||||
|
||||
//Speed.
|
||||
boat.setCurrentSpeed(boatLocation.getBoatSOG() / Constants.KnotsToMMPerSecond);
|
||||
boat.setCurrentSpeed(boatLocation.getBoatSpeedKnots());
|
||||
|
||||
|
||||
//Boat status.
|
||||
BoatStatusEnum newBoatStatusEnum = BoatStatusEnum.fromByte(boatStatus.getBoatStatus());
|
||||
BoatStatusEnum newBoatStatusEnum = boatStatus.getBoatStatus();
|
||||
|
||||
//If we are changing from non-racing to racing, we need to initialise boat with their time at last mark.
|
||||
if ((boat.getStatus() != BoatStatusEnum.RACING) && (newBoatStatusEnum == BoatStatusEnum.RACING)) {
|
||||
@@ -292,8 +291,8 @@ public class VisualiserRace extends Race {
|
||||
if (boatLocation != null) {
|
||||
|
||||
//We only update the boat's position.
|
||||
double latitude = boatLocation.getLatitudeDouble();
|
||||
double longitude = boatLocation.getLongitudeDouble();
|
||||
double latitude = boatLocation.getLatitude();
|
||||
double longitude = boatLocation.getLongitude();
|
||||
GPSCoordinate gpsCoordinate = new GPSCoordinate(latitude, longitude);
|
||||
|
||||
mark.setPosition(gpsCoordinate);
|
||||
@@ -309,13 +308,12 @@ public class VisualiserRace extends Race {
|
||||
private void updateRaceStatus(RaceStatus raceStatus) {
|
||||
if (raceStatus != null) {
|
||||
//Race status enum.
|
||||
this.raceStatusEnum = RaceStatusEnum.fromByte(raceStatus.getRaceStatus());
|
||||
this.raceStatusEnum = raceStatus.getRaceStatus();
|
||||
|
||||
//Wind bearing.
|
||||
this.windDirection.setDegrees(raceStatus.getScaledWindDirection());
|
||||
|
||||
//Wind speed.
|
||||
this.windSpeed = raceStatus.getWindSpeedKnots();
|
||||
//Wind.
|
||||
this.setWind(
|
||||
raceStatus.getWindDirection(),
|
||||
raceStatus.getWindSpeed() );
|
||||
|
||||
//Current race time.
|
||||
this.raceClock.setUTCTime(raceStatus.getCurrentTime());
|
||||
|
||||
Reference in New Issue
Block a user