Merge branch 'master' into storyB-)

# Conflicts:
#	racevisionGame/src/main/java/shared/model/Constants.java
main
Connor Taylor-Brown 8 years ago
commit fbd124e602

@ -21,7 +21,7 @@ public class App extends Application {
public void start(Stage primaryStage) {
try {
//TODO should read a configuration file to configure server?
Event raceEvent = new Event(false);
Event raceEvent = new Event(false, 0);
} catch (Exception e) {

@ -18,6 +18,7 @@ import shared.exceptions.InvalidRegattaDataException;
import shared.exceptions.XMLReaderException;
import shared.model.Bearing;
import shared.model.Constants;
import shared.xml.XMLUtilities;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
@ -70,6 +71,8 @@ public class Event {
private Thread connectionThread;
private int mapIndex;
@ -79,11 +82,32 @@ public class Event {
* @param singlePlayer Whether or not to create a single player event.
* @throws EventConstructionException Thrown if we cannot create an Event for any reason.
*/
public Event(boolean singlePlayer) throws EventConstructionException {
public Event(boolean singlePlayer, int mapIndex) throws EventConstructionException {
String raceXMLFile = "mock/mockXML/raceTest.xml";
// System.out.println(XMLUtilities.validateXML(this.getClass().getClassLoader().getResource("mock/mockXML/iMapLayout.xml").toString()
// , this.getClass().getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd")));
this.mapIndex = mapIndex;
String raceXMLFile;
String boatsXMLFile = "mock/mockXML/boatTest.xml";
String regattaXMLFile = "mock/mockXML/regattaTest.xml";
switch (mapIndex){
case 0:raceXMLFile = "mock/mockXML/raceSixPlayers.xml";
break;
case 1:raceXMLFile = "mock/mockXML/oMapLayout.xml";
break;
case 2: raceXMLFile = "mock/mockXML/iMapLayout.xml";
break;
case 3: raceXMLFile = "mock/mockXML/mMapLayout.xml";
break;
case 4:
raceXMLFile = "mock/mockXML/raceTutorial.xml";
boatsXMLFile = "mock/mockXML/boatTutorial.xml";
regattaXMLFile = "mock/mockXML/regattaTutorial.xml";
break;
default: raceXMLFile = "mock/mockXML/raceSixPlayers.xml";
}
if (singlePlayer) {
raceXMLFile = "mock/mockXML/raceSinglePlayer.xml";
@ -95,7 +119,9 @@ public class Event {
//this.raceXML = RaceXMLCreator.alterRaceToWind(raceXMLFile, 90);
this.raceXML = XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8);
this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8));
if(mapIndex==4){
this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8), 1000, 5000);
}
this.boatXML = XMLReader.readXMLFileToString(boatsXMLFile, StandardCharsets.UTF_8);
this.regattaXML = XMLReader.readXMLFileToString(regattaXMLFile, StandardCharsets.UTF_8);
@ -178,9 +204,15 @@ public class Event {
* @return String containing edited xml
*/
public static String setRaceXMLAtCurrentTimeToNow(String raceXML) {
return setRaceXMLAtCurrentTimeToNow(raceXML, Constants.RacePreStartTime, Constants.RacePreparatoryTime);
}
public static String setRaceXMLAtCurrentTimeToNow(String raceXML, long racePreStartTime, long racePreparatoryTime){
//The start time is current time + 4 minutes. prestart is 3 minutes, and we add another minute.
long millisecondsToAdd = Constants.RacePreStartTime + (1 * 60 * 1000);
long millisecondsToAdd = racePreStartTime + racePreparatoryTime;
long secondsToAdd = millisecondsToAdd / 1000;
//Scale the time using our time scalar.
secondsToAdd = secondsToAdd / Constants.RaceTimeScale;
@ -190,7 +222,6 @@ public class Event {
raceXML = raceXML.replace("RACE_CREATION_TIME", dateFormat.format(creationTime));
raceXML = raceXML.replace("RACE_START_TIME", dateFormat.format(creationTime.plusSeconds(secondsToAdd)));
return raceXML;
}

@ -42,6 +42,7 @@ public class SourceIdAllocator {
}
List<Integer> allocatedIDs = mockRace.getRaceDataSource().getParticipants();
List<Integer> allIDs = new ArrayList<>(mockRace.getBoatDataSource().getBoats().keySet());
//Get list of unallocated ids.

@ -74,7 +74,7 @@ public class RaceXMLCreator {
* @throws SAXException error in schema file
* @throws ParserConfigurationException error in parsing the schema file
*/
public static String alterRaceToWind(String s, double degrees) throws XMLReaderException, InvalidRaceDataException, JAXBException, IOException, SAXException, ParserConfigurationException {
public static String alterRaceToWind(String s, double degrees, boolean tutorial) throws XMLReaderException, InvalidRaceDataException, JAXBException, IOException, SAXException, ParserConfigurationException {
RaceXMLReader reader = new RaceXMLReader(s, XMLFileType.ResourcePath);
XMLRace race = XMLUtilities.xmlToClass(
@ -82,7 +82,11 @@ public class RaceXMLCreator {
RaceXMLCreator.class.getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd"),
XMLRace.class);
if(tutorial){
setRaceXMLAtCurrentTimeToNow(race, 1000l, 5000l);
} else {
setRaceXMLAtCurrentTimeToNow(race);
}
double raceOriginalBearing = getLineAngle(getLeewardGate(reader).getMark1Position(), getWindwardGate(reader).getMark1Position());
@ -93,6 +97,7 @@ public class RaceXMLCreator {
return XMLUtilities.classToXML(race);
}
/**
* Rotate the features in a race such as the boundary, and the marks.
* @param race the race to alter
@ -180,14 +185,11 @@ public class RaceXMLCreator {
}
/**
* Sets the xml description of the race to show the race was created now, and starts in 4 minutes
* @param raceXML The race.xml contents.
*/
public static void setRaceXMLAtCurrentTimeToNow(XMLRace raceXML) {
public static void setRaceXMLAtCurrentTimeToNow(XMLRace raceXML, long racePrestartTime, long racePreparatoryTime){
//The start time is current time + 4 minutes. prestart is 3 minutes, and we add another minute.
long millisecondsToAdd = Constants.RacePreStartTime + (1 * 60 * 1000);
long millisecondsToAdd = racePrestartTime + racePreparatoryTime;
long secondsToAdd = millisecondsToAdd / 1000;
//Scale the time using our time scalar.
secondsToAdd = secondsToAdd / Constants.RaceTimeScale;
@ -198,4 +200,13 @@ public class RaceXMLCreator {
raceXML.getRaceStartTime().setTime(dateFormat.format(creationTime.plusSeconds(secondsToAdd)));
}
/**
* Sets the xml description of the race to show the race was created now, and starts in 4 minutes
* @param raceXML The race.xml contents.
*/
public static void setRaceXMLAtCurrentTimeToNow(XMLRace raceXML) {
setRaceXMLAtCurrentTimeToNow(raceXML, Constants.RacePreStartTime, Constants.RacePreparatoryTime);
}
}

@ -5,7 +5,6 @@ package shared.model;
* Created by Erika on 19-Mar-17.
*/
public class Constants {
/**
* Multiply by this factor to convert nautical miles to meters.
* <br>
@ -15,8 +14,6 @@ public class Constants {
*/
public static final int NMToMetersConversion = 1852;
/**
* Multiply by this factor to convert Knots to millimeters per second.
* <br>
@ -26,8 +23,6 @@ public class Constants {
*/
public static final double KnotsToMMPerSecond = 514.444;
/**
* The scale factor of the race.
* Frame periods are multiplied by this to get the amount of time a single frame represents.
@ -38,20 +33,13 @@ public class Constants {
/**
* The race pre-start time, in milliseconds. 3 minutes.
*/
//
// TODO: CHANGE BACK TO 3 MINUTES BEFORE MERGING TO MASTER
public static final long RacePreStartTime = 30000; //3 * 60 * 1000;
public static final long RacePreStartTime = 3 * 60 * 1000;
/**
* The race preparatory time, in milliseconds. 1 minute.
*/
public static final long RacePreparatoryTime = 1 * 60 * 1000;
/**
* The number of milliseconds in one hour.
* <br>
@ -69,7 +57,4 @@ public class Constants {
* Divide by this factor to convert hours to seconds.
*/
public static long OneHourSeconds = 1 * 60 * 60;
}

@ -1,29 +1,28 @@
package visualiser.Controllers;
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
import javafx.animation.AnimationTimer;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.SplitPane;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.shape.Box;
import javafx.scene.shape.Mesh;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.Shape3D;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import mock.app.Event;
import org.xml.sax.SAXException;
import mock.exceptions.EventConstructionException;
import visualiser.model.View3D;
import shared.exceptions.InvalidBoatDataException;
import shared.exceptions.InvalidRaceDataException;
import shared.exceptions.InvalidRegattaDataException;
import shared.exceptions.XMLReaderException;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.net.Socket;
import java.net.URL;
import java.util.Optional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -34,72 +33,48 @@ import java.util.logging.Logger;
public class HostController extends Controller {
// @FXML
// TextField gameNameField;
//
// @FXML
// TextField hostNameField;
@FXML
private ImageView imageView;
TextField gameNameField;
@FXML
AnchorPane hostWrapper;
TextField hostNameField;
@FXML
AnchorPane imagePane;
AnchorPane hostWrapper;
@FXML
SplitPane splitPane;
Button previousButton;
@FXML
AnchorPane specPane;
Button nextButton;
@FXML
GridPane playerContainer;
ImageView mapImage;
private Event game;
private View3D view3D;
@Override
public void initialize(URL location, ResourceBundle resources) {
ObservableList<Shape3D> shapes = FXCollections.observableArrayList();
view3D = new View3D();
view3D.setItems(shapes);
playerContainer.add(view3D, 0,0);
URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl");
StlMeshImporter importer = new StlMeshImporter();
importer.read(asset);
MeshView mesh = new MeshView(importer.getImport());
shapes.add(mesh);
private ArrayList<Image> listOfMaps;
private int currentMapIndex = 0;
view3D.setPivot(mesh);
view3D.setDistance(50);
view3D.setYaw(45);
view3D.setPitch(20);
Rotate rotation = new Rotate(0, Rotate.Y_AXIS);
mesh.getTransforms().addAll(rotation, new Rotate(-90, Rotate.X_AXIS));
AnimationTimer rotate = new AnimationTimer() {
@Override
public void handle(long now) {
rotation.setAngle(rotation.getAngle() + 0.1);
}
};
rotate.start();
public void initialize(URL location, ResourceBundle resources){
Image ac35Map = new Image(getClass().getClassLoader().getResourceAsStream("images/AC35_Racecourse_MAP.png"));
Image oMap = new Image(getClass().getClassLoader().getResourceAsStream("images/oMapLayout.png"));
Image iMap = new Image(getClass().getClassLoader().getResourceAsStream("images/iMapLayout.png"));
Image mMap = new Image(getClass().getClassLoader().getResourceAsStream("images/mMapLayout.png"));
listOfMaps = new ArrayList(Arrays.asList(ac35Map, oMap, iMap, mMap));
mapImage.setImage(listOfMaps.get(currentMapIndex));
}
/**
* Hosts a game
* @throws IOException if socket cannot be connected to
*/
public void hostGamePressed() {
public void hostGamePressed() throws IOException{
try {
this.game = new Event(false);
this.game = new Event(false, currentMapIndex);
connectSocket("localhost", 4942);
} catch (EventConstructionException e) {
Logger.getGlobal().log(Level.SEVERE, "Could not create Event.", e);
@ -132,10 +107,7 @@ public class HostController extends Controller {
* Hosts a game.
*/
public void hostGame(){
splitPane.setResizableWithParent(specPane, false);
splitPane.lookupAll(".split-pane-divider").stream().forEach(div -> div.setMouseTransparent(true));
imageView.fitWidthProperty().bind(imagePane.widthProperty());
imageView.fitHeightProperty().bind(imagePane.heightProperty());
mapImage.fitWidthProperty().bind(((Stage) mapImage.getScene().getWindow()).widthProperty().multiply(0.6));
hostWrapper.setVisible(true);
}
@ -143,23 +115,31 @@ public class HostController extends Controller {
* Menu button pressed. Prompt alert then return to menu
*/
public void menuBtnPressed(){
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Quitting race");
alert.setContentText("Do you wish to quit the race?");
alert.setHeaderText("You are about to quit the race");
Optional<ButtonType> result = alert.showAndWait();
if(result.get() == ButtonType.OK){
hostWrapper.setVisible(false);
parent.enterTitle();
}
public void nextImage(){
increaseIndex();
mapImage.setImage(listOfMaps.get(currentMapIndex));
}
/**
* Start button pressed. Currently only prints out start
*/
public void startBtnPressed(){
//System.out.println("Should start the race. This button is only visible for the host");
hostGamePressed();
public void previousImage(){
decreaseIndex();
mapImage.setImage(listOfMaps.get(currentMapIndex));
}
private void increaseIndex(){
currentMapIndex = (currentMapIndex + 1)%listOfMaps.size();
}
private void decreaseIndex(){
currentMapIndex = ((((currentMapIndex - 1)%listOfMaps.size())+listOfMaps.size())%listOfMaps.size());
}
public void setGameType(int gameType){
this.currentMapIndex = gameType;
}
public int getGameType(){ return this.currentMapIndex; }
}

@ -86,6 +86,20 @@ public class MainController extends Controller {
*/
public void startCss(){titleController.setDayMode();}
/**
* host controller host a game
* @throws IOException throws exception
*/
public void beginGame() throws IOException {
hostController.hostGamePressed();
}
public void setGameType(int gameType){
hostController.setGameType(gameType);
}
public int getGameType(){ return hostController.getGameType(); }
/**
* Main Controller for the applications will house the menu and the displayed pane.
*

@ -1,6 +1,6 @@
package visualiser.Controllers;
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.collections.FXCollections;
@ -14,20 +14,27 @@ import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.Sphere;
import javafx.scene.transform.Translate;
import javafx.util.Callback;
import network.Messages.Enums.RaceStatusEnum;
import shared.dataInput.RaceDataSource;
import shared.model.Leg;
import shared.model.Mark;
import visualiser.app.App;
import visualiser.enums.TutorialState;
import visualiser.gameController.ControllerClient;
import visualiser.gameController.Keys.ControlKey;
import visualiser.layout.Subject3D;
import visualiser.layout.View3D;
import visualiser.model.*;
import visualiser.utils.GPSConverter;
import java.io.IOException;
import java.net.URL;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -38,53 +45,46 @@ import static visualiser.app.App.keyFactory;
* Controller used to display a running race.
*/
public class RaceController extends Controller {
/**
* The race object which describes the currently occurring race.
*/
private VisualiserRaceEvent visualiserRace;
/**
* Service for sending keystrokes to server
*/
private ControllerClient controllerClient;
private boolean isHost;
/**
* The canvas that draws the race.
*/
private ResizableRaceCanvas raceCanvas;
private TutorialState currentState;
private ArrayList<TutorialState> tutorialStates;
private boolean isTutorial = false;
private String keyToPress;
/**
* The sparkline graph.
*/
private Sparkline sparkline;
/**
* state of the info table
*/
private boolean infoTableShow;
private View3D view3D;
private ObservableList<Subject3D> viewSubjects;
/**
* The arrow controller.
*/
@FXML private ArrowController arrowController;
@FXML private GridPane canvasBase;
@FXML private SplitPane racePane;
@FXML private SplitPane race;
@FXML private Label tutorialText;
/**
* 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.
@ -100,9 +100,6 @@ public class RaceController extends Controller {
@FXML private TableColumn<VisualiserBoat, Leg> boatMarkColumn;
@FXML private TableColumn<VisualiserBoat, Number> boatSpeedColumn;
@FXML private LineChart<Number, Number> sparklineChart;
@FXML private AnchorPane annotationPane;
/**
* Ctor.
@ -112,11 +109,10 @@ public class RaceController extends Controller {
@Override
public void initialize(URL location, ResourceBundle resources) {
// KeyFactory keyFactory = KeyFactory.getFactory();
infoTableShow = true;
// Initialise keyboard handler
race.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
racePane.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
String codeString = event.getCode().toString();
if (codeString.equals("TAB")){toggleTable();}
@ -125,13 +121,27 @@ public class RaceController extends Controller {
if(controlKey != null) {
try {
controlKey.onAction(); // Change key state if applicable
//Check if current race is a tutorial
if (isTutorial){
//Check if current tutorial state has the same boat protocol code as key press
if (controlKey.getProtocolCode().equals(currentState.getAction())){
//Update tutorial
checkTutorialState();
}
}
controllerClient.sendKey(controlKey);
event.consume();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Logger.getGlobal().log(Level.WARNING, "RaceController was interrupted on thread: " + Thread.currentThread() + "while sending: " + controlKey, e);
} catch (Exception e) {
e.printStackTrace();
}
}
if(event.getCode() == KeyCode.ESCAPE) {
try {
@ -142,7 +152,7 @@ public class RaceController extends Controller {
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK) {
parent.endEvent();
race.setVisible(false);
racePane.setVisible(false);
App.app.showMainStage(App.getStage());
}
} else {
@ -151,7 +161,7 @@ public class RaceController extends Controller {
alert.setContentText("Do you wish to quit the race?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK) {
race.setVisible(false);
racePane.setVisible(false);
App.app.showMainStage(App.getStage());
}
}
@ -175,14 +185,10 @@ public class RaceController extends Controller {
//Information table.
initialiseInfoTable(this.visualiserRace);
//Sparkline.
initialiseSparkline(this.visualiserRace);
//Arrow.
initialiseArrow(this.visualiserRace);
//Race canvas.
initialiseRaceCanvas(this.visualiserRace);
initialiseView3D(this.visualiserRace);
//Race timezone label.
initialiseRaceTimezoneLabel(this.visualiserRace);
@ -190,11 +196,106 @@ public class RaceController extends Controller {
//Race clock.
initialiseRaceClock(this.visualiserRace);
//Start the race animation timer.
raceTimer();
}
private void initialiseView3D(VisualiserRaceEvent race) {
viewSubjects = FXCollections.observableArrayList();
// Import boat mesh
URL asset = HostController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl");
StlMeshImporter importer = new StlMeshImporter();
importer.read(asset);
// Configure camera angles and control
view3D = new View3D();
view3D.setDistance(1050);
view3D.setYaw(0);
view3D.setPitch(60);
view3D.enableTracking();
//newPane.getChildren().add(view3D);
canvasBase.add(view3D, 0, 0);
// Set up projection from GPS to view
RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource();
final GPSConverter gpsConverter = new GPSConverter(raceData, 450, 450);
view3D.setItems(viewSubjects);
// Position and add each mark to view
for(Mark mark: race.getVisualiserRaceState().getMarks()) {
Subject3D subject = new Subject3D(new Sphere(2));
subject.setX(gpsConverter.convertGPS(mark.getPosition()).getX());
subject.setZ(gpsConverter.convertGPS(mark.getPosition()).getY());
viewSubjects.add(subject);
}
// Position and add each boat to view
for(VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) {
MeshView mesh = new MeshView(importer.getImport());
Subject3D subject = new Subject3D(mesh);
viewSubjects.add(subject);
// Track this boat's movement with the new subject
AnimationTimer trackBoat = new AnimationTimer() {
@Override
public void handle(long now) {
subject.setHeading(boat.getBearing().degrees());
subject.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
subject.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
}
};
trackBoat.start();
}
// Fix initial bird's-eye position
view3D.updatePivot(new Translate(250, 0, 210));
// Bind zooming to scrolling
view3D.setOnScroll(e -> {
view3D.updateDistance(e.getDeltaY());
});
// Bind zooming to keypress (Z/X default)
racePane.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
ControlKey key = keyFactory.getKey(e.getCode().toString());
if(key != null) {
switch (key.toString()) {
case "Zoom In":
//Check if race is a tutorial
if (isTutorial) {
//Check if the current tutorial state is zoom-in
if (currentState.equals(TutorialState.ZOOMIN)) {
try {
//Update tutorial
checkTutorialState();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
view3D.updateDistance(-10);
break;
case "Zoom Out":
//Check if race is a tutorial
if(isTutorial) {
//Check if current tutorial state is zoom-out
if (currentState.equals(TutorialState.ZOOMOUT)) {
try {
//Update tutorial
checkTutorialState();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
view3D.updateDistance(10);
break;
}
}
});
}
/**
@ -337,42 +438,8 @@ public class RaceController extends Controller {
}
/**
* Initialises the {@link Sparkline}, and listens to a specified {@link VisualiserRaceEvent}.
* @param race The race to listen to.
*/
private void initialiseSparkline(VisualiserRaceEvent race) {
//The race.getBoats() we are passing in is sorted by position in race inside the race class.
this.sparkline = new Sparkline(this.visualiserRace.getVisualiserRaceState(), this.sparklineChart);
}
/**
* Initialises the {@link ResizableRaceCanvas}, provides the race to read data from.
* @param race Race to read data from.
*/
private void initialiseRaceCanvas(VisualiserRaceEvent race) {
//Create canvas.
raceCanvas = new ResizableRaceCanvas(race.getVisualiserRaceState());
//Set properties.
raceCanvas.setMouseTransparent(true);
raceCanvas.widthProperty().bind(canvasBase.widthProperty());
raceCanvas.heightProperty().bind(canvasBase.heightProperty());
//Draw it and show it.
raceCanvas.draw();
raceCanvas.setVisible(true);
//Add to scene.
canvasBase.getChildren().add(0, raceCanvas);
}
/**
* Intialises the race time zone label with the race's time zone.
* Initialises the race time zone label with the race's time zone.
* @param race The race to get time zone from.
*/
private void initialiseRaceTimezoneLabel(VisualiserRaceEvent race) {
@ -407,14 +474,29 @@ public class RaceController extends Controller {
this.controllerClient = controllerClient;
this.isHost = isHost;
initialiseRace();
//Display this controller.
race.setVisible(true);
//Check if the game is a tutorial
if (parent.getGameType()==4){
isTutorial = true;
tutorialText.setVisible(true);
tutorialStates = new ArrayList<>(Arrays.asList(TutorialState.values()));
currentState = tutorialStates.get(0);
tutorialStates.remove(0);
searchMapForKey("Upwind");
// set up annotation displays
new Annotations(annotationPane, raceCanvas);
tutorialText.setText("Welcome to the tutorial! Exit at anytime with ESC. \nWe will first learn how to turn upwind. Press " + keyToPress + " to turn upwind.");
} else {
isTutorial = false;
tutorialText.setVisible(false);
}
initialiseRace();
//Display this controller.
racePane.setVisible(true);
}
/**
@ -422,7 +504,7 @@ public class RaceController extends Controller {
* @param boats boats there are in the race.
*/
public void finishRace(ObservableList<VisualiserBoat> boats) {
race.setVisible(false);
racePane.setVisible(false);
parent.enterFinish(boats);
}
@ -457,18 +539,13 @@ public class RaceController extends Controller {
finishRace(visualiserRace.getVisualiserRaceState().getBoats());
} else {
//Otherwise, render the canvas.
raceCanvas.drawRace();
//Sort the tableview. Doesn't automatically work for all columns.
boatInfoTable.sort();
}
//Return to main screen if we lose connection.
if (!visualiserRace.getServerConnection().isAlive()) {
race.setVisible(false);
racePane.setVisible(false);
//parent.enterTitle();
try {
App.app.showMainStage(App.getStage());
@ -490,10 +567,10 @@ public class RaceController extends Controller {
* toggles if the info table is shown
*/
private void toggleTable() {
double tablePercent = 1 - (boatPlacingColumn.getPrefWidth() + boatTeamColumn.getPrefWidth() + boatMarkColumn.getPrefWidth() + boatSpeedColumn.getPrefWidth())/race.getWidth();
double tablePercent = 1 - (boatPlacingColumn.getPrefWidth() + boatTeamColumn.getPrefWidth() + boatMarkColumn.getPrefWidth() + boatSpeedColumn.getPrefWidth())/racePane.getWidth();
if (infoTableShow){
race.setDividerPositions(tablePercent);
racePane.setDividerPositions(tablePercent);
arrowPane.setScaleX(0.5);
arrowPane.setScaleY(0.5);
@ -501,7 +578,7 @@ public class RaceController extends Controller {
arrowPane.setTranslateY(0 - arrowPane.getScene().getHeight()/4);
}else{
race.setDividerPositions(1);
racePane.setDividerPositions(1);
arrowPane.setScaleX(1);
arrowPane.setScaleY(1);
@ -513,4 +590,108 @@ public class RaceController extends Controller {
infoTableShow = !infoTableShow;
}
/**
* Get the next tutorial state
*/
private void updateTutorialState(){
//Next tutorial state is popped from list
currentState = tutorialStates.get(0);
tutorialStates.remove(0);
}
/**
* Search key map for key given string of command
* @param command the command of the key
*/
private void searchMapForKey(String command){
//For loop through keyFactory
for (Map.Entry<String, ControlKey> entry: keyFactory.getKeyState().entrySet()){
if(entry.getValue().toString().equals(command)){
//Found next key required to press
keyToPress = entry.getKey();
}
}
}
/**
* Updates tutorial state and gui display for tutorial text
* @throws Exception Exception thrown
*/
private void checkTutorialState() throws Exception {
//Switch statement to check what the current tutorial state is
switch (currentState){
case UPWIND:
//Set next key to press as the downwind key
searchMapForKey("Downwind");
//Update tutorial text
tutorialText.setText("Nice! To turn downwind press " + keyToPress + ".");
updateTutorialState();
break;
case DOWNWIND:
//Set next key to press as the tack/gybe key
searchMapForKey("Tack/Gybe");
//Update tutorial text
tutorialText.setText("Nice! To tack or gybe press " + keyToPress + ".");
updateTutorialState();
break;
case TACKGYBE:
//Set next key to press as the VMG key
searchMapForKey("VMG");
//Update tutorial text
tutorialText.setText("Nice! To use VMG press " + keyToPress + ". This will turn the boat.");
updateTutorialState();
break;
case VMG:
//Set next key to press as the sails-in key
searchMapForKey("Toggle Sails");
//Update tutorial text
tutorialText.setText("Nice! To sails in press " + keyToPress + ". This will stop the boat.");
updateTutorialState();
break;
case SAILSIN:
//Set next key to press as the sails-out key
searchMapForKey("Toggle Sails");
//Update tutorial text
tutorialText.setText("Nice! To sails out press " + keyToPress + " again. The will start moving again.");
updateTutorialState();
break;
case SAILSOUT:
//Set next key to press as the zoom-in key
searchMapForKey("Zoom In");
//Update tutorial text
tutorialText.setText("Nice! To zoom in press " + keyToPress + ".");
updateTutorialState();
break;
case ZOOMIN:
//Set next key to press as the zoom-out key
searchMapForKey("Zoom Out");
//Update tutorial text
tutorialText.setText("Nice! You will also be able to zoom into boats and marks by clicking them. To zoom out press " + keyToPress + ".");
updateTutorialState();
break;
case ZOOMOUT:
//Finished tutorial. Display pop-up for exiting the tutorial
tutorialText.setText("Congratulations! You're done!");
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Finished Tutorial");
alert.setHeaderText("You have finished the tutorial.");
alert.setContentText("Now you know the controls you are ready to race!");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK) {
parent.endEvent();
racePane.setVisible(false);
App.app.showMainStage(App.getStage());
}
break;
default:
//State not found. Exit tutorial to title menu
parent.endEvent();
racePane.setVisible(false);
App.app.showMainStage(App.getStage());
break;
}
}
}

@ -86,9 +86,6 @@ public class StartController extends Controller {
/**
* Ctor.
*/
@ -225,12 +222,10 @@ public class StartController extends Controller {
//Get the current race status.
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
//Display it.
raceStatusLabel.setText("Race Status: " + raceStatus.name());
//If the race has reached the preparatory phase, or has started...
if (raceStatus == RaceStatusEnum.PREPARATORY || raceStatus == RaceStatusEnum.STARTED) {
if (raceStatus == RaceStatusEnum.WARNING
|| raceStatus == RaceStatusEnum.PREPARATORY
|| raceStatus == RaceStatusEnum.STARTED) {
//Stop this timer.
stop();
@ -243,6 +238,7 @@ public class StartController extends Controller {
}
}
}.start();
}

@ -6,16 +6,24 @@ import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import mock.app.Event;
import mock.exceptions.EventConstructionException;
import javafx.stage.WindowEvent;
import visualiser.app.App;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Controller for the opening title window.
@ -33,6 +41,12 @@ public class TitleController extends Controller {
RadioButton dayModeRD;
@FXML
RadioButton nightModeRD;
@FXML
Label tutorialLabel;
@FXML
Pane menuPane;
@FXML
ImageView imgSun;
/**
* Method called when the 'host a game' button is pressed.
@ -42,6 +56,7 @@ public class TitleController extends Controller {
*/
public void hostAGame() throws IOException {
titleWrapper.setVisible(false);
parent.setGameType(0);
parent.hostGame();
App.getStage().setResizable(true);
}
@ -68,7 +83,10 @@ public class TitleController extends Controller {
*/
public void setDayMode(){
dayModeRD.getScene().getStylesheets().clear();
menuPane.getStylesheets().clear();
imgSun.setImage(new Image(getClass().getResource("/visualiser/images/sun.png").toExternalForm()));
dayModeRD.getScene().getStylesheets().add("/css/dayMode.css");
menuPane.setStyle("-fx-background-color: #6be6ff;");
nightModeRD.setSelected(false);
}
@ -77,12 +95,17 @@ public class TitleController extends Controller {
*/
public void setNightMode(){
nightModeRD.getScene().getStylesheets().clear();
menuPane.getStylesheets().clear();
imgSun.setImage(new Image(getClass().getResource("/visualiser/images/sunsleep.png").toExternalForm()));
nightModeRD.getScene().getStylesheets().add("/css/nightMode.css");
menuPane.setStyle("-fx-background-color: #1f2c60;");
dayModeRD.setSelected(false);
}
@Override
public void initialize(URL location, ResourceBundle resources) {
tutorialLabel.setWrapText(true);
}
/**
@ -114,4 +137,12 @@ public class TitleController extends Controller {
}
}
public void tutorialStartPressed() throws IOException {
titleWrapper.setVisible(false);
parent.setGameType(4);
parent.beginGame();
}
}

@ -3,6 +3,7 @@ package visualiser.app;
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
@ -14,6 +15,7 @@ import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressBar;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
@ -95,7 +97,7 @@ public class App extends Application {
);
updateMessage("Preparing ingredients . . .");
Thread.sleep(200);
Thread.sleep(100);
for (int i = 0; i < burgerFilling.size(); i++) {
Thread.sleep(100);
updateProgress(i + 1, burgerFilling.size());

@ -0,0 +1,66 @@
package visualiser.enums;
import javafx.scene.input.KeyCode;
import network.Messages.BoatAction;
import network.Messages.Enums.BoatActionEnum;
import visualiser.gameController.Keys.ControlKey;
import static visualiser.app.App.keyFactory;
import java.util.ArrayList;
/**
* State of which stage the tutorial is currently in
*/
public enum TutorialState {
/**
* State for upwind in tutorial
*/
UPWIND(BoatActionEnum.UPWIND),
/**
* State for downwind in tutorial
*/
DOWNWIND(BoatActionEnum.DOWNWIND),
/**
* State for tacking/gybing in tutorial
*/
TACKGYBE(BoatActionEnum.TACK_GYBE),
/**
* State for vmg in tutorial
*/
VMG(BoatActionEnum.AUTO_PILOT),
/**
* State for sails-in in tutorial
*/
SAILSIN(BoatActionEnum.SAILS_IN),
/**
* State for sails-out in tutorial
*/
SAILSOUT(BoatActionEnum.SAILS_OUT),
/**
* State for zoom-in in tutorial
*/
ZOOMIN(null),
/**
* State for zoom-out in tutorial
*/
ZOOMOUT(null);
private BoatActionEnum action;
TutorialState(BoatActionEnum action){
this.action = action;
}
public BoatActionEnum getAction(){
return action;
}
}

@ -1,6 +1,5 @@
package visualiser.gameController.Keys;
import javafx.scene.input.KeyCode;
import network.Messages.Enums.BoatActionEnum;
/**
@ -45,7 +44,7 @@ public abstract class ControlKey {
/**
* What this key should do when the command is issued for it to do its job.
*/
public abstract void onAction();//may want to make it take in a visualiser and stuff in the future.
public abstract void onAction();
/**
* What to do when the key is held

@ -0,0 +1,65 @@
package visualiser.layout;
import javafx.scene.shape.Shape3D;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
/**
* Wrapper for controlling the position and heading of rendered 3D models.
*/
public class Subject3D {
/**
* Rendered mesh
*/
private Shape3D mesh;
/**
* Position translation updated by state listeners
*/
private Translate position;
/**
* Heading rotation updated by state listeners
*/
private Rotate heading;
/**
* Constructor for view subject wrapper
* @param mesh to be rendered
*/
public Subject3D(Shape3D mesh) {
this.mesh = mesh;
this.position = new Translate();
this.heading = new Rotate(0, Rotate.Y_AXIS);
this.mesh.getTransforms().addAll(position, heading, new Rotate(90, Rotate.X_AXIS), new Rotate(180, Rotate.Y_AXIS));
}
public Shape3D getMesh() {
return mesh;
}
public Translate getPosition() {
return this.position;
}
public Rotate getHeading() {
return heading;
}
public void setX(double x) {
position.setX(x);
}
public void setY(double y) {
position.setY(y);
}
public void setZ(double z) {
position.setZ(z);
}
public void setHeading(double angle) {
heading.setAngle(angle);
}
}

@ -0,0 +1,260 @@
package visualiser.layout;
import javafx.beans.value.ChangeListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.SubScene;
import javafx.scene.input.PickResult;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Shape3D;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import java.util.HashMap;
import java.util.Map;
/**
* Control for rendering 3D objects visible through a PerspectiveCamera. Implements Adapter Pattern to
* interface with camera, and allows clients to add shapes to the scene. All scenes contain sea plane and
* sky box, whose textures are set with special methods.
*/
public class View3D extends Pane {
/**
* Container for group and camera
*/
private SubScene scene;
/**
* Observable list of renderable items
*/
private ObservableList<Subject3D> items;
/**
* Map for selecting Subject3D from Shape3D
*/
private Map<Shape3D, Subject3D> selectionMap;
/**
* Subject tracked by camera
*/
private Subject3D target;
/**
* Rendering container for shapes
*/
private Group world;
/**
* Near limit of view frustum
*/
private double nearClip;
/**
* Far limit of view frustum
*/
private double farClip;
/**
* Camera origin
*/
private Translate pivot;
/**
* Distance of camera from pivot point
*/
private Translate distance;
/**
* Angle along ground between z-axis and camera
*/
private Rotate yaw;
/**
* Angle between ground plane and camera direction
*/
private Rotate pitch;
/**
* Single listener for subject heading changes
*/
private ChangeListener<? super Number> pivotHeading = (o, prev, curr) -> yaw.setAngle((double)curr);
/**
* Single listener for subject position (x) changes
*/
private ChangeListener<? super Number> pivotX = (o, prev, curr) -> pivot.setX((double)curr);
/**
* Single listener for subject position (y) changes
*/
private ChangeListener<? super Number> pivotY = (o, prev, curr) -> pivot.setY((double)curr);
/**
* Single listener for subject position (z) changes
*/
private ChangeListener<? super Number> pivotZ = (o, prev, curr) -> pivot.setZ((double)curr);
/**
* Distance to switch from third person to bird's eye
*/
private double THIRD_PERSON_LIMIT = 100;
/**
* Default constructor for View3D. Sets up Scene and PerspectiveCamera.
*/
public View3D() {
this.world = new Group();
this.selectionMap = new HashMap<>();
this.target = null;
this.scene = new SubScene(world, 300, 300);
scene.widthProperty().bind(this.widthProperty());
scene.heightProperty().bind(this.heightProperty());
scene.setFill(new Color(0.2, 0.6, 1, 1));
scene.setCamera(buildCamera());
this.getChildren().add(scene);
}
/**
* Sets up camera view frustum and binds transformations
* @return perspective camera
*/
private PerspectiveCamera buildCamera() {
PerspectiveCamera camera = new PerspectiveCamera(true);
// Set up view frustum
nearClip = 0.1;
farClip = 3000.0;
camera.setNearClip(nearClip);
camera.setFarClip(farClip);
// Set up transformations
pivot = new Translate();
distance = new Translate();
yaw = new Rotate(0, Rotate.Y_AXIS);
pitch = new Rotate(0, Rotate.X_AXIS);
camera.getTransforms().addAll(pivot, yaw, pitch, distance);
return camera;
}
/**
* Provide the list of subjects to be automatically added or removed from the view as the list
* changes.
* @param items list managed by client
*/
public void setItems(ObservableList<Subject3D> items) {
this.items = items;
this.items.addListener((ListChangeListener<? super Subject3D>) c -> {
while(c.next()) {
if (c.wasRemoved() || c.wasAdded()) {
for (Subject3D shape : c.getRemoved()) {
world.getChildren().remove(shape.getMesh());
selectionMap.remove(shape.getMesh());
}
for (Subject3D shape : c.getAddedSubList()) {
world.getChildren().add(shape.getMesh());
selectionMap.put(shape.getMesh(), shape);
}
}
}
});
}
/**
* Intercept mouse clicks on subjects in view. The applied listener cannot be removed.
*/
public void enableTracking() {
scene.setOnMousePressed(e -> {
PickResult result = e.getPickResult();
if(result != null && result.getIntersectedNode() != null && result.getIntersectedNode() instanceof Shape3D) {
trackSubject(selectionMap.get(result.getIntersectedNode()));
}
});
}
/**
* Stop camera from following the last selected subject
*/
private void untrackSubject() {
if(target != null) {
target.getPosition().xProperty().removeListener(pivotX);
target.getPosition().yProperty().removeListener(pivotY);
target.getPosition().zProperty().removeListener(pivotZ);
target.getHeading().angleProperty().removeListener(pivotHeading);
}
}
/**
* Set camera to follow the selected subject
* @param subject to track
*/
private void trackSubject(Subject3D subject) {
untrackSubject();
target = subject;
updatePivot(target.getPosition());
setYaw(target.getHeading().getAngle());
target.getPosition().xProperty().addListener(pivotX);
target.getPosition().yProperty().addListener(pivotY);
target.getPosition().zProperty().addListener(pivotZ);
target.getHeading().angleProperty().addListener(pivotHeading);
this.setDistance(THIRD_PERSON_LIMIT);
this.setPitch(20);
}
public void setNearClip(double nearClip) {
this.nearClip = nearClip;
}
public void setFarClip(double farClip) {
this.farClip = farClip;
}
/**
* Sets the coordinates of the camera pivot once.
* @param pivot source of coordinates
*/
public void updatePivot(Translate pivot) {
this.pivot.setX(pivot.getX());
this.pivot.setY(pivot.getY());
this.pivot.setZ(pivot.getZ());
}
/**
* Set distance of camera from pivot
* @param distance in units
*/
public void setDistance(double distance) {
this.distance.setZ(-distance);
}
/**
* Adds delta to current distance and changes camera mode if applicable.
* Third person limit specifies the distance at which a third person camera
* switches to bird's-eye, remaining focused on the same position.
* @param delta amount to change distance by
*/
public void updateDistance(double delta) {
double distance = -this.distance.getZ() + delta;
if(distance <= 0) {
this.setDistance(0);
} else if(distance > THIRD_PERSON_LIMIT) {
untrackSubject();
this.setYaw(0);
this.setPitch(60);
this.setDistance(distance);
} else {
this.setDistance(distance);
}
}
/**
* Set angle of camera from z-axis along ground
* @param yaw in degrees
*/
public void setYaw(double yaw) {
this.yaw.setAngle(yaw);
}
/**
* Set elevation of camera
* @param pitch in degrees
*/
public void setPitch(double pitch) {
this.pitch.setAngle(-pitch);
}
}

@ -1,18 +0,0 @@
package visualiser.model;
import com.interactivemesh.jfx.importer.Importer;
import javafx.scene.layout.Pane;
/**
* Created by fwy13 on 29/08/17.
*/
public class BoatDisplay3D extends Pane {
public BoatDisplay3D(String filePath){
super();
// Shape3D
// this.getChildren().add();
}
}

@ -1,145 +0,0 @@
package visualiser.model;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.SubScene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Shape3D;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
/**
* Control for rendering 3D objects visible through a PerspectiveCamera. Implements Adapter Pattern to
* interface with camera, and allows clients to add shapes to the scene. All scenes contain sea plane and
* sky box, whose textures are set with special methods.
*/
public class View3D extends Pane {
/**
* Observable list of renderable items
*/
private ObservableList<Shape3D> items;
/**
* Rendering container for shapes
*/
private Group world;
/**
* Near limit of view frustum
*/
private double nearClip;
/**
* Far limit of view frustum
*/
private double farClip;
/**
* Position camera pivots around
*/
private Translate pivot;
/**
* Distance of camera from pivot point
*/
private Translate distance;
/**
* Angle along ground between z-axis and camera
*/
private Rotate yaw;
/**
* Angle between ground plane and camera direction
*/
private Rotate pitch;
/**
* Default constructor for View3D. Sets up Scene and PerspectiveCamera.
*/
public View3D() {
world = new Group();
SubScene scene = new SubScene(world, 300, 300);
scene.widthProperty().bind(this.widthProperty());
scene.heightProperty().bind(this.heightProperty());
scene.setFill(Color.BLACK);
scene.setCamera(buildCamera());
this.getChildren().add(scene);
}
/**
* Sets up camera view frustum and binds transformations
* @return perspective camera
*/
private PerspectiveCamera buildCamera() {
PerspectiveCamera camera = new PerspectiveCamera(true);
// Set up view frustum
nearClip = 0.1;
farClip = 1000.0;
camera.setNearClip(nearClip);
camera.setFarClip(farClip);
// Set up transformations
pivot = new Translate();
distance = new Translate();
yaw = new Rotate(0, Rotate.Y_AXIS);
pitch = new Rotate(0, Rotate.X_AXIS);
camera.getTransforms().addAll(pivot, yaw, pitch, distance);
return camera;
}
public void setItems(ObservableList<Shape3D> items) {
this.items = items;
this.items.addListener((ListChangeListener<? super Shape3D>) c -> {
while(c.next()) {
if (c.wasRemoved() || c.wasAdded()) {
for (Shape3D shape : c.getRemoved()) world.getChildren().remove(shape);
for (Shape3D shape : c.getAddedSubList()) world.getChildren().add(shape);
}
}
});
}
public void setNearClip(double nearClip) {
this.nearClip = nearClip;
}
public void setFarClip(double farClip) {
this.farClip = farClip;
}
/**
* Set object to centre on camera
* @param pivot centred object
*/
public void setPivot(Shape3D pivot) {
this.pivot.setX(pivot.getTranslateX());
this.pivot.setY(pivot.getTranslateY());
this.pivot.setZ(pivot.getTranslateZ());
}
/**
* Set distance of camera from pivot
* @param distance in units
*/
public void setDistance(double distance) {
this.distance.setZ(-distance);
}
/**
* Set angle of camera from z-axis along ground
* @param yaw in degrees
*/
public void setYaw(double yaw) {
this.yaw.setAngle(yaw);
}
/**
* Set elevation of camera
* @param pitch in degrees
*/
public void setPitch(double pitch) {
this.pitch.setAngle(-pitch);
}
}

@ -1,11 +1,10 @@
package visualiser.model;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.paint.Color;
import network.Messages.Enums.BoatStatusEnum;
import shared.model.Azimuth;
import shared.model.Boat;
import shared.model.Constants;
import shared.model.GPSCoordinate;
import shared.model.*;
import java.time.Duration;
import java.time.ZonedDateTime;
@ -61,7 +60,8 @@ public class VisualiserBoat extends Boat {
private boolean isClientBoat = false;
private ObjectProperty<GPSCoordinate> positionProperty;
private ObjectProperty<Bearing> bearingProperty;
/**
@ -239,4 +239,38 @@ public class VisualiserBoat extends Boat {
public void setClientBoat(boolean clientBoat) {
isClientBoat = clientBoat;
}
@Override
public GPSCoordinate getPosition() {
return positionProperty.get();
}
@Override
public void setPosition(GPSCoordinate position) {
if(this.positionProperty == null) {
this.positionProperty = new SimpleObjectProperty<>();
}
this.positionProperty.set(position);
}
public ObjectProperty<GPSCoordinate> positionProperty() {
return positionProperty;
}
@Override
public Bearing getBearing() {
return bearingProperty.get();
}
@Override
public void setBearing(Bearing bearing) {
if(this.bearingProperty == null) {
this.bearingProperty = new SimpleObjectProperty<>();
}
this.bearingProperty.set(bearing);
}
public ObjectProperty<Bearing> bearingProperty() {
return bearingProperty;
}
}

@ -0,0 +1,104 @@
package visualiser.utils;
import shared.dataInput.RaceDataSource;
import shared.model.GPSCoordinate;
import visualiser.model.GraphCoordinate;
/**
* Converts GPS coordinates to view volume coordinates. Longitudes are equally spaced at all latitudes,
* which leads to inaccurate distance measurements close to the poles. This is acceptable as races are
* not likely to be set there.
*/
public class GPSConverter {
private double longRight;
private double longLeft;
private double latBottom;
private double latTop;
/**
* Conversion factor from longitude to view units
*/
private double longitudeFactor;
/**
* Conversion factor from latitude to view units
*/
private double latitudeFactor;
/**
* Set up projection with default view boundaries from RaceDataSource
* @param source for view boundaries
* @param longitudeFactor separation of a degree of longitude in view units
* @param latitudeFactor separation of a degree of latitude in view units
*/
public GPSConverter(RaceDataSource source, double longitudeFactor, double latitudeFactor) {
this.latTop = source.getMapTopLeft().getLatitude();
this.longLeft = source.getMapTopLeft().getLongitude();
this.latBottom = source.getMapBottomRight().getLatitude();
this.longRight = source.getMapBottomRight().getLongitude();
this.longitudeFactor = longitudeFactor;
this.latitudeFactor = latitudeFactor;
}
/**
* Converts GPS coordinates to coordinates for container.
* It is assumed that the provided GPSCoordinate will always be within the GPSCoordinate boundaries of the RaceMap.
*
* @param lat GPS latitude
* @param lon GPS longitude
* @return GraphCoordinate (pair of doubles)
* @see GraphCoordinate
*/
private GraphCoordinate convertGPS(double lat, double lon) {
//Calculate the width/height, in gps coordinates, of the map.
double longWidth = longRight - longLeft;
double latHeight = latBottom - latTop;
//Calculate the distance between the specified coordinate and the edge of the map.
double longDelta = lon - longLeft;
double latDelta = lat - latTop;
//Calculate the proportion along horizontally, from the left, the coordinate should be.
double longProportion = longDelta / longWidth;
//Calculate the proportion along vertically, from the top, the coordinate should be.
double latProportion = latDelta / latHeight;
//Check which metric dimension of our map is smaller. We use this to ensure that any rendered stuff retains its correct aspect ratio, and that everything is visible on screen.
double smallerDimension = Math.min(longitudeFactor, latitudeFactor);
//Calculate the x and y pixel coordinates.
//We take the complement of latProportion to flip it.
int x = (int) (longProportion * smallerDimension);
int y = (int) (latProportion * smallerDimension);
//Because we try to maintain the correct aspect ratio, we will end up with "spare" pixels along the larger dimension (e.g., width 800, height 600, 200 extra pixels along width).
double extraDistance = Math.abs(longitudeFactor - latitudeFactor);
//We therefore "center" the coordinates along this larger dimension, by adding half of the extra pixels.
if (longitudeFactor > latitudeFactor) {
x += extraDistance / 2;
} else {
y += extraDistance / 2;
}
//Finally, create the GraphCoordinate.
GraphCoordinate graphCoordinate = new GraphCoordinate(x, y);
return graphCoordinate;
}
/**
* Converts the GPS Coordinate to GraphCoordinate.
* It is assumed that the provided GPSCoordinate will always be within the GPSCoordinate boundaries of the RaceMap.
*
* @param coordinate GPSCoordinate representation of Latitude and Longitude.
* @return GraphCoordinate that the GPS is coordinates are to be displayed on the map.
* @see GraphCoordinate
* @see GPSCoordinate
*/
public GraphCoordinate convertGPS(GPSCoordinate coordinate) {
return convertGPS(coordinate.getLatitude(), coordinate.getLongitude());
}
}

@ -53,5 +53,39 @@
}
#arrowImage {
-fx-image: url("/visualiser/images/arrow.png");
-fx-graphic: url("/visualiser/images/arrow.png");
}
#nextButton {
-fx-background-image: url("/visualiser/images/ArrowRoundRight.png");
-fx-background-size: 60px;
-fx-background-repeat: no-repeat;
-fx-background-position: center center;
-fx-focus-color: transparent;
-fx-background-color: transparent;
}
#nextButton:pressed {
-fx-background-image: url("/visualiser/images/ArrowRoundRightClicked.png");
-fx-background-size: 60px;
-fx-background-repeat: no-repeat;
-fx-background-position: center center;
-fx-focus-color: transparent;
-fx-background-color: transparent;
}
#previousButton {
-fx-background-image: url("/visualiser/images/ArrowRoundLeft.png");
-fx-background-size: 60px;
-fx-background-repeat: no-repeat;
-fx-background-position: center center;
-fx-focus-color: transparent;
-fx-background-color: transparent;
}
#previousButton:pressed {
-fx-background-image: url("/visualiser/images/ArrowRoundLeftClicked.png");
-fx-background-size: 60px;
-fx-background-repeat: no-repeat;
-fx-background-position: center center;
-fx-focus-color: transparent;
-fx-background-color: transparent;
}

@ -57,3 +57,37 @@
#arrowImage {
-fx-image: url("/visualiser/images/arrowLight.png");
}
#nextButton {
-fx-background-image: url("/visualiser/images/ArrowRoundRight.png");
-fx-background-size: 60px;
-fx-background-repeat: no-repeat;
-fx-background-position: center center;
-fx-focus-color: transparent;
-fx-background-color: transparent;
}
#nextButton:pressed {
-fx-background-image: url("/visualiser/images/ArrowRoundRightClicked.png");
-fx-background-size: 60px;
-fx-background-repeat: no-repeat;
-fx-background-position: center center;
-fx-focus-color: transparent;
-fx-background-color: transparent;
}
#previousButton {
-fx-background-image: url("/visualiser/images/ArrowRoundLeft.png");
-fx-background-size: 60px;
-fx-background-repeat: no-repeat;
-fx-background-position: center center;
-fx-focus-color: transparent;
-fx-background-color: transparent;
}
#previousButton:pressed {
-fx-background-image: url("/visualiser/images/ArrowRoundLeftClicked.png");
-fx-background-size: 60px;
-fx-background-repeat: no-repeat;
-fx-background-position: center center;
-fx-focus-color: transparent;
-fx-background-color: transparent;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<BoatConfig>
<Boats>
<!--Mark Boats-->
<Boat Type="Mark" BoatName="PRO" SourceID="101" >
<GPSposition X= "-64.854304" Y="32.296577" Z="0"/>
</Boat>
<Boat Type="Mark" BoatName="PIN" SourceID="102" >
<GPSposition X= "-64.855242" Y="32.293771" Z="0"/>
</Boat>
<Boat Type="Mark" BoatName="FL" SourceID="108" >
<GPSposition X= "-64.839291" Y="32.317379" Z="0"/>
</Boat>
<Boat Type="Mark" BoatName="FR" SourceID="109" >
<GPSposition X= "-64.83626" Y="32.317257" Z="0"/>
</Boat>
<!--Participants-->
<!--Participants-->
<Boat BoatName="Emirates Team New Zealand" HullNum="RG01" ShapeID="0" ShortName="NZL" SourceID="121" StoweName="NZL" Type="Yacht">
<GPSposition X="-64.854304" Y="32.296577" Z="0"/>
</Boat>
<!--<Boat BoatName="Land Rover BAR" HullNum="RG01" ShapeID="0" ShortName="GBR" SourceID="122" StoweName="GBR" Type="Yacht">
<GPSposition X="-64.854304" Y="32.296577" Z="0"/>
</Boat>
<Boat BoatName="SoftBank Team Japan" HullNum="RG01" ShapeID="0" ShortName="JPN" SourceID="123" StoweName="JPN" Type="Yacht">
<GPSposition X="-64.854304" Y="32.296577" Z="0"/>
</Boat>
<Boat BoatName="Groupama Team France" HullNum="RG01" ShapeID="0" ShortName="FRA" SourceID="124" StoweName="FRA" Type="Yacht">
<GPSposition X="-64.854304" Y="32.296577" Z="0"/>
</Boat>
<Boat BoatName="Artemis Racing" HullNum="RG01" ShapeID="0" ShortName="SWE" SourceID="125" StoweName="SWE" Type="Yacht">
<GPSposition X="-64.854304" Y="32.296577" Z="0"/>
</Boat>
<Boat BoatName="ORACLE TEAM USA" HullNum="RG01" ShapeID="0" ShortName="USA" SourceID="126" StoweName="USA" Type="Yacht">
<GPSposition X="-64.854304" Y="32.296577" Z="0"/>
</Boat>-->
</Boats>
</BoatConfig>

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Race>
<RaceID>5326</RaceID>
<RaceType>FLEET</RaceType>
<CreationTimeDate>RACE_CREATION_TIME</CreationTimeDate>
<RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
<Participants>
</Participants>
<CompoundMarkSequence>
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />
<Corner SeqID="2" CompoundMarkID="2" Rounding="Port" ZoneSize="3" />
<Corner SeqID="3" CompoundMarkID="4" Rounding="Starboard" ZoneSize="3" />
<Corner SeqID="4" CompoundMarkID="2" Rounding="Port" ZoneSize="3" />
<Corner SeqID="5" CompoundMarkID="3" Rounding="Port" ZoneSize="3" />
<Corner SeqID="7" CompoundMarkID="5" Rounding="SP" ZoneSize="3"/>
</CompoundMarkSequence>
<Course>
<CompoundMark CompoundMarkID="1" Name="Start Line">
<Mark SeqId="1" Name="PRO" TargetLat="1.681354" TargetLng="1.132354" SourceID="101"/>
<Mark SeqId="2" Name="PIN" TargetLat="1.681354" TargetLng="1.135604" SourceID="102"/>
</CompoundMark>
<CompoundMark CompoundMarkID="2" Name="Marker 1">
<Mark Name="Marker1" TargetLat="1.728713" TargetLng="1.131345" SourceID="103"/>
</CompoundMark>
<CompoundMark CompoundMarkID="3" Name="Marker 2">
<Mark Name="Marker2" TargetLat="1.725120" TargetLng="1.109701" SourceID="104"/>
</CompoundMark>
<CompoundMark CompoundMarkID="4" Name="Gate 1">
<Mark Name="LGL" SeqId="1" TargetLat="1.707354" TargetLng="1.130505" SourceID="105"/>
<Mark Name="LGR" SeqId="2" TargetLat="1.707404" TargetLng="1.133304" SourceID="106"/>
</CompoundMark>
<CompoundMark CompoundMarkID="5" Name="Finish Line">
<Mark Name="FL" SeqId="1" TargetLat="1.690202" TargetLng="1.109606" SourceID="107"/>
<Mark Name="FR" SeqId="2" TargetLat="1.692202" TargetLng="1.112629" SourceID="108"/>
</CompoundMark>
</Course>
<CourseLimit>
<Limit Lat="1.73558" Lon="1.105505" SeqID="1"/>
<Limit Lat="1.732444" Lon="1.137105" SeqID="2"/>
<Limit Lat="1.680404" Lon="1.140505" SeqID="3"/>
<Limit Lat="1.675232" Lon="1.122202" SeqID="4"/>
<Limit Lat="1.685101" Lon="1.120222" SeqID="5"/>
<Limit Lat="1.680101" Lon="1.102505" SeqID="6"/>
</CourseLimit>
</Race>

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Race>
<RaceID>5326</RaceID>
<RaceType>FLEET</RaceType>
<CreationTimeDate>RACE_CREATION_TIME</CreationTimeDate>
<RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
<Participants>
</Participants>
<CompoundMarkSequence>
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />
<Corner SeqID="2" CompoundMarkID="2" Rounding="Starboard" ZoneSize="3" />
<Corner SeqID="3" CompoundMarkID="3" Rounding="Starboard" ZoneSize="3" />
<Corner SeqID="4" CompoundMarkID="2" Rounding="Starboard" ZoneSize="3" />
<Corner SeqID="5" CompoundMarkID="4" Rounding="SP" ZoneSize="3" />
</CompoundMarkSequence>
<Course>
<CompoundMark CompoundMarkID="1" Name="Start Line">
<Mark SeqId="1" Name="PRO" TargetLat="1.681354" TargetLng="1.132354" SourceID="101"/>
<Mark SeqId="2" Name="PIN" TargetLat="1.680354" TargetLng="1.135604" SourceID="102"/>
</CompoundMark>
<CompoundMark CompoundMarkID="2" Name="Marker 1">
<Mark Name="Marker1" TargetLat="1.780354" TargetLng="1.140600" SourceID="103"/>
</CompoundMark>
<CompoundMark CompoundMarkID="3" Name="Marker 2">
<Mark Name="Marker2" TargetLat="1.730354" TargetLng="1.137604" SourceID="104"/>
</CompoundMark>
<CompoundMark CompoundMarkID="4" Name="Finish Line">
<Mark SeqId="1" Name="FL" TargetLat="1.681354" TargetLng="1.132354" SourceID="105"/>
<Mark SeqId="2" Name="FR" TargetLat="1.680354" TargetLng="1.135604" SourceID="106"/>
</CompoundMark>
</Course>
<CourseLimit>
<Limit Lat="1.675354" Lon="1.122354" SeqID="1"/>
<Limit Lat="1.680354" Lon="1.141600" SeqID="2"/>
<Limit Lat="1.790354" Lon="1.146600" SeqID="3"/>
<Limit Lat="1.785354" Lon="1.127354" SeqID="4"/>
</CourseLimit>
</Race>

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Race>
<RaceID>5326</RaceID>
<RaceType>FLEET</RaceType>
<CreationTimeDate>RACE_CREATION_TIME</CreationTimeDate>
<RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
<Participants>
</Participants>
<CompoundMarkSequence>
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />
<Corner SeqID="2" CompoundMarkID="2" Rounding="Port" ZoneSize="3" />
<Corner SeqID="3" CompoundMarkID="3" Rounding="Port" ZoneSize="3" />
<Corner SeqID="4" CompoundMarkID="4" Rounding="Starboard" ZoneSize="3" />
<Corner SeqID="5" CompoundMarkID="3" Rounding="Starboard" ZoneSize="3" />
<Corner SeqID="6" CompoundMarkID="4" Rounding="Port" ZoneSize="3" />
<Corner SeqID="7" CompoundMarkID="5" Rounding="SP" ZoneSize="3"/>
</CompoundMarkSequence>
<Course>
<CompoundMark CompoundMarkID="1" Name="Start Line">
<Mark SeqId="1" Name="PRO" TargetLat="-40.681354" TargetLng="174.132354" SourceID="101"/>
<Mark SeqId="2" Name="PIN" TargetLat="-40.608415" TargetLng="174.272430" SourceID="102"/>
</CompoundMark>
<CompoundMark CompoundMarkID="2" Name="Marker 1">
<Mark Name="Marker1" TargetLat="-40.118713" TargetLng="173.541839" SourceID="103"/>
</CompoundMark>
<CompoundMark CompoundMarkID="3" Name="Marker 2">
<Mark Name="Marker2" TargetLat="-40.53120" TargetLng="173.250701" SourceID="104"/>
</CompoundMark>
<CompoundMark CompoundMarkID="4" Name="Gate">
<Mark Name="WGL" SeqId="1" TargetLat="-40.722999" TargetLng="173.420989" SourceID="106"/>
<Mark Name="WGR" SeqId="2" TargetLat="-40.789575" TargetLng="173.294647" SourceID="107"/>
</CompoundMark>
<CompoundMark CompoundMarkID="5" Name="Finish Line">
<Mark Name="FL" SeqId="1" TargetLat="-40.968169" TargetLng="173.692901" SourceID="108"/>
<Mark Name="FR" SeqId="2" TargetLat="-40.878932" TargetLng="173.695648" SourceID="109"/>
</CompoundMark>
</Course>
<CourseLimit>
<Limit Lat="-40.984758" Lon="173.736846" SeqID="1"/>
<Limit Lat="-41.160757" Lon="173.140838" SeqID="2"/>
<Limit Lat="-39.937843" Lon="173.085906" SeqID="3"/>
<Limit Lat="-40.244616" Lon="174.420745" SeqID="4"/>
<Limit Lat="-40.627178" Lon="174.412505" SeqID="5"/>
<Limit Lat="-40.783337" Lon="174.093902" SeqID="6"/>
<Limit Lat="-40.612585" Lon="173.893401" SeqID="7"/>
<Limit Lat="-40.752134" Lon="173.723113" SeqID="8"/>
</CourseLimit>
</Race>

@ -5,7 +5,6 @@
<CreationTimeDate>RACE_CREATION_TIME</CreationTimeDate>
<RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
<Participants>
<Yacht SourceID="126"/>
</Participants>
<CompoundMarkSequence>
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />

@ -1,54 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Race>
<RaceID>5326</RaceID>
<RaceType>FLEET</RaceType>
<CreationTimeDate>RACE_CREATION_TIME</CreationTimeDate>
<RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
<Participants>
<Yacht SourceID="124"/>
<Yacht SourceID="125"/>
<Yacht SourceID="126"/>
</Participants>
<CompoundMarkSequence>
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />
<Corner SeqID="2" CompoundMarkID="2" Rounding="Port" ZoneSize="3" />
<Corner SeqID="3" CompoundMarkID="4" Rounding="Port" ZoneSize="3" />
<Corner SeqID="4" CompoundMarkID="3" Rounding="Starboard" ZoneSize="3" />
<Corner SeqID="5" CompoundMarkID="4" Rounding="Port" ZoneSize="3" />
<Corner SeqID="6" CompoundMarkID="5" Rounding="SP" ZoneSize="3"/>
</CompoundMarkSequence>
<Course>
<CompoundMark CompoundMarkID="1" Name="Start Line">
<Mark SeqId="1" Name="PRO" TargetLat="32.296577" TargetLng="-64.854304" SourceID="101"/>
<Mark SeqId="2" Name="PIN" TargetLat="32.293771" TargetLng="-64.855242" SourceID="102"/>
</CompoundMark>
<CompoundMark CompoundMarkID="2" Name="Marker 1">
<Mark Name="Marker1" TargetLat="32.293039" TargetLng="-64.843983" SourceID="103"/>
</CompoundMark>
<CompoundMark CompoundMarkID="3" Name="Windward Gate">
<Mark Name="WGL" SeqId="1" TargetLat="32.28468" TargetLng="-64.850045" SourceID="104"/>
<Mark Name="WGR" SeqId="2" TargetLat="32.280164" TargetLng="-64.847591" SourceID="105"/>
</CompoundMark>
<CompoundMark CompoundMarkID="4" Name="Leeward Gate">
<Mark Name="LGL" SeqId="1" TargetLat="32.309693" TargetLng="-64.835249" SourceID="106"/>
<Mark Name="LGR" SeqId="2" TargetLat="32.308046" TargetLng="-64.831785" SourceID="107"/>
</CompoundMark>
<CompoundMark CompoundMarkID="5" Name="Finish Line">
<Mark Name="FL" SeqId="1" TargetLat="32.317379" TargetLng="-64.839291" SourceID="108"/>
<Mark Name="FR" SeqId="2" TargetLat="32.317257" TargetLng="-64.83626" SourceID="109"/>
</CompoundMark>
</Course>
<CourseLimit>
<Limit Lat="32.313922" Lon="-64.837168" SeqID="1"/>
<Limit Lat="32.317379" Lon="-64.839291" SeqID="2"/>
<Limit Lat="32.317911" Lon="-64.836996" SeqID="3"/>
<Limit Lat="32.317257" Lon="-64.83626" SeqID="4"/>
<Limit Lat="32.304273" Lon="-64.822834" SeqID="5"/>
<Limit Lat="32.279097" Lon="-64.841545" SeqID="6"/>
<Limit Lat="32.279604" Lon="-64.849871" SeqID="7"/>
<Limit Lat="32.289545" Lon="-64.854162" SeqID="8"/>
<Limit Lat="32.290198" Lon="-64.858711" SeqID="9"/>
<Limit Lat="32.297164" Lon="-64.856394" SeqID="10"/>
<Limit Lat="32.296148" Lon="-64.849184" SeqID="11"/>
</CourseLimit>
</Race>

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Race>
<RaceID>9999</RaceID>
<RaceType>FLEET</RaceType>
<CreationTimeDate>RACE_CREATION_TIME</CreationTimeDate>
<RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
<Participants>
<!--<Yacht SourceID="121"/>-->
</Participants>
<CompoundMarkSequence>
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />
<Corner SeqID="2" CompoundMarkID="2" Rounding="SP" ZoneSize="3"/>
</CompoundMarkSequence>
<Course>
<CompoundMark CompoundMarkID="1" Name="Start Line">
<Mark SeqId="1" Name="PRO" TargetLat="32.288148" TargetLng="-64.852996" SourceID="101"/>
<Mark SeqId="2" Name="PIN" TargetLat="32.290148" TargetLng="-64.854996" SourceID="102"/>
</CompoundMark>
<CompoundMark CompoundMarkID="2" Name="Finish Line">
<Mark Name="FL" SeqId="1" TargetLat="42.315911" TargetLng="-64.846996" SourceID="108"/>
<Mark Name="FR" SeqId="2" TargetLat="42.315911" TargetLng="-64.848996" SourceID="109"/>
</CompoundMark>
</Course>
<CourseLimit>
<Limit Lat="32.317911" Lon="-64.836996" SeqID="1"/>
<Limit Lat="32.286148" Lon="-64.836996" SeqID="2"/>
<Limit Lat="32.286148" Lon="-64.856996" SeqID="3"/>
<Limit Lat="32.317911" Lon="-64.856996" SeqID="4"/>
<!--<Limit Lat="32.313922" Lon="-64.837168" SeqID="1"/>
<Limit Lat="32.317379" Lon="-64.839291" SeqID="2"/>
<Limit Lat="32.317911" Lon="-64.836996" SeqID="3"/>
<Limit Lat="32.317257" Lon="-64.83626" SeqID="4"/>
<Limit Lat="32.304273" Lon="-64.822834" SeqID="5"/>
<Limit Lat="32.279097" Lon="-64.841545" SeqID="6"/>
<Limit Lat="32.279604" Lon="-64.849871" SeqID="7"/>
<Limit Lat="32.289545" Lon="-64.854162" SeqID="8"/>
<Limit Lat="32.290198" Lon="-64.858711" SeqID="9"/>
<Limit Lat="32.297164" Lon="-64.856394" SeqID="10"/>
<Limit Lat="32.296148" Lon="-64.849184" SeqID="11"/>-->
</CourseLimit>
</Race>

@ -0,0 +1,10 @@
<RegattaConfig>
<RegattaID>0</RegattaID>
<RegattaName>Race Tutorial</RegattaName>
<CourseName>Tutorial</CourseName>
<CentralLatitude>-36.82791529</CentralLatitude>
<CentralLongitude>174.81218919</CentralLongitude>
<CentralAltitude>0.00</CentralAltitude>
<UtcOffset>12</UtcOffset>
<MagneticVariation>14.1</MagneticVariation>
</RegattaConfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 KiB

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<Race>
<RaceID>99999999</RaceID>
<RaceType>Match</RaceType>
<CreationTimeDate>2011-08-06T13:25:00-0000</CreationTimeDate>
<RaceStartTime Time="2011-08-06T13:30:00-0700" Postpone="false"/>
<Participants>
<Yacht SourceID="101" Entry="Port"/>
<Yacht SourceID="108" Entry="Stbd"/>
</Participants>
<Course>
<CompoundMark CompoundMarkID="1" Name="StartLine">
<Mark SeqID="1" Name="PRO" TargetLat="-36.83" TargetLng="174.83" SourceID="101"/>
<Mark SeqID="2" Name="PIN" TargetLat="-36.84" TargetLng="174.81" SourceID="102"/>
</CompoundMark>
<CompoundMark CompoundMarkID="2" Name="M1">
<Mark Name="M1" TargetLat="-36.63566590" TargetLng="174.88543944" SourceID="103"/>
</CompoundMark>
<CompoundMark CompoundMarkID="3" Name="M2">
<Mark Name="M2" TargetLat="-36.83" TargetLng="174.80" SourceID="102"/>
</CompoundMark>
<CompoundMark CompoundMarkID="4" Name="Gate">
<Mark SeqID="1" Name="G1" TargetLat="-36.63566590" TargetLng="174.97205159" SourceID="104"/>
<Mark SeqID="2" Name="G2" TargetLat="-36.64566590" TargetLng="174.98205159" SourceID="105"/>
</CompoundMark>
</Course>
<CompoundMarkSequence>
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3"/>
<Corner SeqID="2" CompoundMarkID="2" Rounding="Port" ZoneSize="3"/>
<Corner SeqID="3" CompoundMarkID="3" Rounding="Stbd" ZoneSize="6"/>
<Corner SeqID="4" CompoundMarkID="4" Rounding="PS" ZoneSize="6"/>
<Corner SeqID="5" CompoundMarkID="1" Rounding="SP" ZoneSize="3"/>
</CompoundMarkSequence>
<CourseLimit>
<Limit SeqID="1" Lat="-36.8325" Lon="174.8325"/>
<Limit SeqID="2" Lat="-36.82883" Lon="174.81983"/>
<Limit SeqID="3" Lat="-36.82067" Lon="174.81983"/>
<Limit SeqID="4" Lat="-36.811" Lon="174.8265"/>
<Limit SeqID="5" Lat="-36.81033" Lon="174.83833"/>
<Limit SeqID="6" Lat="-36.81533" Lon="174.8525"/>
<Limit SeqID="7" Lat="-36.81533" Lon="174.86733"/>
<Limit SeqID="8" Lat="-36.81633" Lon="174.88217"/>
<Limit SeqID="9" Lat="-36.83383" Lon="174.87117"/>
<Limit SeqID="10" Lat="-36.83417" Lon="174.84767"/>
</CourseLimit>
</Race>

@ -1,44 +1,61 @@
<!--<?xml version="1.0" encoding="UTF-8"?>-->
<?xml version="1.0" encoding="UTF-8"?>
<!--<?import java.lang.*?>-->
<!--<?import javafx.scene.control.*?>-->
<!--<?import javafx.scene.text.*?>-->
<!--<?import javafx.scene.control.Button?>-->
<!--<?import javafx.scene.control.Label?>-->
<!--<?import javafx.scene.layout.*?>-->
<!--<?import javafx.scene.text.Font?>-->
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<!--<AnchorPane fx:id="hostWrapper" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="780.0" visible="false" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">-->
<!--<children>-->
<!--<GridPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">-->
<!--<columnConstraints>-->
<!--<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />-->
<!--<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />-->
<!--<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />-->
<!--</columnConstraints>-->
<!--<rowConstraints>-->
<!--<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />-->
<!--<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />-->
<!--<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />-->
<!--</rowConstraints>-->
<!--<children>-->
<!--<Button fx:id="hostGameBtn" mnemonicParsing="false" onAction="#hostGamePressed" text="Start Game" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="2">-->
<!--<font>-->
<!--<Font size="20.0" />-->
<!--</font>-->
<!--</Button>-->
<!--<Label text="Address: 127.0.0.1" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP">-->
<!--<font>-->
<!--<Font size="17.0" />-->
<!--</font>-->
<!--</Label>-->
<!--<Label text="Port: 4942" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1">-->
<!--<font>-->
<!--<Font size="17.0" />-->
<!--</font>-->
<!--</Label>-->
<!--<Button mnemonicParsing="false" onAction="#menuBtnPressed" text="Main Menu" GridPane.halignment="CENTER" />-->
<!--</children>-->
<!--</GridPane>-->
<!--</children>-->
<!--</AnchorPane>-->
<AnchorPane fx:id="hostWrapper" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="780.0" visible="false" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="visualiser.Controllers.HostController">
<children>
<GridPane layoutY="14.0" AnchorPane.bottomAnchor="-14.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="14.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="170.0" minWidth="10.0" prefWidth="170.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="170.0" minWidth="10.0" prefWidth="170.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="60.0" minHeight="60.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="435.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="80.0" minHeight="80.0" prefHeight="80.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Button fx:id="hostGameBtn" mnemonicParsing="false" onAction="#hostGamePressed" text="Start Game" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="CENTER">
<font>
<Font size="20.0" />
</font>
<GridPane.margin>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</GridPane.margin>
</Button>
<Label text="Address: 127.0.0.1" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="TOP">
<font>
<Font size="17.0" />
</font>
</Label>
<Label text="Port: 4942" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
<font>
<Font size="17.0" />
</font>
</Label>
<Button mnemonicParsing="false" onAction="#menuBtnPressed" text="Main Menu" GridPane.halignment="LEFT" GridPane.valignment="TOP">
<GridPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</GridPane.margin></Button>
<ImageView fx:id="mapImage" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.hgrow="ALWAYS" GridPane.rowIndex="1" GridPane.valignment="CENTER" GridPane.vgrow="ALWAYS" />
<Button fx:id="previousButton" maxHeight="80.0" maxWidth="80.0" mnemonicParsing="false" onAction="#previousImage" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER" />
<Button fx:id="nextButton" maxHeight="80.0" maxWidth="80.0" mnemonicParsing="false" onAction="#nextImage" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER" />
</children>
</GridPane>
</children>
</AnchorPane>

@ -1,89 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<AnchorPane fx:id="hostWrapper" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="780.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="visualiser.Controllers.HostController">
<children>
<SplitPane fx:id="splitPane" dividerPositions="0.7724935732647815" layoutX="580.0" layoutY="129.0" prefHeight="160.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane fx:id="imagePane" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<ImageView fx:id="imageView" fitHeight="376.0" fitWidth="597.0" nodeOrientation="INHERIT" pickOnBounds="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<image>
<Image url="@../images/lobby.gif" />
</image></ImageView>
<GridPane style="-fx-background-color: rgba(0, 0, 0, 0.5);" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="50.0" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" prefHeight="173.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="50.0" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Button mnemonicParsing="false" onAction="#menuBtnPressed" text="Quit" GridPane.rowIndex="2">
<GridPane.margin>
<Insets left="20.0" />
</GridPane.margin>
</Button>
<Button alignment="CENTER_RIGHT" contentDisplay="RIGHT" mnemonicParsing="false" onAction="#startBtnPressed" text="Start Game" GridPane.columnIndex="2" GridPane.halignment="RIGHT" GridPane.rowIndex="2">
<GridPane.margin>
<Insets right="20.0" />
</GridPane.margin>
</Button>
<Label alignment="CENTER" contentDisplay="CENTER" text="Map: MapNameHere" textFill="WHITE" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="2">
<font>
<Font size="16.0" />
</font>
</Label>
<GridPane fx:id="playerContainer" GridPane.columnSpan="3" GridPane.rowIndex="1">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
</GridPane>
</children>
</GridPane>
<Label alignment="TOP_CENTER" text="Get Ready For The Next Race" textFill="#fffdfd" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="20.0">
<font>
<Font size="22.0" />
</font>
</Label>
</children>
</AnchorPane>
<AnchorPane fx:id="specPane" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<TableView prefHeight="200.0" prefWidth="200.0" style="-fx-background-color: rgba(60, 60, 60, 1);" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columns>
<TableColumn prefWidth="173.0" text="Spectators" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</children>
</AnchorPane>
</items>
</SplitPane>
</children>
</AnchorPane>

@ -7,7 +7,7 @@
<fx:include fx:id="start" source="start.fxml" />
<fx:include fx:id="connection" source="connect.fxml" />
<fx:include fx:id="finish" source="finish.fxml" />
<fx:include fx:id="host" source="hostlobby.fxml" />
<fx:include fx:id="host" source="hostgame.fxml" />
<fx:include fx:id="title" source="titleScreen.fxml" />
<fx:include fx:id="lobby" source="lobby.fxml" />
</children>

@ -1,5 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.chart.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
@ -22,17 +28,21 @@
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?>
<SplitPane fx:id="race" dividerPositions="1.0" prefHeight="431.0" prefWidth="610.0" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="visualiser.Controllers.RaceController">
<SplitPane fx:id="racePane" dividerPositions="1.0" prefHeight="431.0" prefWidth="610.0" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="visualiser.Controllers.RaceController">
<items>
<GridPane fx:id="canvasBase">
<StackPane fx:id="newPane" prefHeight="150.0" prefWidth="200.0">
<children>
<AnchorPane>
<children>
<GridPane fx:id="canvasBase" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Pane prefHeight="200.0" prefWidth="400.0" GridPane.halignment="LEFT" GridPane.valignment="TOP">
</GridPane>
<Pane prefHeight="200.0" prefWidth="400.0" visible="false">
<children>
<Accordion>
<panes>
@ -52,7 +62,8 @@
<RadioButton fx:id="hideAnnoRBtn" mnemonicParsing="false" text="Hidden" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="200.0">
<toggleGroup>
<ToggleGroup fx:id="annoToggleGroup" />
</toggleGroup></RadioButton>
</toggleGroup>
</RadioButton>
<RadioButton fx:id="showAnnoRBtn" mnemonicParsing="false" text="Visible" toggleGroup="$annoToggleGroup" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="225.0" />
<RadioButton fx:id="partialAnnoRBtn" mnemonicParsing="false" text="Partial" toggleGroup="$annoToggleGroup" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="250.0" />
<RadioButton fx:id="importantAnnoRBtn" mnemonicParsing="false" text="Important" toggleGroup="$annoToggleGroup" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="275.0" />
@ -74,31 +85,34 @@
</Accordion>
</children>
</Pane>
<Label fx:id="timer" layoutX="45.0" layoutY="146.0" maxHeight="20.0" text="0:0" AnchorPane.bottomAnchor="0.0" AnchorPane.rightAnchor="0.0" GridPane.halignment="RIGHT" GridPane.valignment="BOTTOM">
<Label fx:id="timer" maxHeight="20.0" text="0:0" AnchorPane.bottomAnchor="0.0" AnchorPane.rightAnchor="0.0">
<font>
<Font name="System Bold" size="15.0" />
</font>
</Label>
<Label fx:id="FPS" text="FPS: 0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" GridPane.halignment="LEFT" GridPane.valignment="BOTTOM">
<Label fx:id="FPS" text="FPS: 0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0">
<font>
<Font name="System Bold" size="15.0" />
</font>
</Label>
<Label fx:id="timeZone" text="Label" GridPane.halignment="RIGHT" GridPane.valignment="BOTTOM">
<GridPane.margin>
<Insets bottom="20.0" />
</GridPane.margin>
<Label fx:id="timeZone" text="Label" AnchorPane.bottomAnchor="0.0" AnchorPane.rightAnchor="0.0">
<font>
<Font name="System Bold" size="15.0" />
</font>
<padding>
<Insets bottom="20.0" />
</padding>
</Label>
<StackPane fx:id="arrowPane" alignment="TOP_RIGHT" mouseTransparent="true" prefHeight="150.0" prefWidth="150.0" snapToPixel="false">
<StackPane fx:id="arrowPane" alignment="TOP_RIGHT" mouseTransparent="true" prefHeight="150.0" prefWidth="150.0" snapToPixel="false" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<fx:include fx:id="arrow" source="arrow.fxml" />
</children>
</StackPane>
<Label fx:id="tutorialText" alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" style="-fx-border-color: orange; -fx-border-radius: 5px; -fx-background-color: #ffffcc; -fx-text-fill: #3399ff; -fx-border-width: 3; -fx-border-insets: -3;" text="This is the tutorial text" visible="false" AnchorPane.leftAnchor="150.0" AnchorPane.rightAnchor="150.0" AnchorPane.topAnchor="100.0" />
</children>
</GridPane>
</AnchorPane>
</children>
</StackPane>
<AnchorPane layoutX="450.0" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="200.0" GridPane.columnIndex="1">
<children>
<TableView fx:id="boatInfoTable" layoutX="-2.0" prefHeight="265.0" prefWidth="242.0" AnchorPane.bottomAnchor="164.0" AnchorPane.leftAnchor="-2.0" AnchorPane.rightAnchor="-62.0" AnchorPane.topAnchor="0.0">

@ -1,10 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<AnchorPane fx:id="titleWrapper" maxHeight="600.0" maxWidth="800.0" minHeight="600.0" minWidth="800.0" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="visualiser.Controllers.TitleController">
<children>
<GridPane layoutY="39.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
@ -40,7 +43,7 @@
<Button layoutX="28.0" layoutY="152.0" mnemonicParsing="false" onAction="#controlBtnPressed" text="Controls" />
</children>
</Pane>
<Pane prefHeight="20.0" prefWidth="20.0" style="-fx-background-color: #6be6ff;" GridPane.columnSpan="4" GridPane.rowSpan="4">
<Pane fx:id="menuPane" prefHeight="20.0" prefWidth="20.0" style="-fx-background-color: #6be6ff;" GridPane.columnSpan="4" GridPane.rowSpan="4">
<children>
<ImageView fx:id="imgBoat" fitHeight="404.0" fitWidth="296.0" layoutX="268.0" pickOnBounds="true" preserveRatio="true">
<image>
@ -77,6 +80,7 @@
<Font name="Comic Sans MS Bold" size="16.0" />
</font>
</Button>
<Label fx:id="tutorialLabel" alignment="CENTER" layoutX="94.0" layoutY="223.0" onMouseClicked="#tutorialStartPressed" prefHeight="167.0" prefWidth="206.0" style="-fx-shape: &quot;M 45.673,0 C 67.781,0 85.703,12.475 85.703,27.862 C 85.703,43.249 67.781,55.724 45.673,55.724 C 38.742,55.724 32.224,54.497 26.539,52.34 C 15.319,56.564 0,64.542 0,64.542 C 0,64.542 9.989,58.887 14.107,52.021 C 15.159,50.266 15.775,48.426 16.128,46.659 C 9.618,41.704 5.643,35.106 5.643,27.862 C 5.643,12.475 23.565,0 45.673,0 M 45.673,2.22 C 24.824,2.22 7.862,13.723 7.862,27.863 C 7.862,34.129 11.275,40.177 17.472,44.893 L 18.576,45.734 L 18.305,47.094 C 17.86,49.324 17.088,51.366 16.011,53.163 C 15.67,53.73 15.294,54.29 14.891,54.837 C 18.516,53.191 22.312,51.561 25.757,50.264 L 26.542,49.968 L 27.327,50.266 C 32.911,52.385 39.255,53.505 45.673,53.505 C 66.522,53.505 83.484,42.002 83.484,27.862 C 83.484,13.722 66.522,2.22 45.673,2.22 L 45.673,2.22 z &quot;; -fx-background-color: black, white; -fx-background-insets: 0,1; -fx-padding: 50;" text="How do you play this game? Click here!" textAlignment="CENTER" />
</children>
</Pane>
</children>

@ -1,47 +0,0 @@
//package seng302.Mock;
//
//import org.junit.Before;
//import org.junit.Test;
//
//import java.util.HashMap;
//import java.util.Map;
//
//import static org.junit.Assert.assertEquals;
//
///**
// * Created by jjg64 on 21/04/17.
// */
//public class BoatsXMLTest {
// private BoatXMLReader boatXMLReader;
//
// @Before
// public void setup() {
// try {
// boatXMLReader = new BoatXMLReader("mockXML/boatXML/boatTest.xml", false);
// } catch (Exception e) {
// e.printStackTrace();
// //fail("Cannot find mockXML/raceXML/raceTest.xml in the resources folder");
// }
// }
//
// @Test
// public void testInvalidParticipant() {
// Map<Integer, StreamedBoat> inputParticipants = new HashMap<>();
// inputParticipants.put(420, new StreamedBoat(420));
// boatXMLReader.setParticipants(inputParticipants);
// boatXMLReader.read();
// assertEquals(boatXMLReader.getBoats().size(), 0);
// }
//
// @Test
// public void testValidParticipant() {
// Map<Integer, StreamedBoat> inputParticipants = new HashMap<>();
// inputParticipants.put(101, new StreamedBoat(101));
// boatXMLReader.setParticipants(inputParticipants);
// boatXMLReader.read();
// assertEquals(boatXMLReader.getBoats().size(), 1);
// StreamedBoat boat = (StreamedBoat) boatXMLReader.getBoats().get(0);
// assertEquals(boat.getSourceID(), 101);
// }
//
//}

@ -1,26 +0,0 @@
//package seng302.Mock;
//
//import org.junit.Test;
//import org.xml.sax.SAXException;
//
//import javax.xml.parsers.ParserConfigurationException;
//import java.io.IOException;
//import java.text.ParseException;
//
///**
// * Created by jjg64 on 1/05/17.
// */
//public class FailBoatXMLTest {
// private final String path = "mockXML/boatXML/";
//
// @Test(expected = NumberFormatException.class)
// public void invalidSourceID() throws SAXException, ParserConfigurationException, ParseException, IOException {
// new BoatXMLReader(path + "invalidSourceID.xml");
// }
//
// @Test(expected = NullPointerException.class)
// public void insufficientInformation() throws SAXException, ParserConfigurationException, ParseException, IOException {
// new BoatXMLReader(path + "insufficientInformation.xml");
// }
//
//}

@ -1,345 +0,0 @@
//package shared.model;
//
//
//import mock.model.Polars;
//import org.junit.Before;
//import org.junit.Ignore;
//import org.junit.Test;
//import org.mockito.Mockito;
//import org.xml.sax.SAXException;
//
//import javax.xml.parsers.ParserConfigurationException;
//import java.io.IOException;
//import java.text.ParseException;
//import java.util.ArrayList;
//
//import static org.junit.Assert.*;
//import static org.mockito.Mockito.*;
//
///**
// * Created by esa46 on 15/03/17.
// */
//public class RaceTest{
//
//
// private CompoundMark ORIGIN;
//
// private CompoundMark THREE_NM_FROM_ORIGIN;
//
// private CompoundMark FIFTEEN_NM_FROM_ORIGIN;
//
// private ArrayList<Leg> TEST_LEGS = new ArrayList<>();
//
// private int START_LEG_DISTANCE = 3;
// private int MIDDLE_LEG_DISTANCE = 12;
//
// private Leg START_LEG;
//
// private Leg MIDDLE_LEG;
//
// private Leg FINISH_LEG;
//
//
// @Before
// public void setUp() {
//
// ORIGIN = new CompoundMark(
// 1,
// "origin compound",
// new Mark(1, "test origin 1", new GPSCoordinate(0, 0)) );
//
//
// THREE_NM_FROM_ORIGIN = new CompoundMark(
// 2,
// "3 NM from origin compound",
// new Mark(2, "test mark 2", new GPSCoordinate(0.050246769, 0)) );
//
//
// FIFTEEN_NM_FROM_ORIGIN = new CompoundMark(
// 3,
// "15 NM from origin compound",
// new Mark(3, "test mark 3", new GPSCoordinate(0.251233845, 0)) );
//
//
// START_LEG = new Leg("Start", ORIGIN, THREE_NM_FROM_ORIGIN, 0);
//
//
// MIDDLE_LEG = new Leg("Middle", THREE_NM_FROM_ORIGIN, FIFTEEN_NM_FROM_ORIGIN, 1);
//
//
// FINISH_LEG = new Leg("Finish", FIFTEEN_NM_FROM_ORIGIN, FIFTEEN_NM_FROM_ORIGIN, 2);
//
//
// TEST_LEGS.add(START_LEG);
// TEST_LEGS.add(MIDDLE_LEG);
// TEST_LEGS.add(FINISH_LEG);
// }
//
// @Ignore
// @Test
// public void countdownTimerSendsBoatLocations() {
//
// try {
// MockOutput mockOutput = Mockito.mock(MockOutput.class);
// BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml", new Polars());
// RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
// Race testRace = new Race(raceDataSource, mockOutput);
// testRace.initialiseBoats();
// testRace.countdownTimer.handle(1);
// verify(mockOutput, atLeast(boatDataSource.getBoats().size())).parseBoatLocation(anyInt(), anyDouble(), anyDouble(), anyDouble(), anyDouble(), anyLong());
//
// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
// e.printStackTrace();
// fail();
// }
// }
//
// @Ignore
// @Test
// public void countdownTimerSendsRaceStatusMessages() {
//
// try {
// MockOutput mockOutput = Mockito.mock(MockOutput.class);
// RaceDataSource dataSource = new RaceXMLReader("mockXML/raceTest.xml", new BoatXMLReader("mockXML/boatTest.xml", new Polars()));
// Race testRace = new Race(dataSource, mockOutput);
// testRace.initialiseBoats();
// testRace.countdownTimer.handle(1);
// verify(mockOutput, atLeast(1)).parseRaceStatus(any());
//
// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
// e.printStackTrace();
// fail();
// }
// }
//
// @Ignore
// @Test
// public void checkPositionFinishedUpdatesNumberFinishedBoats() {
//
// try {
// MockOutput mockOutput = Mockito.mock(MockOutput.class);
// RaceDataSource dataSource = new RaceXMLReader("mockXML/raceTest.xml", new BoatXMLReader("mockXML/boatTest.xml", new Polars()));
// Race testRace = new Race(dataSource, mockOutput);
// testRace.initialiseBoats();
// Boat testBoat = testRace.getBoats().get(0);
// testBoat.setCurrentLeg(FINISH_LEG);
// testBoat.setDistanceTravelledInLeg(1);
// testRace.checkPosition(testBoat, 1);
//
// assertEquals(testRace.getNumberOfActiveBoats(), 0);
//
// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
// e.printStackTrace();
// fail();
// }
// }
//
// @Ignore
// @Test
// public void checkPositionSetFinishedBoatVelocityTo0() {
//
// try {
// MockOutput mockOutput = Mockito.mock(MockOutput.class);
// RaceDataSource dataSource = new RaceXMLReader("mockXML/raceTest.xml", new BoatXMLReader("mockXML/boatTest.xml", new Polars()));
// Race testRace = new Race(dataSource, mockOutput);
// testRace.initialiseBoats();
// Boat testBoat = testRace.getBoats().get(0);
// testBoat.setCurrentLeg(FINISH_LEG);
// testBoat.setDistanceTravelledInLeg(1);
// testRace.checkPosition(testBoat, 1);
//
// assertEquals(testBoat.getCurrentSpeed(), 0, 1e-8);
//
// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
// e.printStackTrace();
// fail();
// }
// }
//
// @Ignore
// @Test
// public void checkPositionSetsFinishTime() {
//
// try {
// MockOutput mockOutput = Mockito.mock(MockOutput.class);
// RaceDataSource dataSource = new RaceXMLReader("mockXML/raceTest.xml", new BoatXMLReader("mockXML/boatTest.xml", new Polars()));
// Race testRace = new Race(dataSource, mockOutput);
// testRace.initialiseBoats();
// Boat testBoat = testRace.getBoats().get(0);
// testBoat.setCurrentLeg(FINISH_LEG);
// testBoat.setDistanceTravelledInLeg(1);
// testRace.checkPosition(testBoat, 1);
//
// assertEquals(testBoat.getTimeFinished(), 1, 1e-8);
//
// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
// e.printStackTrace();
// fail();
// }
// }
//
// @Ignore
// @Test
// public void checkPositionUnfinishedDoesntUpdateNumberFinishedBoats() {
//
// try {
// MockOutput mockOutput = Mockito.mock(MockOutput.class);
// RaceDataSource dataSource = new RaceXMLReader("mockXML/raceTest.xml", new BoatXMLReader("mockXML/boatTest.xml", new Polars()));
// Race testRace = new Race(dataSource, mockOutput);
// testRace.initialiseBoats();
// Boat testBoat = testRace.getBoats().get(0);
// testBoat.setCurrentLeg(START_LEG);
// testBoat.setDistanceTravelledInLeg(START_LEG_DISTANCE);
// testRace.checkPosition(testBoat, 1);
//
// assertEquals(testRace.getNumberOfActiveBoats(), 1);
//
// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
// e.printStackTrace();
// fail();
// }
// }
//
//
// @Ignore
// @Test
// public void distanceTravelledBeforeUpdatingLegIsRetained() {
//
// try {
// MockOutput mockOutput = Mockito.mock(MockOutput.class);
// RaceDataSource dataSource = new RaceXMLReader("mockXML/raceTest.xml", new BoatXMLReader("mockXML/boatTest.xml", new Polars()));
// Race testRace = new Race(dataSource, mockOutput);
// testRace.initialiseBoats();
// Boat testBoat = testRace.getBoats().get(0);
// testBoat.setCurrentLeg(START_LEG);
// testBoat.setDistanceTravelledInLeg(START_LEG_DISTANCE + 1);
// testRace.checkPosition(testBoat, 0);
//
// assertEquals(testBoat.getDistanceTravelledInLeg(), 1, 1e-7);
//
// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
// e.printStackTrace();
// fail();
// }
// }
//
// @Ignore
// @Test
// public void doNotFinishAnswersYesIf100PercentChance() {
//
// try {
// MockOutput mockOutput = Mockito.mock(MockOutput.class);
// BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml", new Polars());
// RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
// Race testRace = new Race(raceDataSource, mockOutput);
//
// testRace.setDnfChance(100);
// assertTrue(testRace.doNotFinish());
//
// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
// e.printStackTrace();
// fail();
// }
// }
//
// @Ignore
// @Test
// public void doNotFinishAnswersNoIf0PercentChance() {
//
// try {
// MockOutput mockOutput = Mockito.mock(MockOutput.class);
// BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml", new Polars());
// RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
// Race testRace = new Race(raceDataSource, mockOutput);
// testRace.setDnfChance(0);
// assertFalse(testRace.doNotFinish());
//
// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
// e.printStackTrace();
// fail();
// }
// }
//
// @Ignore
// @Test
// public void boatsAreSetToDNF() {
// try {
// MockOutput mockOutput = Mockito.mock(MockOutput.class);
// BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml", new Polars());
// RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
// Race testRace = new Race(raceDataSource, mockOutput);
// testRace.setDnfChance(100);
// Boat testBoat = testRace.getBoats().get(0);
// testBoat.setCurrentLeg(START_LEG);
// testBoat.setDistanceTravelledInLeg(START_LEG_DISTANCE + 1);
// testRace.checkPosition(testBoat, 1);
// assertEquals(testBoat.getCurrentLeg().getName(), "DNF");
//
// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
// e.printStackTrace();
// fail();
// }
//
// }
//
// @Ignore
// @Test
// public void updatePositionIgnoresFinishedBoats() {
// try {
// MockOutput mockOutput = Mockito.mock(MockOutput.class);
// BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml", new Polars());
// RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
// Race testRace = new Race(raceDataSource, mockOutput);
// Boat testBoat = testRace.getBoats().get(0);
// testBoat.setCurrentLeg(FINISH_LEG);
// testBoat.setCurrentPosition(ORIGIN.getAverageGPSCoordinate());
// testRace.updatePosition(testBoat, 1, 1);
// assertEquals(testBoat.getCurrentPosition(), ORIGIN.getAverageGPSCoordinate());
//
// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
// e.printStackTrace();
// fail();
// }
// }
//
// @Ignore
// @Test
// public void updatePositionChangesBoatPosition() {
// try {
// MockOutput mockOutput = Mockito.mock(MockOutput.class);
// BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml", new Polars());
// RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
// Race testRace = new Race(raceDataSource, mockOutput);
// testRace.initialiseBoats();
// Boat testBoat = testRace.getBoats().get(0);
// testBoat.setCurrentLeg(START_LEG);
// testBoat.setDistanceTravelledInLeg(START_LEG_DISTANCE - 1);
// testBoat.setCurrentPosition(ORIGIN.getAverageGPSCoordinate());
// testRace.updatePosition(testBoat, 100, 100);
// assertFalse(testBoat.getCurrentPosition() == ORIGIN.getAverageGPSCoordinate());
// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
// e.printStackTrace();
// fail();
// }
// }
//
// @Ignore
// @Test
// public void windDirectionCorrectValues(){
//// try {
//// MockOutput mockOutput = Mockito.mock(MockOutput.class);
//// BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml");
//// RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
//// Race testRace = new Race(raceDataSource, mockOutput);
//// testRace.setChangeWind(1);
//// testRace.setWindDir(65535);
//// testRace.changeWindDir();
//// assertEquals(100, testRace.getWind());
//// } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
//// e.printStackTrace();
//// fail();
//// }
// }
//
//
//}

@ -1,100 +0,0 @@
//package visualiser.model;
//
//
//import org.junit.Before;
//import org.junit.Test;
//import shared.dataInput.RaceXMLReader;
//import shared.dataInput.XMLReader;
//import shared.exceptions.InvalidRaceDataException;
//import shared.model.GPSCoordinate;
//
//import java.nio.charset.StandardCharsets;
//import java.util.List;
//
//import static org.junit.Assert.assertEquals;
//import static org.junit.Assert.fail;
//
///**
// * Tests only work on the current version of mockXML/raceXML/raceTest.xml
// */
//public class VisualiserRaceTest {
// private RaceXMLReader streamedCourseXMLReader;
// private List<GPSCoordinate> boundary;
//
// @Before
// public void setup() {
// try {
// streamedCourseXMLReader = new RaceXMLReader(XMLReader.readXMLFileToString("mockXML/raceXML/raceTest.xml", StandardCharsets.UTF_8));
// boundary = streamedCourseXMLReader.getBoundary();
// } catch (InvalidRaceDataException e) {
// e.printStackTrace();
// fail("Cannot find mockXML/raceXML/raceTest.xml in the resources folder");
// }
// }
//
// @Test
// public void testAllBoundaryPointsRead() {
// assertEquals(boundary.size(), 10);
// }
//
// @Test
// public void testBoundaryPointData() {
// // First point
// assertEquals(boundary.get(0).getLatitude(), -36.8325, 1e-6);
// assertEquals(boundary.get(0).getLongitude(), 174.8325, 1e-6);
//
// // Last point
// assertEquals(boundary.get(boundary.size() - 1).getLatitude(), -36.83417, 1e-6);
// assertEquals(boundary.get(boundary.size() - 1).getLongitude(), 174.84767, 1e-6);
// }
//
// @Test
// public void testMapEdges() {
// double maxLatitude = streamedCourseXMLReader.getMapBottomRight().getLatitude() - streamedCourseXMLReader.getPadding();
// double maxLongitude = streamedCourseXMLReader.getMapBottomRight().getLongitude() - streamedCourseXMLReader.getPadding();
// double minLatitude = streamedCourseXMLReader.getMapTopLeft().getLatitude() - streamedCourseXMLReader.getPadding();
// double minLongitude = streamedCourseXMLReader.getMapTopLeft().getLongitude() - streamedCourseXMLReader.getPadding();
//
// assertEquals(maxLatitude, -36.81033, 1e-6);
// assertEquals(maxLongitude, 174.88217, 1e-6);
// assertEquals(minLatitude, -36.83417, 1e-6);
// assertEquals(minLongitude, 174.81983, 1e-6);
// }
//
// @Test
// public void testRaceSettings() {
//
// }
//
// @Test
// public void correctLegSequence() {
// List<Leg> legs = streamedCourseXMLReader.getLegs();
// String[] expectedNames = {
// "StartLine",
// "M1",
// "M2",
// "Gate"
// };
// for(int i = 0; i < legs.size(); i++) {
// assertEquals(expectedNames[i], legs.get(i).getName());
// }
// }
//
// /**
// * raceTest.xml is not compliant with this test. Markers are positioned far out of bounds.
// */
// @Test
// @Ignore
// public void markersWithinRaceBoundaries() {
// GPSCoordinate topLeft = streamedCourseXMLReader.getMapTopLeft();
// GPSCoordinate bottomRight = streamedCourseXMLReader.getMapBottomRight();
//
// for(Marker compoundMark : streamedCourseXMLReader.getMarkers()) {
// GPSCoordinate centre = compoundMark.getAverageGPSCoordinate();
// assertTrue(centre.getLatitude() < bottomRight.getLatitude());
// assertTrue(centre.getLatitude() > topLeft.getLatitude());
// assertTrue(centre.getLongitude() > bottomRight.getLongitude());
// assertTrue(centre.getLongitude() < topLeft.getLongitude());
// }
// }
//}

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_111" class="java.beans.XMLDecoder">
<java version="1.8.0_144" class="java.beans.XMLDecoder">
<object class="java.util.HashMap">
<void method="put">
<string>SPACE</string>
@ -10,8 +10,8 @@
<object class="visualiser.gameController.Keys.SailsToggleKey"/>
</void>
<void method="put">
<string>DOWN</string>
<object class="visualiser.gameController.Keys.DownWindKey"/>
<string>LEFT</string>
<object class="visualiser.gameController.Keys.UpWindKey"/>
</void>
<void method="put">
<string>X</string>
@ -22,12 +22,12 @@
<object class="visualiser.gameController.Keys.TackGybeKey"/>
</void>
<void method="put">
<string>Z</string>
<object class="visualiser.gameController.Keys.ZoomInKey"/>
<string>RIGHT</string>
<object class="visualiser.gameController.Keys.DownWindKey"/>
</void>
<void method="put">
<string>UP</string>
<object class="visualiser.gameController.Keys.UpWindKey"/>
<string>Z</string>
<object class="visualiser.gameController.Keys.ZoomInKey"/>
</void>
</object>
</java>

Loading…
Cancel
Save