Merge branch 'master' into storyA_sounds
# Conflicts: # racevisionGame/src/main/java/mock/model/RaceServer.java # racevisionGame/src/main/java/visualiser/Controllers/HostController.java # racevisionGame/src/main/java/visualiser/Controllers/MainController.java # racevisionGame/src/main/java/visualiser/Controllers/TitleController.java
@@ -106,6 +106,9 @@
|
|||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||||
<version>2.8.1</version>
|
<version>2.8.1</version>
|
||||||
|
<configuration>
|
||||||
|
<dependencyLocationsEnabled>false</dependencyLocationsEnabled>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</reporting>
|
</reporting>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class App extends Application {
|
|||||||
public void start(Stage primaryStage) {
|
public void start(Stage primaryStage) {
|
||||||
try {
|
try {
|
||||||
//TODO should read a configuration file to configure server?
|
//TODO should read a configuration file to configure server?
|
||||||
Event raceEvent = new Event(false);
|
Event raceEvent = new Event(false, 0);
|
||||||
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -14,4 +14,12 @@
|
|||||||
|
|
||||||
<url>https://eng-git.canterbury.ac.nz/SENG302-2016/team-7</url>
|
<url>https://eng-git.canterbury.ac.nz/SENG302-2016/team-7</url>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>central</id>
|
||||||
|
<name>Maven Central</name>
|
||||||
|
<url>http://repo1.maven.org/maven2/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -25,10 +25,30 @@
|
|||||||
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
|
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mockito</groupId>
|
<groupId>org.mockito</groupId>
|
||||||
<artifactId>mockito-all</artifactId>
|
<artifactId>mockito-core</artifactId>
|
||||||
<version>1.9.5</version>
|
<version>2.9.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Maven Dependencies block START-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.bytebuddy</groupId>
|
||||||
|
<artifactId>byte-buddy</artifactId>
|
||||||
|
<version>1.7.0</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.bytebuddy</groupId>
|
||||||
|
<artifactId>byte-buddy-agent</artifactId>
|
||||||
|
<version>1.7.0</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.objenesis</groupId>
|
||||||
|
<artifactId>objenesis</artifactId>
|
||||||
|
<version>2.6</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- Maven Dependencies block END-->
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -46,14 +66,17 @@
|
|||||||
<version>15.0</version>
|
<version>15.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.geotools</groupId>
|
<groupId>org.geotools</groupId>
|
||||||
<artifactId>gt-referencing</artifactId>
|
<artifactId>gt-referencing</artifactId>
|
||||||
<version>9.0</version>
|
<version>9.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>JavaInteractiveMesh</groupId>
|
||||||
|
<artifactId>STLImporter</artifactId>
|
||||||
|
<version>0.7</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
@@ -72,7 +95,11 @@
|
|||||||
<url>http://download.osgeo.org/webdav/geotools/</url>
|
<url>http://download.osgeo.org/webdav/geotools/</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
|
||||||
|
<repository>
|
||||||
|
<id>interactivemesh</id>
|
||||||
|
<name>Interactive Mesh</name>
|
||||||
|
<url>http://umbrasheep.com:8888/repository/internal/</url>
|
||||||
|
</repository>
|
||||||
|
|
||||||
|
|
||||||
</repositories>
|
</repositories>
|
||||||
@@ -163,6 +190,9 @@
|
|||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||||
<version>2.8.1</version>
|
<version>2.8.1</version>
|
||||||
|
<configuration>
|
||||||
|
<dependencyLocationsEnabled>false</dependencyLocationsEnabled>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</reporting>
|
</reporting>
|
||||||
|
|||||||
@@ -61,12 +61,7 @@ public class ConnectionAcceptor implements Runnable {
|
|||||||
*/
|
*/
|
||||||
private SourceIdAllocator sourceIdAllocator;
|
private SourceIdAllocator sourceIdAllocator;
|
||||||
|
|
||||||
//race xml sequence number
|
|
||||||
private short raceXMLSequenceNumber;
|
|
||||||
//boat xml sequence number
|
|
||||||
private short boatXMLSequenceNumber;
|
|
||||||
//regatta xml sequence number
|
|
||||||
private short regattaXMLSequenceNumber;
|
|
||||||
//
|
//
|
||||||
private RaceLogic raceLogic = null;
|
private RaceLogic raceLogic = null;
|
||||||
|
|
||||||
@@ -210,76 +205,5 @@ public class ConnectionAcceptor implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the Race XML to send.
|
|
||||||
* @param raceXml XML to send to the Client.
|
|
||||||
*/
|
|
||||||
public void setRaceXml(String raceXml) {
|
|
||||||
//Create the message.
|
|
||||||
XMLMessage message = this.createXMLMessage(raceXml, XMLMessageType.RACE);
|
|
||||||
|
|
||||||
//Place it in LatestMessages.
|
|
||||||
this.latestMessages.setRaceXMLMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the Regatta XMl to send.
|
|
||||||
* @param regattaXml XML to send to Client.
|
|
||||||
*/
|
|
||||||
public void setRegattaXml(String regattaXml) {
|
|
||||||
//Create the message.
|
|
||||||
XMLMessage message = this.createXMLMessage(regattaXml, XMLMessageType.REGATTA);
|
|
||||||
|
|
||||||
//Place it in LatestMessages.
|
|
||||||
this.latestMessages.setRegattaXMLMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the Boats XML to send.
|
|
||||||
* @param boatsXml XMl to send to the Client.
|
|
||||||
*/
|
|
||||||
public void setBoatsXml(String boatsXml) {
|
|
||||||
//Create the message.
|
|
||||||
XMLMessage message = this.createXMLMessage(boatsXml, XMLMessageType.BOAT);
|
|
||||||
|
|
||||||
//Place it in LatestMessages.
|
|
||||||
this.latestMessages.setBoatXMLMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an XMLMessage of a specified subtype using the xml contents string.
|
|
||||||
* @param xmlString The contents of the xml file.
|
|
||||||
* @param messageType The subtype of xml message (race, regatta, boat).
|
|
||||||
* @return The created XMLMessage object.
|
|
||||||
*/
|
|
||||||
private XMLMessage createXMLMessage(String xmlString, XMLMessageType messageType) {
|
|
||||||
|
|
||||||
//Get the correct sequence number to use, and increment it.
|
|
||||||
short sequenceNumber = 0;
|
|
||||||
if (messageType == XMLMessageType.RACE) {
|
|
||||||
sequenceNumber = this.raceXMLSequenceNumber;
|
|
||||||
this.raceXMLSequenceNumber++;
|
|
||||||
|
|
||||||
} else if (messageType == XMLMessageType.BOAT) {
|
|
||||||
sequenceNumber = this.boatXMLSequenceNumber;
|
|
||||||
this.boatXMLSequenceNumber++;
|
|
||||||
|
|
||||||
} else if (messageType == XMLMessageType.REGATTA) {
|
|
||||||
sequenceNumber = this.regattaXMLSequenceNumber;
|
|
||||||
this.regattaXMLSequenceNumber++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Create the message.
|
|
||||||
XMLMessage message = new XMLMessage(
|
|
||||||
XMLMessage.currentVersionNumber,
|
|
||||||
AckSequencer.getNextAckNum(),
|
|
||||||
System.currentTimeMillis(),
|
|
||||||
messageType,
|
|
||||||
sequenceNumber,
|
|
||||||
xmlString);
|
|
||||||
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import mock.dataInput.PolarParser;
|
|||||||
import mock.exceptions.EventConstructionException;
|
import mock.exceptions.EventConstructionException;
|
||||||
import mock.model.*;
|
import mock.model.*;
|
||||||
import mock.model.commandFactory.CompositeCommand;
|
import mock.model.commandFactory.CompositeCommand;
|
||||||
|
import mock.model.wind.RandomWindGenerator;
|
||||||
|
import mock.model.wind.ShiftingWindGenerator;
|
||||||
|
import mock.model.wind.WindGenerator;
|
||||||
import mock.xml.RaceXMLCreator;
|
import mock.xml.RaceXMLCreator;
|
||||||
import network.Messages.LatestMessages;
|
import network.Messages.LatestMessages;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
@@ -15,16 +18,16 @@ import shared.exceptions.InvalidRegattaDataException;
|
|||||||
import shared.exceptions.XMLReaderException;
|
import shared.exceptions.XMLReaderException;
|
||||||
import shared.model.Bearing;
|
import shared.model.Bearing;
|
||||||
import shared.model.Constants;
|
import shared.model.Constants;
|
||||||
|
import shared.xml.XMLUtilities;
|
||||||
|
|
||||||
import javax.xml.bind.JAXBException;
|
import javax.xml.bind.JAXBException;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
import javax.xml.transform.TransformerException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -68,6 +71,8 @@ public class Event {
|
|||||||
|
|
||||||
private Thread connectionThread;
|
private Thread connectionThread;
|
||||||
|
|
||||||
|
private int mapIndex;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -77,11 +82,32 @@ public class Event {
|
|||||||
* @param singlePlayer Whether or not to create a single player event.
|
* @param singlePlayer Whether or not to create a single player event.
|
||||||
* @throws EventConstructionException Thrown if we cannot create an Event for any reason.
|
* @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 boatsXMLFile = "mock/mockXML/boatTest.xml";
|
||||||
String regattaXMLFile = "mock/mockXML/regattaTest.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) {
|
if (singlePlayer) {
|
||||||
raceXMLFile = "mock/mockXML/raceSinglePlayer.xml";
|
raceXMLFile = "mock/mockXML/raceSinglePlayer.xml";
|
||||||
@@ -93,7 +119,9 @@ public class Event {
|
|||||||
//this.raceXML = RaceXMLCreator.alterRaceToWind(raceXMLFile, 90);
|
//this.raceXML = RaceXMLCreator.alterRaceToWind(raceXMLFile, 90);
|
||||||
this.raceXML = XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8);
|
this.raceXML = XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8);
|
||||||
this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(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.boatXML = XMLReader.readXMLFileToString(boatsXMLFile, StandardCharsets.UTF_8);
|
||||||
this.regattaXML = XMLReader.readXMLFileToString(regattaXMLFile, StandardCharsets.UTF_8);
|
this.regattaXML = XMLReader.readXMLFileToString(regattaXMLFile, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
@@ -104,6 +132,7 @@ public class Event {
|
|||||||
this.xmlFileType = XMLFileType.Contents;
|
this.xmlFileType = XMLFileType.Contents;
|
||||||
|
|
||||||
this.boatPolars = PolarParser.parse("mock/polars/acc_polars.csv");
|
this.boatPolars = PolarParser.parse("mock/polars/acc_polars.csv");
|
||||||
|
PolarParser.parseNewPolars("mock/polars/acc_polars.csv");
|
||||||
|
|
||||||
|
|
||||||
//Parse the XML files into data sources.
|
//Parse the XML files into data sources.
|
||||||
@@ -118,18 +147,14 @@ public class Event {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sourceIdAllocator = new SourceIdAllocator(raceDataSource.getParticipants());
|
|
||||||
this.compositeCommand = new CompositeCommand();
|
this.compositeCommand = new CompositeCommand();
|
||||||
this.latestMessages = new LatestMessages();
|
this.latestMessages = new LatestMessages();
|
||||||
|
|
||||||
//Create and start race.
|
WindGenerator windGenerator = new ShiftingWindGenerator(
|
||||||
WindGenerator windGenerator = new RandomWindGenerator(
|
|
||||||
Bearing.fromDegrees(225),
|
Bearing.fromDegrees(225),
|
||||||
Bearing.fromDegrees(215),
|
12
|
||||||
Bearing.fromDegrees(235),
|
);
|
||||||
12d,
|
|
||||||
8d,
|
|
||||||
16d );
|
|
||||||
RaceLogic newRace = new RaceLogic(
|
RaceLogic newRace = new RaceLogic(
|
||||||
new MockRace(
|
new MockRace(
|
||||||
boatDataSource,
|
boatDataSource,
|
||||||
@@ -146,6 +171,7 @@ public class Event {
|
|||||||
|
|
||||||
|
|
||||||
//Create connection acceptor.
|
//Create connection acceptor.
|
||||||
|
this.sourceIdAllocator = new SourceIdAllocator(newRace.getRace());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.connectionAcceptor = new ConnectionAcceptor(latestMessages, compositeCommand, sourceIdAllocator, newRace);
|
this.connectionAcceptor = new ConnectionAcceptor(latestMessages, compositeCommand, sourceIdAllocator, newRace);
|
||||||
@@ -158,8 +184,6 @@ public class Event {
|
|||||||
this.connectionThread = new Thread(connectionAcceptor, "Event.Start()->ConnectionAcceptor thread");
|
this.connectionThread = new Thread(connectionAcceptor, "Event.Start()->ConnectionAcceptor thread");
|
||||||
connectionThread.start();
|
connectionThread.start();
|
||||||
|
|
||||||
sendXMLs();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,18 +196,6 @@ public class Event {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends out each xml string, via the mock output
|
|
||||||
*/
|
|
||||||
private void sendXMLs() {
|
|
||||||
|
|
||||||
connectionAcceptor.setRegattaXml(regattaXML);
|
|
||||||
|
|
||||||
connectionAcceptor.setRaceXml(raceXML);
|
|
||||||
|
|
||||||
connectionAcceptor.setBoatsXml(boatXML);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//TODO remove this after demo on 18th august!
|
//TODO remove this after demo on 18th august!
|
||||||
/**
|
/**
|
||||||
@@ -192,9 +204,15 @@ public class Event {
|
|||||||
* @return String containing edited xml
|
* @return String containing edited xml
|
||||||
*/
|
*/
|
||||||
public static String setRaceXMLAtCurrentTimeToNow(String raceXML) {
|
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.
|
//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;
|
long secondsToAdd = millisecondsToAdd / 1000;
|
||||||
//Scale the time using our time scalar.
|
//Scale the time using our time scalar.
|
||||||
secondsToAdd = secondsToAdd / Constants.RaceTimeScale;
|
secondsToAdd = secondsToAdd / Constants.RaceTimeScale;
|
||||||
@@ -204,7 +222,6 @@ public class Event {
|
|||||||
raceXML = raceXML.replace("RACE_CREATION_TIME", dateFormat.format(creationTime));
|
raceXML = raceXML.replace("RACE_CREATION_TIME", dateFormat.format(creationTime));
|
||||||
raceXML = raceXML.replace("RACE_START_TIME", dateFormat.format(creationTime.plusSeconds(secondsToAdd)));
|
raceXML = raceXML.replace("RACE_START_TIME", dateFormat.format(creationTime.plusSeconds(secondsToAdd)));
|
||||||
|
|
||||||
|
|
||||||
return raceXML;
|
return raceXML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,12 @@ public class MockOutput implements RunnableWithFramePeriod {
|
|||||||
*/
|
*/
|
||||||
private LatestMessages latestMessages;
|
private LatestMessages latestMessages;
|
||||||
|
|
||||||
|
//These sequence number track the last race/boat/regatta xml message we've sent.
|
||||||
|
private int lastSentRaceNumber = -1;
|
||||||
|
|
||||||
|
private int lastSentBoatNumber = -1;
|
||||||
|
|
||||||
|
private int lastSentRegattaNumber = -1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -69,7 +74,6 @@ public class MockOutput implements RunnableWithFramePeriod {
|
|||||||
|
|
||||||
|
|
||||||
long previousFrameTime = System.currentTimeMillis();
|
long previousFrameTime = System.currentTimeMillis();
|
||||||
boolean sentXMLs = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -82,16 +86,26 @@ public class MockOutput implements RunnableWithFramePeriod {
|
|||||||
previousFrameTime = currentFrameTime;
|
previousFrameTime = currentFrameTime;
|
||||||
|
|
||||||
|
|
||||||
//Send XML messages.
|
|
||||||
if (!sentXMLs) {
|
|
||||||
|
|
||||||
|
//Send XML messages if needed.
|
||||||
|
|
||||||
|
if (lastSentRaceNumber != latestMessages.getRaceXMLMessage().getSequenceNumber()) {
|
||||||
|
lastSentRaceNumber = latestMessages.getRaceXMLMessage().getSequenceNumber();
|
||||||
outgoingMessages.put(latestMessages.getRaceXMLMessage());
|
outgoingMessages.put(latestMessages.getRaceXMLMessage());
|
||||||
outgoingMessages.put(latestMessages.getRegattaXMLMessage());
|
|
||||||
outgoingMessages.put(latestMessages.getBoatXMLMessage());
|
|
||||||
|
|
||||||
sentXMLs = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastSentBoatNumber != latestMessages.getBoatXMLMessage().getSequenceNumber()) {
|
||||||
|
lastSentBoatNumber = latestMessages.getBoatXMLMessage().getSequenceNumber();
|
||||||
|
outgoingMessages.put(latestMessages.getBoatXMLMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastSentRegattaNumber != latestMessages.getRegattaXMLMessage().getSequenceNumber()) {
|
||||||
|
lastSentRegattaNumber = latestMessages.getRegattaXMLMessage().getSequenceNumber();
|
||||||
|
outgoingMessages.put(latestMessages.getRegattaXMLMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
List<AC35Data> snapshot = latestMessages.getSnapshot();
|
List<AC35Data> snapshot = latestMessages.getSnapshot();
|
||||||
for (AC35Data message : snapshot) {
|
for (AC35Data message : snapshot) {
|
||||||
outgoingMessages.put(message);
|
outgoingMessages.put(message);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package mock.dataInput;
|
|||||||
|
|
||||||
|
|
||||||
import mock.exceptions.InvalidPolarFileException;
|
import mock.exceptions.InvalidPolarFileException;
|
||||||
|
import mock.model.NewPolars;
|
||||||
import mock.model.Polars;
|
import mock.model.Polars;
|
||||||
import shared.model.Bearing;
|
import shared.model.Bearing;
|
||||||
|
|
||||||
@@ -104,4 +105,90 @@ public class PolarParser {
|
|||||||
return polarTable;
|
return polarTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a filename, this function parses it and generates a Polar object, which can be queried for polar information.
|
||||||
|
* @param filename The filename to load and read data from (loaded as a resource).
|
||||||
|
*/
|
||||||
|
public static void parseNewPolars(String filename) throws InvalidPolarFileException {
|
||||||
|
NewPolars newPolars = new NewPolars();
|
||||||
|
|
||||||
|
|
||||||
|
//Open the file for reading.
|
||||||
|
InputStream fileStream = PolarParser.class.getClassLoader().getResourceAsStream(filename);
|
||||||
|
if (fileStream == null) {
|
||||||
|
throw new InvalidPolarFileException("Could not open polar data file: " + filename);
|
||||||
|
}
|
||||||
|
//Wrap it with buffered input stream to set encoding and buffer.
|
||||||
|
InputStreamReader in = null;
|
||||||
|
try {
|
||||||
|
in = new InputStreamReader(fileStream, "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InvalidPolarFileException("Unsupported encoding: UTF-8", e);
|
||||||
|
}
|
||||||
|
BufferedReader inputStream = new BufferedReader(in);
|
||||||
|
|
||||||
|
|
||||||
|
//We expect the polar data file to have the column headings:
|
||||||
|
// Tws, Twa0, Bsp0, Twa1, Bsp1, UpTwa, UpBsp, Twa2, Bsp2, Twa3, Bsp3, Twa4, Bsp4, Twa5, Bsp5, Twa6, Bsp6, DnTwa, DnBsp, Twa7, Bsp7
|
||||||
|
//and to have 7 rows of data.
|
||||||
|
//Angles are expected to be in degrees, and velocities in knots.
|
||||||
|
|
||||||
|
|
||||||
|
//We read data rows, and split them into arrays of elements.
|
||||||
|
ArrayList<String[]> dataRows = new ArrayList<>(7);
|
||||||
|
try {
|
||||||
|
//Heading row.
|
||||||
|
//We skip the heading row by reading it.
|
||||||
|
String headingRow = inputStream.readLine();
|
||||||
|
|
||||||
|
//Data rows.
|
||||||
|
while (inputStream.ready()) {
|
||||||
|
//Read line.
|
||||||
|
String dataRow = inputStream.readLine();
|
||||||
|
|
||||||
|
//Split line.
|
||||||
|
String[] dataElements = dataRow.split(",");
|
||||||
|
|
||||||
|
//Add to collection.
|
||||||
|
dataRows.add(dataElements);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new InvalidPolarFileException("Could not read from polar data file: " + filename, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Finished reading in data, now we need to construct polar rows and table from it.
|
||||||
|
//For each row...
|
||||||
|
int rowNumber = 0;
|
||||||
|
for (String[] row : dataRows) {
|
||||||
|
|
||||||
|
//For each pair of columns (the pair is angle, speed).
|
||||||
|
//We start at column 1 since column 0 is the wind speed column.
|
||||||
|
for (int i = 1; i < row.length; i += 2) {
|
||||||
|
|
||||||
|
//Add angle+speed=velocity estimate to polar table.
|
||||||
|
try {
|
||||||
|
|
||||||
|
//Add the polar value to the polar table
|
||||||
|
double windSpeedKnots = Double.parseDouble(row[0]);
|
||||||
|
double angleDegrees = Double.parseDouble(row[i]);
|
||||||
|
Bearing angle = Bearing.fromDegrees(angleDegrees);
|
||||||
|
double boatSpeedKnots = Double.parseDouble(row[i + 1]);
|
||||||
|
newPolars.addPolars(windSpeedKnots, angle, boatSpeedKnots);
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new InvalidPolarFileException("Could not convert (Row,Col): (" + rowNumber + "," + i +") = " + row[i] + " to a double.", e);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Increment row number.
|
||||||
|
rowNumber++;
|
||||||
|
|
||||||
|
}
|
||||||
|
newPolars.linearInterpolatePolars();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,11 @@ public class ClientConnection implements Runnable {
|
|||||||
*/
|
*/
|
||||||
private ConnectionStateEnum connectionState = ConnectionStateEnum.UNKNOWN;
|
private ConnectionStateEnum connectionState = ConnectionStateEnum.UNKNOWN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The source ID that has been allocated to the client.
|
||||||
|
* 0 means not allocated.
|
||||||
|
*/
|
||||||
|
private int allocatedSourceID = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -178,7 +183,7 @@ public class ClientConnection implements Runnable {
|
|||||||
|
|
||||||
RequestToJoin requestToJoin = waitForRequestToJoin();
|
RequestToJoin requestToJoin = waitForRequestToJoin();
|
||||||
|
|
||||||
int allocatedSourceID = 0;
|
allocatedSourceID = 0;
|
||||||
|
|
||||||
//If they want to participate, give them a source ID number.
|
//If they want to participate, give them a source ID number.
|
||||||
if (requestToJoin.getRequestType() == RequestToJoinEnum.PARTICIPANT) {
|
if (requestToJoin.getRequestType() == RequestToJoinEnum.PARTICIPANT) {
|
||||||
@@ -283,6 +288,10 @@ public class ClientConnection implements Runnable {
|
|||||||
if (this.controllerServerThread != null) {
|
if (this.controllerServerThread != null) {
|
||||||
this.controllerServerThread.interrupt();
|
this.controllerServerThread.interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (allocatedSourceID != 0) {
|
||||||
|
sourceIdAllocator.returnSourceID(allocatedSourceID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package mock.model;
|
package mock.model;
|
||||||
|
|
||||||
|
import mock.model.wind.WindGenerator;
|
||||||
import javafx.animation.AnimationTimer;
|
import javafx.animation.AnimationTimer;
|
||||||
|
import mock.model.collider.ColliderRegistry;
|
||||||
import mock.xml.*;
|
import mock.xml.*;
|
||||||
import network.Messages.BoatLocation;
|
import network.Messages.BoatLocation;
|
||||||
import network.Messages.BoatStatus;
|
import network.Messages.BoatStatus;
|
||||||
@@ -13,7 +15,6 @@ import shared.exceptions.BoatNotFoundException;
|
|||||||
import shared.enums.RoundingType;
|
import shared.enums.RoundingType;
|
||||||
import shared.model.*;
|
import shared.model.*;
|
||||||
import shared.model.Bearing;
|
import shared.model.Bearing;
|
||||||
import shared.model.Race;
|
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
@@ -27,7 +28,7 @@ import static java.lang.Math.cos;
|
|||||||
* Has a course, boats, boundaries, etc...
|
* Has a course, boats, boundaries, etc...
|
||||||
* Is responsible for simulating the race, and sending messages to a MockOutput instance.
|
* Is responsible for simulating the race, and sending messages to a MockOutput instance.
|
||||||
*/
|
*/
|
||||||
public class MockRace extends Race {
|
public class MockRace extends RaceState {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An observable list of boats in the race.
|
* An observable list of boats in the race.
|
||||||
@@ -40,6 +41,12 @@ public class MockRace extends Race {
|
|||||||
private List<GPSCoordinate> shrinkBoundary;
|
private List<GPSCoordinate> shrinkBoundary;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry for all collider object in this race
|
||||||
|
*/
|
||||||
|
private ColliderRegistry colliderRegistry;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The scale factor of the race.
|
* The scale factor of the race.
|
||||||
* See {@link Constants#RaceTimeScale}.
|
* See {@link Constants#RaceTimeScale}.
|
||||||
@@ -51,6 +58,14 @@ public class MockRace extends Race {
|
|||||||
*/
|
*/
|
||||||
private WindGenerator windGenerator;
|
private WindGenerator windGenerator;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The polars file to use for each boat.
|
||||||
|
*/
|
||||||
|
private Polars polars;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a race object with a given RaceDataSource, BoatDataSource, and RegattaDataSource and sends events to the given mockOutput.
|
* Constructs a race object with a given RaceDataSource, BoatDataSource, and RegattaDataSource and sends events to the given mockOutput.
|
||||||
* @param boatDataSource Data source for boat related data (yachts and marker boats).
|
* @param boatDataSource Data source for boat related data (yachts and marker boats).
|
||||||
@@ -62,13 +77,16 @@ public class MockRace extends Race {
|
|||||||
*/
|
*/
|
||||||
public MockRace(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, Polars polars, int timeScale, WindGenerator windGenerator) {
|
public MockRace(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, Polars polars, int timeScale, WindGenerator windGenerator) {
|
||||||
|
|
||||||
super(boatDataSource, raceDataSource, regattaDataSource);
|
this.setBoatDataSource(boatDataSource);
|
||||||
|
this.setRaceDataSource(raceDataSource);
|
||||||
|
this.setRegattaDataSource(regattaDataSource);
|
||||||
|
|
||||||
|
this.polars = polars;
|
||||||
this.scaleFactor = timeScale;
|
this.scaleFactor = timeScale;
|
||||||
|
|
||||||
this.boats = this.generateMockBoats(boatDataSource.getBoats(), raceDataSource.getParticipants(), polars);
|
this.boats = new ArrayList<>();
|
||||||
|
|
||||||
this.shrinkBoundary = GPSCoordinate.getShrinkBoundary(this.boundary);
|
this.shrinkBoundary = GPSCoordinate.getShrinkBoundary(this.getBoundary());
|
||||||
|
|
||||||
|
|
||||||
this.windGenerator = windGenerator;
|
this.windGenerator = windGenerator;
|
||||||
@@ -76,35 +94,53 @@ public class MockRace extends Race {
|
|||||||
//Wind.
|
//Wind.
|
||||||
this.setWind(windGenerator.generateBaselineWind());
|
this.setWind(windGenerator.generateBaselineWind());
|
||||||
|
|
||||||
|
|
||||||
|
// Set up colliders
|
||||||
|
this.colliderRegistry = new ColliderRegistry();
|
||||||
|
|
||||||
|
for(CompoundMark mark: this.getCompoundMarks()) {
|
||||||
|
colliderRegistry.addCollider(mark.getMark1());
|
||||||
|
if(mark.getMark2() != null) colliderRegistry.addCollider(mark.getMark2());
|
||||||
|
}
|
||||||
|
|
||||||
this.colliderRegistry.addAllColliders(boats);
|
this.colliderRegistry.addAllColliders(boats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a list of MockBoats given a list of Boats, and a list of participating boats.
|
* Generates a MockBoat from the BoatDataSource, given a source ID. Also adds it to the participant list.
|
||||||
* @param boats The map of Boats describing boats that are potentially in the race. Maps boat sourceID to boat.
|
* @param sourceID The source ID to assign the boat.
|
||||||
* @param sourceIDs The list of boat sourceIDs describing which specific boats are actually participating.
|
|
||||||
* @param polars The polars table to be used for boat simulation.
|
|
||||||
* @return A list of MockBoats that are participating in the race.
|
|
||||||
*/
|
*/
|
||||||
private List<MockBoat> generateMockBoats(Map<Integer, Boat> boats, List<Integer> sourceIDs, Polars polars) {
|
public void generateMockBoat(Integer sourceID) {
|
||||||
|
|
||||||
List<MockBoat> mockBoats = new ArrayList<>(sourceIDs.size());
|
|
||||||
|
|
||||||
//For each sourceID participating...
|
|
||||||
for (int sourceID : sourceIDs) {
|
|
||||||
|
|
||||||
//Get the boat associated with the sourceID.
|
//Get the boat associated with the sourceID.
|
||||||
Boat boat = boats.get(sourceID);
|
Boat boat = getBoatDataSource().getBoats().get(sourceID);
|
||||||
|
|
||||||
//Construct a MockBoat using the Boat and Polars.
|
//Construct a MockBoat using the Boat and Polars.
|
||||||
MockBoat mockBoat = new MockBoat(boat, polars);
|
MockBoat mockBoat = new MockBoat(boat, polars);
|
||||||
|
mockBoat.setCurrentLeg(this.getLegs().get(0));
|
||||||
|
|
||||||
mockBoats.add(mockBoat);
|
//Update participant list.
|
||||||
|
getRaceDataSource().getParticipants().add(sourceID);
|
||||||
|
|
||||||
|
this.boats.add(mockBoat);
|
||||||
|
|
||||||
|
getRaceDataSource().incrementSequenceNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
return mockBoats;
|
/**
|
||||||
|
* Removes a MockBoat from the race, by sourceID. Also removes it from the participant list.
|
||||||
|
* @param sourceID Source ID of boat to remove.
|
||||||
|
*/
|
||||||
|
public void removeMockBoat(Integer sourceID) {
|
||||||
|
this.boats.removeIf(mockBoat -> mockBoat.getSourceID() == sourceID);
|
||||||
|
getRaceDataSource().getParticipants().remove(sourceID);
|
||||||
|
getRaceDataSource().incrementSequenceNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ColliderRegistry getColliderRegistry() {
|
||||||
|
return colliderRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -113,7 +149,7 @@ public class MockRace extends Race {
|
|||||||
* @param currentTime Milliseconds since unix epoch.
|
* @param currentTime Milliseconds since unix epoch.
|
||||||
*/
|
*/
|
||||||
public void updateRaceTime(long currentTime) {
|
public void updateRaceTime(long currentTime) {
|
||||||
this.raceClock.setUTCTime(currentTime);
|
this.getRaceClock().setUTCTime(currentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -123,7 +159,7 @@ public class MockRace extends Race {
|
|||||||
public void updateRaceStatusEnum() {
|
public void updateRaceStatusEnum() {
|
||||||
|
|
||||||
//The millisecond duration of the race. Negative means it hasn't started, so we flip sign.
|
//The millisecond duration of the race. Negative means it hasn't started, so we flip sign.
|
||||||
long timeToStart = - this.raceClock.getDurationMilli();
|
long timeToStart = - this.getRaceClock().getDurationMilli();
|
||||||
|
|
||||||
|
|
||||||
if (timeToStart > Constants.RacePreStartTime) {
|
if (timeToStart > Constants.RacePreStartTime) {
|
||||||
@@ -194,7 +230,7 @@ public class MockRace extends Race {
|
|||||||
|
|
||||||
|
|
||||||
//The boat starts on the first leg of the race.
|
//The boat starts on the first leg of the race.
|
||||||
boat.setCurrentLeg(this.legs.get(0));
|
boat.setCurrentLeg(this.getLegs().get(0));
|
||||||
|
|
||||||
//Boats start with 0 knots speed.
|
//Boats start with 0 knots speed.
|
||||||
boat.setCurrentSpeed(0d);
|
boat.setCurrentSpeed(0d);
|
||||||
@@ -209,7 +245,9 @@ public class MockRace extends Race {
|
|||||||
boat.setStatus(BoatStatusEnum.PRESTART);
|
boat.setStatus(BoatStatusEnum.PRESTART);
|
||||||
|
|
||||||
//We set a large time since tack change so that it calculates a new VMG when the simulation starts.
|
//We set a large time since tack change so that it calculates a new VMG when the simulation starts.
|
||||||
boat.setTimeSinceTackChange(Long.MAX_VALUE);
|
//boat.setTimeSinceTackChange(Long.MAX_VALUE);
|
||||||
|
boat.setTimeSinceTackChange(0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -223,7 +261,7 @@ public class MockRace extends Race {
|
|||||||
private List<GPSCoordinate> getSpreadStartingPositions() {
|
private List<GPSCoordinate> getSpreadStartingPositions() {
|
||||||
|
|
||||||
//The first compound marker of the race - the starting gate.
|
//The first compound marker of the race - the starting gate.
|
||||||
CompoundMark compoundMark = this.legs.get(0).getStartCompoundMark();
|
CompoundMark compoundMark = this.getLegs().get(0).getStartCompoundMark();
|
||||||
|
|
||||||
//The position of the two markers from the compound marker.
|
//The position of the two markers from the compound marker.
|
||||||
GPSCoordinate mark1Position = compoundMark.getMark1Position();
|
GPSCoordinate mark1Position = compoundMark.getMark1Position();
|
||||||
@@ -341,6 +379,7 @@ public class MockRace extends Race {
|
|||||||
|
|
||||||
if (boat.getAutoVMG()) {
|
if (boat.getAutoVMG()) {
|
||||||
newOptimalVMG(boat);
|
newOptimalVMG(boat);
|
||||||
|
boat.setAutoVMG(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -353,31 +392,38 @@ public class MockRace extends Race {
|
|||||||
|
|
||||||
private void newOptimalVMG(MockBoat boat) {
|
private void newOptimalVMG(MockBoat boat) {
|
||||||
long tackPeriod = 1000;
|
long tackPeriod = 1000;
|
||||||
|
|
||||||
if (boat.getTimeSinceTackChange() > tackPeriod) {
|
if (boat.getTimeSinceTackChange() > tackPeriod) {
|
||||||
|
//System.out.println("optim called");
|
||||||
//Calculate the new VMG.
|
//Calculate the new VMG.
|
||||||
VMG newVMG = boat.getPolars().calculateVMG(
|
// VMG newVMG = boat.getPolars().calculateVMG(
|
||||||
this.getWindDirection(),
|
// this.getWindDirection(),
|
||||||
this.getWindSpeed(),
|
// this.getWindSpeed(),
|
||||||
boat.calculateBearingToNextMarker(),
|
// boat.calculateBearingToNextMarker(),
|
||||||
Bearing.fromDegrees(0d),
|
// Bearing.fromDegrees(0d),
|
||||||
Bearing.fromDegrees(359.99999d));
|
// Bearing.fromDegrees(359.99999d));
|
||||||
|
|
||||||
|
|
||||||
|
VMG newVMG = NewPolars.setBestVMG(this.getWindDirection(), this.getWindSpeed(), boat.getBearing());
|
||||||
|
//System.out.println(newVMG);
|
||||||
//If the new vmg improves velocity, use it.
|
//If the new vmg improves velocity, use it.
|
||||||
if (improvesVelocity(boat, newVMG)) {
|
/*if (improvesVelocity(boat, newVMG)) {
|
||||||
|
}*/
|
||||||
boat.setVMG(newVMG);
|
boat.setVMG(newVMG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void setBoatSpeed(MockBoat boat) {
|
private void setBoatSpeed(MockBoat boat) {
|
||||||
VMG vmg = boat.getPolars().calculateVMG(
|
// VMG vmg = boat.getPolars().calculateVMG(
|
||||||
|
// this.getWindDirection(),
|
||||||
|
// this.getWindSpeed(),
|
||||||
|
// boat.getBearing(),
|
||||||
|
// Bearing.fromDegrees(boat.getBearing().degrees() - 1),
|
||||||
|
// Bearing.fromDegrees(boat.getBearing().degrees() + 1));
|
||||||
|
//VMG vmg = boat.getPolars().setBestVMG(this.getWindDirection(), this.getWindSpeed(), boat.getBearing());
|
||||||
|
VMG vmg = new VMG(NewPolars.calculateSpeed(
|
||||||
this.getWindDirection(),
|
this.getWindDirection(),
|
||||||
this.getWindSpeed(),
|
this.getWindSpeed(),
|
||||||
boat.getBearing(),
|
boat.getBearing()
|
||||||
Bearing.fromDegrees(boat.getBearing().degrees() - 1),
|
), boat.getBearing()) ;
|
||||||
Bearing.fromDegrees(boat.getBearing().degrees() + 1));
|
|
||||||
if (vmg.getSpeed() > 0) {
|
if (vmg.getSpeed() > 0) {
|
||||||
boat.setCurrentSpeed(vmg.getSpeed());
|
boat.setCurrentSpeed(vmg.getSpeed());
|
||||||
}
|
}
|
||||||
@@ -485,7 +531,7 @@ public class MockRace extends Race {
|
|||||||
roundingChecks.get(0), boat.getPosition(), legBearing) &&
|
roundingChecks.get(0), boat.getPosition(), legBearing) &&
|
||||||
gateCheck && boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) {
|
gateCheck && boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) {
|
||||||
boat.increaseRoundingStatus();
|
boat.increaseRoundingStatus();
|
||||||
if (boat.getCurrentLeg().getLegNumber() + 2 >= legs.size()){
|
if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){
|
||||||
//boat has finished race
|
//boat has finished race
|
||||||
boat.increaseRoundingStatus();
|
boat.increaseRoundingStatus();
|
||||||
}
|
}
|
||||||
@@ -503,7 +549,7 @@ public class MockRace extends Race {
|
|||||||
case 2://has traveled 180 degrees around the mark
|
case 2://has traveled 180 degrees around the mark
|
||||||
//Move boat on to next leg.
|
//Move boat on to next leg.
|
||||||
boat.resetRoundingStatus();
|
boat.resetRoundingStatus();
|
||||||
Leg nextLeg = this.legs.get(boat.getCurrentLeg().getLegNumber() + 1);
|
Leg nextLeg = this.getLegs().get(boat.getCurrentLeg().getLegNumber() + 1);
|
||||||
boat.setCurrentLeg(nextLeg);
|
boat.setCurrentLeg(nextLeg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -530,7 +576,7 @@ public class MockRace extends Race {
|
|||||||
gateCheck &&
|
gateCheck &&
|
||||||
boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) {
|
boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) {
|
||||||
boat.increaseRoundingStatus();
|
boat.increaseRoundingStatus();
|
||||||
if (boat.getCurrentLeg().getLegNumber() + 2 >= legs.size()){
|
if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){
|
||||||
//boat has finished race
|
//boat has finished race
|
||||||
boat.increaseRoundingStatus();
|
boat.increaseRoundingStatus();
|
||||||
}
|
}
|
||||||
@@ -547,7 +593,7 @@ public class MockRace extends Race {
|
|||||||
case 2://has traveled 180 degrees around the mark
|
case 2://has traveled 180 degrees around the mark
|
||||||
//Move boat on to next leg.
|
//Move boat on to next leg.
|
||||||
boat.resetRoundingStatus();
|
boat.resetRoundingStatus();
|
||||||
Leg nextLeg = this.legs.get(boat.getCurrentLeg().getLegNumber() + 1);
|
Leg nextLeg = this.getLegs().get(boat.getCurrentLeg().getLegNumber() + 1);
|
||||||
boat.setCurrentLeg(nextLeg);
|
boat.setCurrentLeg(nextLeg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -585,7 +631,7 @@ public class MockRace extends Race {
|
|||||||
|
|
||||||
GPSCoordinate roundCheck2;
|
GPSCoordinate roundCheck2;
|
||||||
try{
|
try{
|
||||||
Leg nextLeg = legs.get(legs.indexOf(boat.getCurrentLeg()) + 1);
|
Leg nextLeg = getLegs().get(getLegs().indexOf(boat.getCurrentLeg()) + 1);
|
||||||
|
|
||||||
GPSCoordinate startNextDirectionLinePoint = nextLeg.getStartCompoundMark().getMark1Position();
|
GPSCoordinate startNextDirectionLinePoint = nextLeg.getStartCompoundMark().getMark1Position();
|
||||||
GPSCoordinate endNextDirectionLinePoint = nextLeg.getEndCompoundMark().getMark1Position();
|
GPSCoordinate endNextDirectionLinePoint = nextLeg.getEndCompoundMark().getMark1Position();
|
||||||
@@ -681,7 +727,7 @@ public class MockRace extends Race {
|
|||||||
*/
|
*/
|
||||||
public void changeWindDirection() {
|
public void changeWindDirection() {
|
||||||
|
|
||||||
Wind nextWind = windGenerator.generateNextWind(raceWind.getValue());
|
Wind nextWind = windGenerator.generateNextWind(windProperty().getValue());
|
||||||
|
|
||||||
setWind(nextWind);
|
setWind(nextWind);
|
||||||
}
|
}
|
||||||
@@ -702,13 +748,10 @@ public class MockRace extends Race {
|
|||||||
long timeFromNow = (long) (1000 * boat.calculateDistanceToNextMarker() / velocityToMark);
|
long timeFromNow = (long) (1000 * boat.calculateDistanceToNextMarker() / velocityToMark);
|
||||||
|
|
||||||
//Calculate time at which it will reach mark.
|
//Calculate time at which it will reach mark.
|
||||||
ZonedDateTime timeAtMark = this.raceClock.getCurrentTime().plus(timeFromNow, ChronoUnit.MILLIS);
|
ZonedDateTime timeAtMark = this.getRaceClock().getCurrentTime().plus(timeFromNow, ChronoUnit.MILLIS);
|
||||||
boat.setEstimatedTimeAtNextMark(timeAtMark);
|
boat.setEstimatedTimeAtNextMark(timeAtMark);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CompoundMark> getCompoundMarks() {
|
|
||||||
return compoundMarks;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
package mock.model;
|
||||||
|
|
||||||
|
import shared.model.Bearing;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New Polars are the revampe of the old Polars class which interpolates the data after being parsed from the Polar Parser
|
||||||
|
* There can only be one NewPolars instance stored statically however if a boat does happen to have a special case it can be assigned.
|
||||||
|
*/
|
||||||
|
public class NewPolars {
|
||||||
|
|
||||||
|
|
||||||
|
//true wind speed, <true wind angle, best boat angle>
|
||||||
|
private static Map<Double, TreeMap<Double, Double>> polars = new TreeMap<>();
|
||||||
|
|
||||||
|
public static NewPolars newPolars = null;
|
||||||
|
|
||||||
|
public NewPolars(){
|
||||||
|
newPolars = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add polars from the polar table
|
||||||
|
* @param trueWindSpeed True Wind Speed that the true wind angle and speed corresponds to
|
||||||
|
* @param trueWindAngle True Wind Angle of the race
|
||||||
|
* @param boatSpeed The speed the boat should be going at given the true wind angle
|
||||||
|
*/
|
||||||
|
public static void addPolars(double trueWindSpeed, Bearing trueWindAngle, double boatSpeed){
|
||||||
|
double tws = trueWindSpeed;
|
||||||
|
double bs = boatSpeed;
|
||||||
|
double twa = trueWindAngle.degrees();
|
||||||
|
if (!polars.containsKey(tws)){
|
||||||
|
polars.put(tws, new TreeMap<>());
|
||||||
|
}
|
||||||
|
polars.get(tws).putIfAbsent(twa, bs);
|
||||||
|
polars.get(tws).putIfAbsent(360d - twa, bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linearly Interpolates this should only be called once per parsing of a polar table
|
||||||
|
*/
|
||||||
|
public static void linearInterpolatePolars(){
|
||||||
|
TreeMap<Double, Double> prevTWS = null;
|
||||||
|
TreeMap<Double, TreeMap<Double, Double>> iterablePolars = new TreeMap<>(polars);
|
||||||
|
//this loop averages out the speed between tow angles
|
||||||
|
//Example: Pair one: 0 degrees, 0 knots
|
||||||
|
// Pair two: 3 degrees, 6 knots
|
||||||
|
//This loop will add
|
||||||
|
//Pair one: 0 degrees, 0 knots
|
||||||
|
//Pair two: 1 degrees, 2 knots
|
||||||
|
//Pair three: 2 degrees, 4 knots
|
||||||
|
//Pair four: 3 degrees, 6 knots
|
||||||
|
for (double windSpeed: iterablePolars.keySet()){
|
||||||
|
TreeMap<Double, Double> tws = iterablePolars.get(windSpeed);
|
||||||
|
|
||||||
|
if (prevTWS == null){
|
||||||
|
prevTWS = tws;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double previousTWA = -1;
|
||||||
|
TreeMap<Double, Double> iterableTWS = new TreeMap<>(tws);
|
||||||
|
|
||||||
|
for (double twa: iterableTWS.keySet()){
|
||||||
|
if (previousTWA == -1){
|
||||||
|
previousTWA = twa;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double twaDiff = twa - previousTWA;
|
||||||
|
double speedDiff = iterableTWS.get(twa) - iterableTWS.get(previousTWA);
|
||||||
|
double prevSpeed = iterableTWS.get(previousTWA);
|
||||||
|
double diff = speedDiff/twaDiff;
|
||||||
|
|
||||||
|
for (double i = previousTWA; i < twa; i ++){
|
||||||
|
double mult = i - previousTWA;
|
||||||
|
double newSpeed = diff * mult + prevSpeed;
|
||||||
|
tws.put(i, newSpeed);
|
||||||
|
}
|
||||||
|
previousTWA = twa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double getClosest(double value, Set<Double> set){
|
||||||
|
double closestVal = 0;
|
||||||
|
double smallestDiff = Double.MAX_VALUE;
|
||||||
|
for (double d: set){
|
||||||
|
double diff = Math.abs(value - d);
|
||||||
|
if (diff < smallestDiff){
|
||||||
|
closestVal = d;
|
||||||
|
smallestDiff = diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines which quadrant degrees are in
|
||||||
|
* 0/360 Degrees
|
||||||
|
* Quadrant 4 | Quadrant 1
|
||||||
|
* -----------------------
|
||||||
|
* Quadrant 3 | Quadrant 2
|
||||||
|
* @param degrees
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static int getQuadrant(double degrees){
|
||||||
|
return (int) modulateAngle(degrees) / 90 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double getBestSpeedInQuadrant(int quad, Map<Double, Double> set){
|
||||||
|
double min = (quad - 1)* 90;
|
||||||
|
double max = quad * 90;
|
||||||
|
double maxAngle = 0;
|
||||||
|
double maxSpeed = 0;
|
||||||
|
double dupAngle = 0;
|
||||||
|
//DupAngle will average the angle between maxAngles that have the same speed
|
||||||
|
//Example: if 150 degrees, 180 degrees, and 210 degrees all go at 10 knots
|
||||||
|
//then the average will be taken as (150 + 210) / 2 and the angle will be returned on that.
|
||||||
|
for (Double s: set.keySet()){
|
||||||
|
if (s >= min && s < max){
|
||||||
|
if (set.get(s) > maxSpeed){
|
||||||
|
dupAngle = 0;
|
||||||
|
maxAngle = s;
|
||||||
|
maxSpeed = set.get(s);
|
||||||
|
} else if (set.get(s) == maxSpeed){
|
||||||
|
dupAngle = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dupAngle != 0 ){
|
||||||
|
return getClosest((dupAngle + maxAngle) / 2, set.keySet());
|
||||||
|
}
|
||||||
|
return maxAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the best VMG that the boat can change to given it's current diagonal heading direction.
|
||||||
|
* @param trueWindAngle True wind angle of the race
|
||||||
|
* @param trueWindSpeed True wind speed of the race
|
||||||
|
* @param boatAngle Angle that the boat is currently at
|
||||||
|
* @return the best vmg that the boat can change to
|
||||||
|
*/
|
||||||
|
public static VMG setBestVMG(Bearing trueWindAngle, double trueWindSpeed, Bearing boatAngle){
|
||||||
|
//System.out.println("VMG AUTO CALLED");
|
||||||
|
//speed
|
||||||
|
double closestSpeed = getClosest(trueWindSpeed, polars.keySet());
|
||||||
|
|
||||||
|
double angle = modulateAngle(boatAngle.degrees() - trueWindAngle.degrees());
|
||||||
|
int quad = getQuadrant(angle);
|
||||||
|
double bestAngle = getBestSpeedInQuadrant(quad, polars.get(closestSpeed));
|
||||||
|
|
||||||
|
double boatSpeed = polars.get(closestSpeed).get(bestAngle);
|
||||||
|
|
||||||
|
double newAngle = modulateAngle(bestAngle + trueWindAngle.degrees());
|
||||||
|
|
||||||
|
return new VMG(boatSpeed, Bearing.fromDegrees(newAngle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the speed that a certain angle should be doing
|
||||||
|
* @param trueWindAngle TrueWind Angle of the race
|
||||||
|
* @param trueWindSpeed True Wind Speed of the race
|
||||||
|
* @param boatAngle Angle that the boat is current at
|
||||||
|
* @return the speed that the boat should be traveling at.
|
||||||
|
*/
|
||||||
|
public static double calculateSpeed(Bearing trueWindAngle, double trueWindSpeed, Bearing boatAngle){
|
||||||
|
//speed
|
||||||
|
double closestSpeed = getClosest(trueWindSpeed, polars.keySet());
|
||||||
|
|
||||||
|
double angleDiff = modulateAngle(boatAngle.degrees() - trueWindAngle.degrees());
|
||||||
|
double closestAngle = getClosest(angleDiff, polars.get(closestSpeed).keySet());
|
||||||
|
|
||||||
|
double boatSpeed = polars.get(closestSpeed).get(closestAngle);
|
||||||
|
|
||||||
|
return boatSpeed;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double modulateAngle(double angle){
|
||||||
|
return (angle % 360 + 360) % 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Double, TreeMap<Double, Double>> getPolars(){
|
||||||
|
//this function is just for testing so therefore it is private
|
||||||
|
return polars;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printOutLinearInterpolated(){
|
||||||
|
for (double tws: polars.keySet()){
|
||||||
|
System.out.println("==================================================");
|
||||||
|
System.out.println("Speed: " + tws);
|
||||||
|
System.out.println("==================================================");
|
||||||
|
for (double twa: polars.get(tws).keySet()){
|
||||||
|
System.out.println("TWA: " + twa + ", Boat Speed: " + polars.get(tws).get(twa));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,10 +3,7 @@ package mock.model;
|
|||||||
import javafx.util.Pair;
|
import javafx.util.Pair;
|
||||||
import shared.model.Bearing;
|
import shared.model.Bearing;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates an entire polar table. Has a function to calculate VMG.
|
* Encapsulates an entire polar table. Has a function to calculate VMG.
|
||||||
@@ -78,8 +75,6 @@ public class Polars {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the VMG for a given wind angle, wind speed, and angle to destination. Will only return VMGs that have a true bearing (angle) within a given bound - this is to ensure that you can calculate VMGs without going out of bounds.
|
* Calculates the VMG for a given wind angle, wind speed, and angle to destination. Will only return VMGs that have a true bearing (angle) within a given bound - this is to ensure that you can calculate VMGs without going out of bounds.
|
||||||
* <br>
|
* <br>
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
|
prestartCountdown();
|
||||||
|
|
||||||
race.initialiseBoats();
|
race.initialiseBoats();
|
||||||
|
|
||||||
countdown();
|
countdown();
|
||||||
@@ -61,6 +64,37 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The countdown timer until the prestart period is finished. This timer will stop 3 minutes before the race starts, and players can no longer start participating.
|
||||||
|
*/
|
||||||
|
private void prestartCountdown() {
|
||||||
|
|
||||||
|
long previousFrameTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
while (((race.getRaceStatusEnum() == RaceStatusEnum.PRESTART)
|
||||||
|
|| (race.getRaceStatusEnum() == RaceStatusEnum.NOT_ACTIVE)
|
||||||
|
|| (race.getRaceStatusEnum() == RaceStatusEnum.WARNING)) && loopBool) {
|
||||||
|
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
//Update race time.
|
||||||
|
race.updateRaceTime(currentTime);
|
||||||
|
|
||||||
|
//Update the race status based on the current time.
|
||||||
|
race.updateRaceStatusEnum();
|
||||||
|
|
||||||
|
//Provide boat's with an estimated time at next mark until the race starts.
|
||||||
|
race.setBoatsTimeNextMark(race.getRaceClock().getCurrentTime());
|
||||||
|
|
||||||
|
//Parse the race snapshot.
|
||||||
|
server.parseSnapshot();
|
||||||
|
|
||||||
|
|
||||||
|
waitForFramePeriod(previousFrameTime, currentTime, 50);
|
||||||
|
previousFrameTime = currentTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Countdown timer until race starts.
|
* Countdown timer until race starts.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
package mock.model;
|
package mock.model;
|
||||||
|
|
||||||
|
import network.AckSequencer;
|
||||||
import network.Messages.*;
|
import network.Messages.*;
|
||||||
import network.Messages.Enums.BoatLocationDeviceEnum;
|
import network.Messages.Enums.BoatLocationDeviceEnum;
|
||||||
import network.Messages.Enums.YachtEventEnum;
|
import network.Messages.Enums.YachtEventEnum;
|
||||||
|
import network.Messages.Enums.XMLMessageType;
|
||||||
import shared.model.Bearing;
|
import shared.model.Bearing;
|
||||||
import shared.model.CompoundMark;
|
import shared.model.CompoundMark;
|
||||||
import shared.model.Mark;
|
import shared.model.Mark;
|
||||||
|
import shared.xml.Race.RaceDataSourceToXML;
|
||||||
|
import shared.xml.boats.BoatDataSourceToXML;
|
||||||
|
import shared.xml.regatta.RegattaDataSourceToXML;
|
||||||
|
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by connortaylorbrown on 2/08/17.
|
* Created by connortaylorbrown on 2/08/17.
|
||||||
@@ -24,6 +32,21 @@ public class RaceServer {
|
|||||||
*/
|
*/
|
||||||
private int boatLocationSequenceNumber = 1;
|
private int boatLocationSequenceNumber = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The sequence number of race XML messages.
|
||||||
|
*/
|
||||||
|
private int raceXMLSeqNumber = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The sequence number of boat XML messages.
|
||||||
|
*/
|
||||||
|
private int boatXMLSeqNumber = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The sequence number of regatta XML messages.
|
||||||
|
*/
|
||||||
|
private int regattaXMLSeqNumber = -1;
|
||||||
|
|
||||||
|
|
||||||
public RaceServer(MockRace race, LatestMessages latestMessages) {
|
public RaceServer(MockRace race, LatestMessages latestMessages) {
|
||||||
this.race = race;
|
this.race = race;
|
||||||
@@ -53,6 +76,8 @@ public class RaceServer {
|
|||||||
|
|
||||||
latestMessages.setSnapshot(snapshotMessages);
|
latestMessages.setSnapshot(snapshotMessages);
|
||||||
|
|
||||||
|
updateXMLFiles();
|
||||||
|
|
||||||
//Reset collision list
|
//Reset collision list
|
||||||
collisionEvents.clear();
|
collisionEvents.clear();
|
||||||
//System.out.println(collisionEvents.size());
|
//System.out.println(collisionEvents.size());
|
||||||
@@ -60,6 +85,73 @@ public class RaceServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the race/boat/regatta data sources have changed, and if they have, update their xml representations.
|
||||||
|
*/
|
||||||
|
private void updateXMLFiles() {
|
||||||
|
updateRaceXMLFile();
|
||||||
|
updateBoatXMLFile();
|
||||||
|
updateRegattaXMLFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the race data source has changed, and if it has, updates LatestMessages' race xml message.
|
||||||
|
*/
|
||||||
|
private void updateRaceXMLFile() {
|
||||||
|
if (raceXMLSeqNumber != race.getRaceDataSource().getSequenceNumber()) {
|
||||||
|
|
||||||
|
raceXMLSeqNumber = race.getRaceDataSource().getSequenceNumber();
|
||||||
|
try {
|
||||||
|
String raceXMLString = RaceDataSourceToXML.toString(race.getRaceDataSource());
|
||||||
|
XMLMessage message = createXMLMessage(raceXMLString, XMLMessageType.RACE);
|
||||||
|
latestMessages.setXMLMessage(message);
|
||||||
|
|
||||||
|
} catch (JAXBException e) {
|
||||||
|
Logger.getGlobal().log(Level.WARNING, "Could not serialise: " + race.getRaceDataSource(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the boat data source has changed, and if it has, updates LatestMessages' boat xml message.
|
||||||
|
*/
|
||||||
|
private void updateBoatXMLFile() {
|
||||||
|
if (boatXMLSeqNumber != race.getBoatDataSource().getSequenceNumber()) {
|
||||||
|
|
||||||
|
boatXMLSeqNumber = race.getBoatDataSource().getSequenceNumber();
|
||||||
|
try {
|
||||||
|
String boatXMLString = BoatDataSourceToXML.toString(race.getBoatDataSource());
|
||||||
|
XMLMessage message = createXMLMessage(boatXMLString, XMLMessageType.BOAT);
|
||||||
|
latestMessages.setXMLMessage(message);
|
||||||
|
|
||||||
|
} catch (JAXBException e) {
|
||||||
|
Logger.getGlobal().log(Level.WARNING, "Could not serialise: " + race.getBoatDataSource(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the regatta data source has changed, and if it has, updates LatestMessages' regatta xml message.
|
||||||
|
*/
|
||||||
|
private void updateRegattaXMLFile() {
|
||||||
|
if (regattaXMLSeqNumber != race.getRegattaDataSource().getSequenceNumber()) {
|
||||||
|
|
||||||
|
regattaXMLSeqNumber = race.getRegattaDataSource().getSequenceNumber();
|
||||||
|
|
||||||
|
try {
|
||||||
|
String regattaXMLString = RegattaDataSourceToXML.toString(race.getRegattaDataSource());
|
||||||
|
XMLMessage message = createXMLMessage(regattaXMLString, XMLMessageType.REGATTA);
|
||||||
|
latestMessages.setXMLMessage(message);
|
||||||
|
|
||||||
|
} catch (JAXBException e) {
|
||||||
|
Logger.getGlobal().log(Level.WARNING, "Could not serialise: " + race.getRegattaDataSource(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses an individual marker boat, and returns it.
|
* Parses an individual marker boat, and returns it.
|
||||||
* @param mark The marker boat to parse.
|
* @param mark The marker boat to parse.
|
||||||
@@ -191,6 +283,43 @@ public class RaceServer {
|
|||||||
return raceStatus;
|
return raceStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an XMLMessage of a specified subtype using the xml contents string.
|
||||||
|
* @param xmlString The contents of the xml file.
|
||||||
|
* @param messageType The subtype of xml message (race, regatta, boat).
|
||||||
|
* @return The created XMLMessage object.
|
||||||
|
*/
|
||||||
|
private XMLMessage createXMLMessage(String xmlString, XMLMessageType messageType) {
|
||||||
|
|
||||||
|
//Get the correct sequence number to use.
|
||||||
|
int sequenceNumber = 0;
|
||||||
|
if (messageType == XMLMessageType.RACE) {
|
||||||
|
sequenceNumber = this.raceXMLSeqNumber;
|
||||||
|
|
||||||
|
} else if (messageType == XMLMessageType.BOAT) {
|
||||||
|
sequenceNumber = this.boatXMLSeqNumber;
|
||||||
|
|
||||||
|
} else if (messageType == XMLMessageType.REGATTA) {
|
||||||
|
sequenceNumber = this.regattaXMLSeqNumber;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create the message.
|
||||||
|
XMLMessage message = new XMLMessage(
|
||||||
|
XMLMessage.currentVersionNumber,
|
||||||
|
AckSequencer.getNextAckNum(),
|
||||||
|
System.currentTimeMillis(),
|
||||||
|
messageType,
|
||||||
|
sequenceNumber,
|
||||||
|
xmlString);
|
||||||
|
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the yacht event and return it
|
* Parse the yacht event and return it
|
||||||
* @param boat yacht with event
|
* @param boat yacht with event
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package mock.model;
|
|||||||
|
|
||||||
|
|
||||||
import mock.exceptions.SourceIDAllocationException;
|
import mock.exceptions.SourceIDAllocationException;
|
||||||
|
import network.Messages.Enums.RaceStatusEnum;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -13,24 +14,18 @@ public class SourceIdAllocator {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This list contains all unallocated source IDs.
|
* The race we are allocating for.
|
||||||
*/
|
*/
|
||||||
List<Integer> unallocatedIDs = new ArrayList<>();
|
private MockRace mockRace;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This list contains all allocated source IDs.
|
* Creates a SourceIdAllocator for a given race.
|
||||||
|
* @param mockRace Race to allocate source IDs for.
|
||||||
*/
|
*/
|
||||||
List<Integer> allocatedIDs = new ArrayList<>();
|
public SourceIdAllocator(MockRace mockRace) {
|
||||||
|
this.mockRace = mockRace;
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a source ID allocator, using the given list of unallocated source IDs.
|
|
||||||
* @param unallocatedIDs List of unallocated source IDs.
|
|
||||||
*/
|
|
||||||
public SourceIdAllocator(List<Integer> unallocatedIDs) {
|
|
||||||
//We need to copy the list.
|
|
||||||
this.unallocatedIDs.addAll(unallocatedIDs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -41,11 +36,25 @@ public class SourceIdAllocator {
|
|||||||
*/
|
*/
|
||||||
public synchronized int allocateSourceID() throws SourceIDAllocationException {
|
public synchronized int allocateSourceID() throws SourceIDAllocationException {
|
||||||
|
|
||||||
|
if (!((mockRace.getRaceStatusEnum() == RaceStatusEnum.PRESTART)
|
||||||
|
|| (mockRace.getRaceStatusEnum() == RaceStatusEnum.WARNING))) {
|
||||||
|
throw new SourceIDAllocationException("Could not allocate a source ID. Can only allocate during pre-start period. It is currently: " + mockRace.getRaceStatusEnum());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> allocatedIDs = mockRace.getRaceDataSource().getParticipants();
|
||||||
|
|
||||||
|
List<Integer> allIDs = new ArrayList<>(mockRace.getBoatDataSource().getBoats().keySet());
|
||||||
|
|
||||||
|
//Get list of unallocated ids.
|
||||||
|
List<Integer> unallocatedIDs = new ArrayList<>(allIDs);
|
||||||
|
unallocatedIDs.removeAll(allocatedIDs);
|
||||||
|
|
||||||
|
|
||||||
if (!unallocatedIDs.isEmpty()) {
|
if (!unallocatedIDs.isEmpty()) {
|
||||||
|
|
||||||
int sourceID = unallocatedIDs.remove(0);
|
int sourceID = unallocatedIDs.remove(0);
|
||||||
|
|
||||||
allocatedIDs.add(sourceID);
|
mockRace.generateMockBoat(sourceID);
|
||||||
|
|
||||||
return sourceID;
|
return sourceID;
|
||||||
|
|
||||||
@@ -61,10 +70,6 @@ public class SourceIdAllocator {
|
|||||||
* @param sourceID Source ID to return.
|
* @param sourceID Source ID to return.
|
||||||
*/
|
*/
|
||||||
public void returnSourceID(Integer sourceID) {
|
public void returnSourceID(Integer sourceID) {
|
||||||
|
mockRace.removeMockBoat(sourceID);
|
||||||
//We remove an Integer, not an int, so that we remove by value not by index.
|
|
||||||
allocatedIDs.remove(sourceID);
|
|
||||||
|
|
||||||
unallocatedIDs.add(sourceID);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,4 +45,8 @@ public class VMG {
|
|||||||
return bearing;
|
return bearing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString(){
|
||||||
|
return String.format("VMG Object: Speed %f, Bearing %f.", speed, bearing.degrees());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package mock.model;
|
package mock.model.wind;
|
||||||
|
|
||||||
|
|
||||||
import shared.model.Bearing;
|
import shared.model.Bearing;
|
||||||
import shared.model.Wind;
|
import shared.model.Wind;
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class generates Wind objects for use in a MockRace.
|
* This class generates Wind objects for use in a MockRace.
|
||||||
* Initialised with a baseline wind speed and direction, and keeps it constant.
|
* Initialised with a baseline wind speed and direction, and keeps it constant.
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package mock.model;
|
package mock.model.wind;
|
||||||
|
|
||||||
|
|
||||||
import shared.model.Bearing;
|
import shared.model.Bearing;
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package mock.model.wind;
|
||||||
|
|
||||||
|
import shared.model.Bearing;
|
||||||
|
import shared.model.Wind;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class ShiftingWindGenerator implements WindGenerator {
|
||||||
|
private Bearing baselineBearing;
|
||||||
|
private double baseLineSpeed;
|
||||||
|
private double windSpeedVariance = 5;
|
||||||
|
private double bearingVariance = 5; // In degrees
|
||||||
|
private double oscillationVariance = 0.25;
|
||||||
|
private double oscillationPeriod = 1e3 * 60 * 1; // In milliseconds
|
||||||
|
private double shiftTime = 1e3 * 60;
|
||||||
|
private double shiftedSoFar = 0;
|
||||||
|
|
||||||
|
private double timeOfLastOscillationReset = 0;
|
||||||
|
private double timeOfLastChange = 0;
|
||||||
|
private double timeOfLastShift = 0; // Back / veer
|
||||||
|
|
||||||
|
private boolean anticlockwise = false;
|
||||||
|
private boolean shiftAnticlockwise = false;//true for Back, false for veer
|
||||||
|
private boolean shiftThisRace = Math.random() > 0.5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param baselineBearing baseline bearing for wind
|
||||||
|
* @param baseLineSpeed base line speed for wind
|
||||||
|
*/
|
||||||
|
public ShiftingWindGenerator(Bearing baselineBearing, double baseLineSpeed) {
|
||||||
|
this.baselineBearing = baselineBearing;
|
||||||
|
this.baseLineSpeed = baseLineSpeed;
|
||||||
|
initialiseOscillationDirection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Wind generateBaselineWind() {
|
||||||
|
return new Wind(baselineBearing, baseLineSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Wind generateNextWind(Wind currentWind) {
|
||||||
|
return changeWind(currentWind);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param wind the wind to change
|
||||||
|
* @return the changed wind
|
||||||
|
*/
|
||||||
|
private Wind changeWind(Wind wind) {
|
||||||
|
Wind newWind = new Wind(wind.getWindDirection(), wind.getWindSpeed());
|
||||||
|
oscillateWind(newWind);
|
||||||
|
if (shiftThisRace){shiftWind(newWind);}
|
||||||
|
changeWindSpeed(newWind);
|
||||||
|
timeOfLastChange = System.currentTimeMillis();
|
||||||
|
return newWind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* moves the wind 5 degrees up and down
|
||||||
|
* @param wind the wind to oscillate
|
||||||
|
*/
|
||||||
|
private void oscillateWind(Wind wind) {
|
||||||
|
double timeSinceLastOscillationReset = System.currentTimeMillis() - timeOfLastOscillationReset;
|
||||||
|
double timeSinceLastChange = System.currentTimeMillis() - timeOfLastChange;
|
||||||
|
double newBearing = wind.getWindDirection().degrees();
|
||||||
|
double degreeChange = timeSinceLastChange * 2 * bearingVariance / oscillationPeriod;
|
||||||
|
degreeChange = (1 - oscillationVariance) * degreeChange + (2 * oscillationVariance) * degreeChange * Math.random();
|
||||||
|
|
||||||
|
if (timeSinceLastOscillationReset >= oscillationPeriod) {
|
||||||
|
timeOfLastOscillationReset = System.currentTimeMillis();
|
||||||
|
anticlockwise = !anticlockwise;
|
||||||
|
}
|
||||||
|
if (anticlockwise) {
|
||||||
|
newBearing -= degreeChange;
|
||||||
|
if (newBearing < baselineBearing.degrees() - bearingVariance) {
|
||||||
|
anticlockwise = !anticlockwise;
|
||||||
|
timeOfLastOscillationReset = System.currentTimeMillis();
|
||||||
|
} else {
|
||||||
|
wind.setWindDirection(Bearing.fromDegrees(newBearing % 360));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newBearing += degreeChange;
|
||||||
|
if (newBearing > baselineBearing.degrees() + bearingVariance) {
|
||||||
|
anticlockwise = !anticlockwise;
|
||||||
|
timeOfLastOscillationReset = System.currentTimeMillis();
|
||||||
|
} else {
|
||||||
|
wind.setWindDirection(Bearing.fromDegrees(newBearing % 360));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slowly shifts the wind up to 180 degrees from where it started
|
||||||
|
* @param wind the wind to change
|
||||||
|
*/
|
||||||
|
private void shiftWind(Wind wind) {
|
||||||
|
double timeSinceLastShift = System.currentTimeMillis() - timeOfLastShift;
|
||||||
|
double newBearing = wind.getWindDirection().degrees();
|
||||||
|
double degreeChange = 7;
|
||||||
|
|
||||||
|
if (timeSinceLastShift >= shiftTime){
|
||||||
|
shiftedSoFar += degreeChange;
|
||||||
|
if (shiftedSoFar >= 180){
|
||||||
|
shiftAnticlockwise = Math.random() > 0.5;
|
||||||
|
shiftedSoFar = 0;
|
||||||
|
// System.out.println("Swapping");
|
||||||
|
}
|
||||||
|
|
||||||
|
timeOfLastShift = System.currentTimeMillis();
|
||||||
|
if (shiftAnticlockwise){
|
||||||
|
newBearing -= degreeChange;
|
||||||
|
wind.setWindDirection(Bearing.fromDegrees(newBearing % 360));
|
||||||
|
} else {
|
||||||
|
newBearing += degreeChange;
|
||||||
|
wind.setWindDirection(Bearing.fromDegrees(newBearing % 360));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the wind speed
|
||||||
|
* @param wind the wind to change
|
||||||
|
*/
|
||||||
|
private void changeWindSpeed(Wind wind) {
|
||||||
|
double offsetAngle = (wind.getWindDirection().radians() - baselineBearing.radians());
|
||||||
|
double offset = Math.sin(offsetAngle) * windSpeedVariance;
|
||||||
|
double newWindSpeed = baseLineSpeed + offset;
|
||||||
|
wind.setWindSpeed(newWindSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* starts the wind oscillation direction
|
||||||
|
*/
|
||||||
|
private void initialiseOscillationDirection() {
|
||||||
|
anticlockwise = new Random().nextBoolean();
|
||||||
|
timeOfLastOscillationReset = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBearingVariance(double maxBearingVariance) {
|
||||||
|
this.bearingVariance = maxBearingVariance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWindSpeedVariance(double windSpeedVariance) {
|
||||||
|
this.windSpeedVariance = windSpeedVariance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOscillationPeriod(double oscillationPeriod) {
|
||||||
|
this.oscillationPeriod = oscillationPeriod;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package mock.model;
|
package mock.model.wind;
|
||||||
|
|
||||||
import shared.model.Wind;
|
import shared.model.Wind;
|
||||||
|
|
||||||
@@ -74,14 +74,19 @@ public class RaceXMLCreator {
|
|||||||
* @throws SAXException error in schema file
|
* @throws SAXException error in schema file
|
||||||
* @throws ParserConfigurationException error in parsing the 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);
|
RaceXMLReader reader = new RaceXMLReader(s, XMLFileType.ResourcePath);
|
||||||
|
|
||||||
XMLRace race = (XMLRace) XMLUtilities.xmlToClass(RaceXMLCreator.class.getClassLoader().getResourceAsStream(s),
|
XMLRace race = XMLUtilities.xmlToClass(
|
||||||
|
RaceXMLCreator.class.getClassLoader().getResourceAsStream(s),
|
||||||
RaceXMLCreator.class.getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd"),
|
RaceXMLCreator.class.getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd"),
|
||||||
XMLRace.class);
|
XMLRace.class);
|
||||||
|
|
||||||
|
if(tutorial){
|
||||||
|
setRaceXMLAtCurrentTimeToNow(race, 1000l, 5000l);
|
||||||
|
} else {
|
||||||
setRaceXMLAtCurrentTimeToNow(race);
|
setRaceXMLAtCurrentTimeToNow(race);
|
||||||
|
}
|
||||||
|
|
||||||
double raceOriginalBearing = getLineAngle(getLeewardGate(reader).getMark1Position(), getWindwardGate(reader).getMark1Position());
|
double raceOriginalBearing = getLineAngle(getLeewardGate(reader).getMark1Position(), getWindwardGate(reader).getMark1Position());
|
||||||
|
|
||||||
@@ -89,16 +94,10 @@ public class RaceXMLCreator {
|
|||||||
|
|
||||||
alterRaceRotation(race, degreesToRotate);
|
alterRaceRotation(race, degreesToRotate);
|
||||||
|
|
||||||
JAXBContext context = JAXBContext.newInstance(XMLRace.class);
|
return XMLUtilities.classToXML(race);
|
||||||
Marshaller jaxbMarshaller = context.createMarshaller();
|
|
||||||
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
|
|
||||||
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
|
|
||||||
jaxbMarshaller.marshal(race, sw);
|
|
||||||
return sw.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotate the features in a race such as the boundary, and the marks.
|
* Rotate the features in a race such as the boundary, and the marks.
|
||||||
* @param race the race to alter
|
* @param race the race to alter
|
||||||
@@ -186,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.
|
//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;
|
long secondsToAdd = millisecondsToAdd / 1000;
|
||||||
//Scale the time using our time scalar.
|
//Scale the time using our time scalar.
|
||||||
secondsToAdd = secondsToAdd / Constants.RaceTimeScale;
|
secondsToAdd = secondsToAdd / Constants.RaceTimeScale;
|
||||||
@@ -204,4 +200,13 @@ public class RaceXMLCreator {
|
|||||||
raceXML.getRaceStartTime().setTime(dateFormat.format(creationTime.plusSeconds(secondsToAdd)));
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ public enum RaceTypeEnum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a mapping between Byte values and RaceStatusEnum values.
|
* Stores a mapping between Byte values and RaceStatusEnum values.
|
||||||
*/
|
*/
|
||||||
@@ -107,4 +109,14 @@ public enum RaceTypeEnum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (fromByte(value) == FLEET_RACE) {
|
||||||
|
return "fleet";
|
||||||
|
} else if (fromByte(value) == MATCH_RACE) {
|
||||||
|
return "match";
|
||||||
|
} else {
|
||||||
|
return super.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public class LatestMessages extends Observable {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ctor.
|
* Ctor.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -22,4 +22,16 @@ public interface BoatDataSource {
|
|||||||
* @return Map between source ID and mark.
|
* @return Map between source ID and mark.
|
||||||
*/
|
*/
|
||||||
Map<Integer, Mark> getMarkerBoats();
|
Map<Integer, Mark> getMarkerBoats();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sequence number associated with this data source. Used to indicate when it has changed.
|
||||||
|
* @return Sequence number.
|
||||||
|
*/
|
||||||
|
int getSequenceNumber();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the sequence number for this data source. Used to indicate that it has changed.
|
||||||
|
*/
|
||||||
|
void incrementSequenceNumber();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ public class BoatXMLReader extends XMLReader implements BoatDataSource {
|
|||||||
private final Map<Integer, Mark> markerMap = new HashMap<>();
|
private final Map<Integer, Mark> markerMap = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
private int sequenceNumber = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for Boat XML using a file.
|
* Constructor for Boat XML using a file.
|
||||||
*
|
*
|
||||||
@@ -174,4 +176,15 @@ public class BoatXMLReader extends XMLReader implements BoatDataSource {
|
|||||||
public Map<Integer, Mark> getMarkerBoats() {
|
public Map<Integer, Mark> getMarkerBoats() {
|
||||||
return markerMap;
|
return markerMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSequenceNumber() {
|
||||||
|
return sequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrementSequenceNumber() {
|
||||||
|
sequenceNumber++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ public class EmptyBoatDataSource implements BoatDataSource {
|
|||||||
private final Map<Integer, Mark> markerMap = new HashMap<>();
|
private final Map<Integer, Mark> markerMap = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
private int sequenceNumber = 0;
|
||||||
|
|
||||||
|
|
||||||
public EmptyBoatDataSource() {
|
public EmptyBoatDataSource() {
|
||||||
}
|
}
|
||||||
@@ -44,4 +46,15 @@ public class EmptyBoatDataSource implements BoatDataSource {
|
|||||||
public Map<Integer, Mark> getMarkerBoats() {
|
public Map<Integer, Mark> getMarkerBoats() {
|
||||||
return markerMap;
|
return markerMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSequenceNumber() {
|
||||||
|
return sequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrementSequenceNumber() {
|
||||||
|
sequenceNumber++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package shared.dataInput;
|
|||||||
|
|
||||||
import network.Messages.Enums.RaceTypeEnum;
|
import network.Messages.Enums.RaceTypeEnum;
|
||||||
import shared.model.CompoundMark;
|
import shared.model.CompoundMark;
|
||||||
|
import shared.model.Corner;
|
||||||
import shared.model.GPSCoordinate;
|
import shared.model.GPSCoordinate;
|
||||||
import shared.model.Leg;
|
import shared.model.Leg;
|
||||||
|
import shared.xml.Race.XMLCorner;
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -48,6 +50,12 @@ public class EmptyRaceDataSource implements RaceDataSource {
|
|||||||
*/
|
*/
|
||||||
private final List<Leg> legs = new ArrayList<>();
|
private final List<Leg> legs = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Corners in race.
|
||||||
|
*/
|
||||||
|
private final List<Corner> corners = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time that the race.xml file was created.
|
* The time that the race.xml file was created.
|
||||||
@@ -76,6 +84,8 @@ public class EmptyRaceDataSource implements RaceDataSource {
|
|||||||
private RaceTypeEnum raceType = RaceTypeEnum.NOT_A_RACE_TYPE;
|
private RaceTypeEnum raceType = RaceTypeEnum.NOT_A_RACE_TYPE;
|
||||||
|
|
||||||
|
|
||||||
|
private int sequenceNumber = 0;
|
||||||
|
|
||||||
|
|
||||||
public EmptyRaceDataSource() {
|
public EmptyRaceDataSource() {
|
||||||
}
|
}
|
||||||
@@ -98,6 +108,11 @@ public class EmptyRaceDataSource implements RaceDataSource {
|
|||||||
return legs;
|
return legs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Corner> getCorners() {
|
||||||
|
return corners;
|
||||||
|
}
|
||||||
|
|
||||||
public List<CompoundMark> getCompoundMarks() {
|
public List<CompoundMark> getCompoundMarks() {
|
||||||
return new ArrayList<>(compoundMarkMap.values());
|
return new ArrayList<>(compoundMarkMap.values());
|
||||||
}
|
}
|
||||||
@@ -126,4 +141,14 @@ public class EmptyRaceDataSource implements RaceDataSource {
|
|||||||
public List<Integer> getParticipants() {
|
public List<Integer> getParticipants() {
|
||||||
return participants;
|
return participants;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSequenceNumber() {
|
||||||
|
return sequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrementSequenceNumber() {
|
||||||
|
sequenceNumber++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ public class EmptyRegattaDataSource implements RegattaDataSource {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private int sequenceNumber = 0;
|
||||||
|
|
||||||
|
|
||||||
public EmptyRegattaDataSource() {
|
public EmptyRegattaDataSource() {
|
||||||
}
|
}
|
||||||
@@ -119,4 +121,15 @@ public class EmptyRegattaDataSource implements RegattaDataSource {
|
|||||||
public GPSCoordinate getGPSCoordinate() {
|
public GPSCoordinate getGPSCoordinate() {
|
||||||
return new GPSCoordinate(centralLatitude, centralLongitude);
|
return new GPSCoordinate(centralLatitude, centralLongitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSequenceNumber() {
|
||||||
|
return sequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrementSequenceNumber() {
|
||||||
|
sequenceNumber++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package shared.dataInput;
|
|||||||
|
|
||||||
import network.Messages.Enums.RaceTypeEnum;
|
import network.Messages.Enums.RaceTypeEnum;
|
||||||
import shared.model.CompoundMark;
|
import shared.model.CompoundMark;
|
||||||
|
import shared.model.Corner;
|
||||||
import shared.model.GPSCoordinate;
|
import shared.model.GPSCoordinate;
|
||||||
import shared.model.Leg;
|
import shared.model.Leg;
|
||||||
|
import shared.xml.Race.XMLCorner;
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -27,6 +29,12 @@ public interface RaceDataSource {
|
|||||||
*/
|
*/
|
||||||
List<Leg> getLegs();
|
List<Leg> getLegs();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of corners in the race - two adjacent corners form a leg.
|
||||||
|
* @return List of corners in race.
|
||||||
|
*/
|
||||||
|
List<Corner> getCorners();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of coordinates representing the boundary of the race.
|
* Returns a list of coordinates representing the boundary of the race.
|
||||||
* @return The boundary of the race.
|
* @return The boundary of the race.
|
||||||
@@ -83,4 +91,16 @@ public interface RaceDataSource {
|
|||||||
* @return Bottom right GPS coordinate.
|
* @return Bottom right GPS coordinate.
|
||||||
*/
|
*/
|
||||||
GPSCoordinate getMapBottomRight();
|
GPSCoordinate getMapBottomRight();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sequence number associated with this data source. Used to indicate when it has changed.
|
||||||
|
* @return Sequence number.
|
||||||
|
*/
|
||||||
|
int getSequenceNumber();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the sequence number for this data source. Used to indicate that it has changed.
|
||||||
|
*/
|
||||||
|
void incrementSequenceNumber();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
|
|||||||
private RaceTypeEnum raceType;
|
private RaceTypeEnum raceType;
|
||||||
|
|
||||||
|
|
||||||
|
private int sequenceNumber = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -343,7 +345,7 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
|
|||||||
|
|
||||||
int cornerSeq = Integer.parseInt(getAttribute(cornerElement, "SeqID"));
|
int cornerSeq = Integer.parseInt(getAttribute(cornerElement, "SeqID"));
|
||||||
|
|
||||||
cornersList.add(new Corner(cornerID, cornerSeq, "SP", 3));
|
cornersList.add(new Corner(cornerID, cornerSeq, cornerRounding, 3));
|
||||||
|
|
||||||
//Gets the CompoundMark associated with this corner.
|
//Gets the CompoundMark associated with this corner.
|
||||||
CompoundMark lastCompoundMark = this.compoundMarkMap.get(cornerID);
|
CompoundMark lastCompoundMark = this.compoundMarkMap.get(cornerID);
|
||||||
@@ -366,7 +368,7 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
|
|||||||
|
|
||||||
cornerSeq = Integer.parseInt(getAttribute(cornerElement, "SeqID"));
|
cornerSeq = Integer.parseInt(getAttribute(cornerElement, "SeqID"));
|
||||||
|
|
||||||
cornersList.add(new Corner(cornerID, cornerSeq, "Port", 3));
|
cornersList.add(new Corner(cornerID, cornerSeq, getCompoundMarkRounding(cornerElement), 3));
|
||||||
//gets the Rounding of this corner element
|
//gets the Rounding of this corner element
|
||||||
cornerRounding = getCompoundMarkRounding(cornerElement);
|
cornerRounding = getCompoundMarkRounding(cornerElement);
|
||||||
|
|
||||||
@@ -488,7 +490,17 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
|
|||||||
return participants;
|
return participants;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Corner> getCornersList() {
|
public List<Corner> getCorners() {
|
||||||
return cornersList;
|
return cornersList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSequenceNumber() {
|
||||||
|
return sequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrementSequenceNumber() {
|
||||||
|
sequenceNumber++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,5 +66,15 @@ public interface RegattaDataSource {
|
|||||||
float getMagneticVariation();
|
float getMagneticVariation();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sequence number associated with this data source. Used to indicate when it has changed.
|
||||||
|
* @return Sequence number.
|
||||||
|
*/
|
||||||
|
int getSequenceNumber();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the sequence number for this data source. Used to indicate that it has changed.
|
||||||
|
*/
|
||||||
|
void incrementSequenceNumber();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ public class RegattaXMLReader extends XMLReader implements RegattaDataSource {
|
|||||||
private float magneticVariation;
|
private float magneticVariation;
|
||||||
|
|
||||||
|
|
||||||
|
private int sequenceNumber = 0;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for Regatta XML using a file.
|
* Constructor for Regatta XML using a file.
|
||||||
@@ -209,4 +211,14 @@ public class RegattaXMLReader extends XMLReader implements RegattaDataSource {
|
|||||||
public GPSCoordinate getGPSCoordinate() {
|
public GPSCoordinate getGPSCoordinate() {
|
||||||
return new GPSCoordinate(centralLatitude, centralLongitude);
|
return new GPSCoordinate(centralLatitude, centralLongitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSequenceNumber() {
|
||||||
|
return sequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrementSequenceNumber() {
|
||||||
|
sequenceNumber++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,4 +46,19 @@ public enum RoundingType {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getStringOf(RoundingType value) {
|
||||||
|
switch (value) {
|
||||||
|
case Port:
|
||||||
|
return "Port";
|
||||||
|
case Starboard:
|
||||||
|
return "Starboard";
|
||||||
|
case SP:
|
||||||
|
return "SP";
|
||||||
|
case PS:
|
||||||
|
return "PS";
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,20 +33,26 @@ public class Constants {
|
|||||||
* Frame periods are multiplied by this to get the amount of time a single frame represents.
|
* Frame periods are multiplied by this to get the amount of time a single frame represents.
|
||||||
* E.g., frame period = 20ms, scale = 5, frame represents 20 * 5 = 100ms, and so boats are simulated for 100ms, even though only 20ms actually occurred.
|
* E.g., frame period = 20ms, scale = 5, frame represents 20 * 5 = 100ms, and so boats are simulated for 100ms, even though only 20ms actually occurred.
|
||||||
*/
|
*/
|
||||||
public static final int RaceTimeScale = 10;
|
public static final int RaceTimeScale = 2;//10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The race pre-start time, in milliseconds. 3 minutes.
|
* The race pre-start time, in milliseconds. 3 minutes (30 seconds for development).
|
||||||
*/
|
*/
|
||||||
public static final long RacePreStartTime = 1 * 10 * 1000;
|
// public static final long RacePreStartTime = 30 * 1000;
|
||||||
|
public static final long RacePreStartTime = 1000;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The race preparatory time, in milliseconds. 1 minutes.
|
* The race preparatory time, in milliseconds. 1 minute.
|
||||||
*/
|
*/
|
||||||
|
// public static final long RacePreparatoryTime = 60 * 1000;
|
||||||
public static final long RacePreparatoryTime = 1 * 60 * 1000;
|
public static final long RacePreparatoryTime = 1 * 60 * 1000;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of milliseconds in one hour.
|
* The number of milliseconds in one hour.
|
||||||
* <br>
|
* <br>
|
||||||
|
|||||||
@@ -1,318 +0,0 @@
|
|||||||
package shared.model;
|
|
||||||
|
|
||||||
import javafx.beans.property.IntegerProperty;
|
|
||||||
import javafx.beans.property.Property;
|
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
import mock.model.collider.ColliderRegistry;
|
|
||||||
import network.Messages.Enums.RaceStatusEnum;
|
|
||||||
import network.Messages.Enums.RaceTypeEnum;
|
|
||||||
import shared.dataInput.BoatDataSource;
|
|
||||||
import shared.dataInput.RaceDataSource;
|
|
||||||
import shared.dataInput.RegattaDataSource;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a yacht race.
|
|
||||||
* Has a course, state, wind, boundaries, etc.... Boats are added by inheriting classes (see {@link Boat}, {@link mock.model.MockBoat}, {@link visualiser.model.VisualiserBoat}.
|
|
||||||
*/
|
|
||||||
public abstract class Race {
|
|
||||||
/**
|
|
||||||
* The source of race related data.
|
|
||||||
*/
|
|
||||||
protected RaceDataSource raceDataSource;
|
|
||||||
/**
|
|
||||||
* The source of boat related data.
|
|
||||||
*/
|
|
||||||
protected BoatDataSource boatDataSource;
|
|
||||||
/**
|
|
||||||
* The source of regatta related data.
|
|
||||||
*/
|
|
||||||
protected RegattaDataSource regattaDataSource;
|
|
||||||
/**
|
|
||||||
* A list of compound marks in the race.
|
|
||||||
*/
|
|
||||||
protected List<CompoundMark> compoundMarks;
|
|
||||||
/**
|
|
||||||
* A list of legs in the race.
|
|
||||||
*/
|
|
||||||
protected List<Leg> legs;
|
|
||||||
/**
|
|
||||||
* A list of coordinates describing the boundary of the course.
|
|
||||||
*/
|
|
||||||
protected List<GPSCoordinate> boundary;
|
|
||||||
/**
|
|
||||||
* The clock which tracks the race's start time, current time, and elapsed duration.
|
|
||||||
*/
|
|
||||||
protected RaceClock raceClock;
|
|
||||||
/**
|
|
||||||
* The race ID of the course.
|
|
||||||
*/
|
|
||||||
protected int raceId;
|
|
||||||
/**
|
|
||||||
* The name of the regatta.
|
|
||||||
*/
|
|
||||||
protected String regattaName;
|
|
||||||
/**
|
|
||||||
* The current status of the race.
|
|
||||||
*/
|
|
||||||
protected RaceStatusEnum raceStatusEnum;
|
|
||||||
/**
|
|
||||||
* The type of race this is.
|
|
||||||
*/
|
|
||||||
protected RaceTypeEnum raceType;
|
|
||||||
/**
|
|
||||||
* The race's wind.
|
|
||||||
*/
|
|
||||||
protected Property<Wind> raceWind = new SimpleObjectProperty<>();
|
|
||||||
/**
|
|
||||||
* Registry for all collider object in this race
|
|
||||||
*/
|
|
||||||
protected ColliderRegistry colliderRegistry;
|
|
||||||
/**
|
|
||||||
* The number of frames per second.
|
|
||||||
* We essentially track the number of frames generated per second, over a one second period. When {@link #lastFpsResetTime} reaches 1 second, currentFps is reset.
|
|
||||||
*/
|
|
||||||
private int currentFps = 0;
|
|
||||||
/**
|
|
||||||
* The number of frames per second we generated over the last 1 second period.
|
|
||||||
*/
|
|
||||||
private IntegerProperty lastFps = new SimpleIntegerProperty(0);
|
|
||||||
/**
|
|
||||||
* The time, in milliseconds, since we last reset our {@link #currentFps} counter.
|
|
||||||
*/
|
|
||||||
private long lastFpsResetTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a race object with a given BoatDataSource, RaceDataSource, and RegattaDataSource.
|
|
||||||
* @param boatDataSource Data source for boat related data (yachts and marker boats).
|
|
||||||
* @param raceDataSource Data source for race related data (participating boats, legs, etc...).
|
|
||||||
* @param regattaDataSource Data source for race related data (course name, location, timezone, etc...).
|
|
||||||
*/
|
|
||||||
public Race(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource) {
|
|
||||||
|
|
||||||
//Keep a reference to data sources.
|
|
||||||
this.raceDataSource = raceDataSource;
|
|
||||||
this.boatDataSource = boatDataSource;
|
|
||||||
this.regattaDataSource = regattaDataSource;
|
|
||||||
|
|
||||||
//Marks.
|
|
||||||
this.compoundMarks = raceDataSource.getCompoundMarks();
|
|
||||||
//Boundaries.
|
|
||||||
this.boundary = raceDataSource.getBoundary();
|
|
||||||
//Legs.
|
|
||||||
this.useLegsList(raceDataSource.getLegs());
|
|
||||||
//Race ID.
|
|
||||||
this.raceId = raceDataSource.getRaceId();
|
|
||||||
//Regatta name.
|
|
||||||
this.regattaName = regattaDataSource.getRegattaName();
|
|
||||||
//Race clock.
|
|
||||||
this.raceClock = new RaceClock(this.raceDataSource.getStartDateTime());
|
|
||||||
//Race status.
|
|
||||||
this.setRaceStatusEnum(RaceStatusEnum.NOT_ACTIVE);
|
|
||||||
//Race type.
|
|
||||||
this.raceType = raceDataSource.getRaceType();
|
|
||||||
//Wind.
|
|
||||||
this.setWind(Bearing.fromDegrees(0), 0);
|
|
||||||
// Set up colliders
|
|
||||||
this.colliderRegistry = new ColliderRegistry();
|
|
||||||
|
|
||||||
for(CompoundMark mark: compoundMarks) {
|
|
||||||
colliderRegistry.addCollider(mark.getMark1());
|
|
||||||
if(mark.getMark2() != null) colliderRegistry.addCollider(mark.getMark2());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ColliderRegistry getColliderRegistry() {
|
|
||||||
return colliderRegistry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialise the boats in the race.
|
|
||||||
* This sets their starting positions and current legs.
|
|
||||||
*/
|
|
||||||
protected abstract void initialiseBoats();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the race to use a new list of legs, and adds a dummy "Finish" leg at the end.
|
|
||||||
* @param legs The new list of legs to use.
|
|
||||||
*/
|
|
||||||
protected void useLegsList(List<Leg> legs) {
|
|
||||||
//We add a "dummy" leg at the end of the race.
|
|
||||||
this.legs = legs;
|
|
||||||
this.legs.add(new Leg("Finish", this.legs.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether or not a specific leg is the last leg in the race.
|
|
||||||
* @param leg The leg to check.
|
|
||||||
* @return Returns true if it is the last, false otherwise.
|
|
||||||
*/
|
|
||||||
protected boolean isLastLeg(Leg leg) {
|
|
||||||
|
|
||||||
//Get the last leg.
|
|
||||||
Leg lastLeg = this.legs.get(this.legs.size() - 1);
|
|
||||||
|
|
||||||
//Check its ID.
|
|
||||||
int lastLegID = lastLeg.getLegNumber();
|
|
||||||
|
|
||||||
//Get the specified leg's ID.
|
|
||||||
int legID = leg.getLegNumber();
|
|
||||||
|
|
||||||
|
|
||||||
//Check if they are the same.
|
|
||||||
return legID == lastLegID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current race status.
|
|
||||||
* @return The current race status.
|
|
||||||
*/
|
|
||||||
public RaceStatusEnum getRaceStatusEnum() {
|
|
||||||
return raceStatusEnum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the current race status.
|
|
||||||
* @param raceStatusEnum The new status of the race.
|
|
||||||
*/
|
|
||||||
public void setRaceStatusEnum(RaceStatusEnum raceStatusEnum) {
|
|
||||||
this.raceStatusEnum = raceStatusEnum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the type of race this is.
|
|
||||||
* @return The type of race this is.
|
|
||||||
*/
|
|
||||||
public RaceTypeEnum getRaceType() {
|
|
||||||
return raceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name of the regatta.
|
|
||||||
* @return The name of the regatta.
|
|
||||||
*/
|
|
||||||
public String getRegattaName() {
|
|
||||||
return regattaName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the race to have a specified wind bearing and speed.
|
|
||||||
* @param windBearing New wind bearing.
|
|
||||||
* @param windSpeedKnots New wind speed, in knots.
|
|
||||||
*/
|
|
||||||
protected void setWind(Bearing windBearing, double windSpeedKnots) {
|
|
||||||
Wind wind = new Wind(windBearing, windSpeedKnots);
|
|
||||||
setWind(wind);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the race to have a specified wind (bearing and speed).
|
|
||||||
* @param wind New wind.
|
|
||||||
*/
|
|
||||||
protected void setWind(Wind wind) {
|
|
||||||
this.raceWind.setValue(wind);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the wind bearing.
|
|
||||||
* @return The wind bearing.
|
|
||||||
*/
|
|
||||||
public Bearing getWindDirection() {
|
|
||||||
return raceWind.getValue().getWindDirection();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the wind speed.
|
|
||||||
* Measured in knots.
|
|
||||||
* @return The wind speed.
|
|
||||||
*/
|
|
||||||
public double getWindSpeed() {
|
|
||||||
return raceWind.getValue().getWindSpeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the RaceClock for this race.
|
|
||||||
* This is used to track the start time, current time, and elapsed duration of the race.
|
|
||||||
* @return The RaceClock for the race.
|
|
||||||
*/
|
|
||||||
public RaceClock getRaceClock() {
|
|
||||||
return raceClock;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the RaceDataSource used for the race.
|
|
||||||
* @return The RaceDataSource used for the race.
|
|
||||||
*/
|
|
||||||
public RaceDataSource getRaceDataSource() {
|
|
||||||
return raceDataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of legs in the race.
|
|
||||||
* @return The number of legs in the race.
|
|
||||||
*/
|
|
||||||
public int getLegCount() {
|
|
||||||
//We minus one, as we have added an extra "dummy" leg.
|
|
||||||
return legs.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the race boundary.
|
|
||||||
* @return The race boundary.
|
|
||||||
*/
|
|
||||||
public List<GPSCoordinate> getBoundary() {
|
|
||||||
return boundary;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the marks of the race.
|
|
||||||
* @return Marks of the race.
|
|
||||||
*/
|
|
||||||
public List<CompoundMark> getCompoundMarks() {
|
|
||||||
return compoundMarks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the legs of the race.
|
|
||||||
* @return Legs of the race.
|
|
||||||
*/
|
|
||||||
public List<Leg> getLegs() {
|
|
||||||
return legs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the fps property.
|
|
||||||
* @return The fps property.
|
|
||||||
*/
|
|
||||||
public IntegerProperty fpsProperty() {
|
|
||||||
return lastFps;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increments the FPS counter, and adds timePeriod milliseconds to our FPS reset timer.
|
|
||||||
* @param timePeriod Time, in milliseconds, to add to {@link #lastFpsResetTime}.
|
|
||||||
*/
|
|
||||||
protected void incrementFps(long timePeriod) {
|
|
||||||
//Increment.
|
|
||||||
this.currentFps++;
|
|
||||||
|
|
||||||
//Add period to timer.
|
|
||||||
this.lastFpsResetTime += timePeriod;
|
|
||||||
|
|
||||||
//If we have reached 1 second period, snapshot the framerate and reset.
|
|
||||||
if (this.lastFpsResetTime > 1000) {
|
|
||||||
this.lastFps.set(this.currentFps);
|
|
||||||
|
|
||||||
this.currentFps = 0;
|
|
||||||
this.lastFpsResetTime = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRaceId() {
|
|
||||||
return raceId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -188,7 +188,20 @@ public abstract class RaceState {
|
|||||||
* @return List of mark boats.
|
* @return List of mark boats.
|
||||||
*/
|
*/
|
||||||
public List<Mark> getMarks() {
|
public List<Mark> getMarks() {
|
||||||
return new ArrayList<>(boatDataSource.getMarkerBoats().values());
|
//BoatDataSource contains a collection of Marks, and RaceDataSource contains a collection of Compound marks (which contain marks). RaceDataSource is the "definitive" source of mark data.
|
||||||
|
List<Mark> marks = new ArrayList<>(getCompoundMarks().size() * 2);
|
||||||
|
|
||||||
|
for (CompoundMark compoundMark : getCompoundMarks()) {
|
||||||
|
if (compoundMark.getMark1() != null) {
|
||||||
|
marks.add(compoundMark.getMark1());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compoundMark.getMark2() != null) {
|
||||||
|
marks.add(compoundMark.getMark2());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return marks;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -48,4 +48,11 @@ public class Wind {
|
|||||||
return windSpeed;
|
return windSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setWindSpeed(double windSpeed) {
|
||||||
|
this.windSpeed = windSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWindDirection(Bearing windDirection) {
|
||||||
|
this.windDirection = windDirection;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package shared.xml.Race;
|
||||||
|
|
||||||
|
import shared.dataInput.RaceDataSource;
|
||||||
|
import shared.enums.RoundingType;
|
||||||
|
import shared.model.CompoundMark;
|
||||||
|
import shared.model.Corner;
|
||||||
|
import shared.model.GPSCoordinate;
|
||||||
|
import shared.model.Leg;
|
||||||
|
import shared.xml.XMLUtilities;
|
||||||
|
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has functions to convert a {@link shared.dataInput.RaceDataSource} to an {@link XMLRace} object.
|
||||||
|
*/
|
||||||
|
public class RaceDataSourceToXML {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a race data source to an XMLRace object.
|
||||||
|
* @param raceDataSource The data source to convert.
|
||||||
|
* @return The XMLRace file.
|
||||||
|
*/
|
||||||
|
public static XMLRace toXML(RaceDataSource raceDataSource) {
|
||||||
|
|
||||||
|
//Kind of ugly. Could be refactored/split up a bit.
|
||||||
|
|
||||||
|
|
||||||
|
XMLRace race = new XMLRace();
|
||||||
|
|
||||||
|
|
||||||
|
race.setRaceID(raceDataSource.getRaceId());
|
||||||
|
race.setRaceType(raceDataSource.getRaceType().toString());
|
||||||
|
|
||||||
|
|
||||||
|
race.setCreationTimeDate(raceDataSource.getCreationDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ")));
|
||||||
|
|
||||||
|
|
||||||
|
XMLRaceStartTime startTime = new XMLRaceStartTime();
|
||||||
|
startTime.setPostpone(String.valueOf(raceDataSource.getPostponed()));
|
||||||
|
startTime.setTime(raceDataSource.getStartDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ")));
|
||||||
|
race.setRaceStartTime(startTime);
|
||||||
|
|
||||||
|
|
||||||
|
XMLParticipants participants = new XMLParticipants();
|
||||||
|
participants.yacht = new ArrayList<>();
|
||||||
|
for (int i : raceDataSource.getParticipants()) {
|
||||||
|
XMLYacht yacht = new XMLYacht();
|
||||||
|
yacht.setSourceID(i);
|
||||||
|
participants.yacht.add(yacht);
|
||||||
|
}
|
||||||
|
race.setParticipants(participants);
|
||||||
|
|
||||||
|
|
||||||
|
XMLCourseLimit courseLimit = new XMLCourseLimit();
|
||||||
|
courseLimit.limit = new ArrayList<>();
|
||||||
|
for (int i = 0; i < raceDataSource.getBoundary().size(); i++) {
|
||||||
|
XMLLimit limit = new XMLLimit();
|
||||||
|
limit.setLat(raceDataSource.getBoundary().get(i).getLatitude());
|
||||||
|
limit.setLon(raceDataSource.getBoundary().get(i).getLongitude());
|
||||||
|
limit.setSeqID(i + 1);
|
||||||
|
courseLimit.limit.add(limit);
|
||||||
|
}
|
||||||
|
race.setCourseLimit(courseLimit);
|
||||||
|
|
||||||
|
|
||||||
|
XMLCourse course = new XMLCourse();
|
||||||
|
course.compoundMark = new ArrayList<>();
|
||||||
|
for (CompoundMark compoundMark : raceDataSource.getCompoundMarks()) {
|
||||||
|
XMLCompoundMark xmlCompoundMark = new XMLCompoundMark();
|
||||||
|
xmlCompoundMark.setCompoundMarkID(compoundMark.getCompoundMarkID());
|
||||||
|
xmlCompoundMark.setName(compoundMark.getName());
|
||||||
|
|
||||||
|
if (compoundMark.getMark1() != null) {
|
||||||
|
XMLMark xmlMark = new XMLMark();
|
||||||
|
xmlMark.setName(compoundMark.getMark1().getName());
|
||||||
|
xmlMark.setSourceID(compoundMark.getMark1().getSourceID());
|
||||||
|
xmlMark.setSeqId(1);
|
||||||
|
xmlMark.setTargetLat(compoundMark.getMark1().getPosition().getLatitude());
|
||||||
|
xmlMark.setTargetLng(compoundMark.getMark1().getPosition().getLongitude());
|
||||||
|
|
||||||
|
xmlCompoundMark.getMark().add(xmlMark);
|
||||||
|
}
|
||||||
|
if (compoundMark.getMark2() != null) {
|
||||||
|
XMLMark xmlMark = new XMLMark();
|
||||||
|
xmlMark.setName(compoundMark.getMark2().getName());
|
||||||
|
xmlMark.setSourceID(compoundMark.getMark2().getSourceID());
|
||||||
|
xmlMark.setSeqId(2);
|
||||||
|
xmlMark.setTargetLat(compoundMark.getMark2().getPosition().getLatitude());
|
||||||
|
xmlMark.setTargetLng(compoundMark.getMark2().getPosition().getLongitude());
|
||||||
|
|
||||||
|
xmlCompoundMark.getMark().add(xmlMark);
|
||||||
|
}
|
||||||
|
|
||||||
|
course.compoundMark.add(xmlCompoundMark);
|
||||||
|
}
|
||||||
|
race.setCourse(course);
|
||||||
|
|
||||||
|
XMLCompoundMarkSequence compoundMarkSequence = new XMLCompoundMarkSequence();
|
||||||
|
compoundMarkSequence.corner = new ArrayList<>();
|
||||||
|
for (Corner corner : raceDataSource.getCorners()) {
|
||||||
|
XMLCorner xmlCorner = new XMLCorner();
|
||||||
|
xmlCorner.setZoneSize(corner.getZoneSize());
|
||||||
|
xmlCorner.setSeqID(corner.getSeqID());
|
||||||
|
xmlCorner.setCompoundMarkID(corner.getCompoundMarkID());
|
||||||
|
xmlCorner.setRounding(corner.getRounding());
|
||||||
|
|
||||||
|
compoundMarkSequence.corner.add(xmlCorner);
|
||||||
|
}
|
||||||
|
race.setCompoundMarkSequence(compoundMarkSequence);
|
||||||
|
|
||||||
|
|
||||||
|
return race;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a race data source to an xml string.
|
||||||
|
* @param raceDataSource Data source to convert.
|
||||||
|
* @return String containing xml file.
|
||||||
|
* @throws JAXBException Thrown if it cannot be converted.
|
||||||
|
*/
|
||||||
|
public static String toString(RaceDataSource raceDataSource) throws JAXBException {
|
||||||
|
XMLRace race = toXML(raceDataSource);
|
||||||
|
return XMLUtilities.classToXML(race);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,10 +20,17 @@ import java.io.*;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by fwy13 on 13/08/17.
|
* Contains utility functions to convert between xml files and xml class objects.
|
||||||
*/
|
*/
|
||||||
public class XMLUtilities {
|
public class XMLUtilities {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an XML class object to an XML string.
|
||||||
|
* @param o The XML class object to convert.
|
||||||
|
* @return String containing the serialised XML data.
|
||||||
|
* @throws JAXBException Thrown if the object is cannot be serialised to XML.
|
||||||
|
*/
|
||||||
public static String classToXML(Object o) throws JAXBException {
|
public static String classToXML(Object o) throws JAXBException {
|
||||||
JAXBContext context = JAXBContext.newInstance(o.getClass());
|
JAXBContext context = JAXBContext.newInstance(o.getClass());
|
||||||
Marshaller jaxbMarshaller = context.createMarshaller();
|
Marshaller jaxbMarshaller = context.createMarshaller();
|
||||||
@@ -49,14 +56,26 @@ public class XMLUtilities {
|
|||||||
return xmlToClass(document, schemaURL, c);
|
return xmlToClass(document, schemaURL, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object xmlToClass(InputStream i, URL schemaURL, Class c) throws ParserConfigurationException, IOException, SAXException, JAXBException {
|
/**
|
||||||
|
* Converts an XML file to an XML class (e.g., {@link shared.xml.Race.XMLRace}).
|
||||||
|
* @param i The input stream for the XML file.
|
||||||
|
* @param schemaURL URL for the XML schema.
|
||||||
|
* @param c The XML class to convert to.
|
||||||
|
* @param <T> The XML class to convert to.
|
||||||
|
* @return The XML class object.
|
||||||
|
* @throws ParserConfigurationException
|
||||||
|
* @throws IOException
|
||||||
|
* @throws SAXException
|
||||||
|
* @throws JAXBException
|
||||||
|
*/
|
||||||
|
public static <T> T xmlToClass(InputStream i, URL schemaURL, Class<T> c) throws ParserConfigurationException, IOException, SAXException, JAXBException {
|
||||||
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||||
Document document = parser.parse(i);
|
Document document = parser.parse(i);
|
||||||
|
|
||||||
return xmlToClass(document, schemaURL, c);
|
return xmlToClass(document, schemaURL, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object xmlToClass(Document document, URL schemaURL, Class c) throws ParserConfigurationException, IOException, SAXException, JAXBException {
|
public static <T> T xmlToClass(Document document, URL schemaURL, Class<T> c) throws ParserConfigurationException, IOException, SAXException, JAXBException {
|
||||||
JAXBContext jc = JAXBContext.newInstance(c);
|
JAXBContext jc = JAXBContext.newInstance(c);
|
||||||
Unmarshaller unmarshaller = jc.createUnmarshaller();
|
Unmarshaller unmarshaller = jc.createUnmarshaller();
|
||||||
|
|
||||||
@@ -64,7 +83,7 @@ public class XMLUtilities {
|
|||||||
Schema schema = sf.newSchema(schemaURL);
|
Schema schema = sf.newSchema(schemaURL);
|
||||||
unmarshaller.setSchema(schema);
|
unmarshaller.setSchema(schema);
|
||||||
|
|
||||||
return unmarshaller.unmarshal(new DOMSource(document));
|
return (T) unmarshaller.unmarshal(new DOMSource(document));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean validateXML(String file, URL schemaURL){
|
public static boolean validateXML(String file, URL schemaURL){
|
||||||
|
|||||||
@@ -0,0 +1,533 @@
|
|||||||
|
//
|
||||||
|
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802
|
||||||
|
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
|
||||||
|
// Any modifications to this file will be lost upon recompilation of the source schema.
|
||||||
|
// Generated on: 2017.09.01 at 11:12:43 PM NZST
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package shared.xml.boats;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlAttribute;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
import javax.xml.bind.annotation.XmlType;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Java class for anonymous complex type.
|
||||||
|
*
|
||||||
|
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <complexType>
|
||||||
|
* <complexContent>
|
||||||
|
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||||
|
* <sequence>
|
||||||
|
* <element name="Boats">
|
||||||
|
* <complexType>
|
||||||
|
* <complexContent>
|
||||||
|
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||||
|
* <sequence>
|
||||||
|
* <element name="Boat" maxOccurs="unbounded">
|
||||||
|
* <complexType>
|
||||||
|
* <complexContent>
|
||||||
|
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||||
|
* <sequence>
|
||||||
|
* <element name="GPSposition">
|
||||||
|
* <complexType>
|
||||||
|
* <complexContent>
|
||||||
|
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||||
|
* <attribute name="X" type="{http://www.w3.org/2001/XMLSchema}double" />
|
||||||
|
* <attribute name="Y" use="required" type="{http://www.w3.org/2001/XMLSchema}double" />
|
||||||
|
* <attribute name="Z" use="required" type="{http://www.w3.org/2001/XMLSchema}double" />
|
||||||
|
* </restriction>
|
||||||
|
* </complexContent>
|
||||||
|
* </complexType>
|
||||||
|
* </element>
|
||||||
|
* </sequence>
|
||||||
|
* <attribute name="Type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* <attribute name="BoatName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* <attribute name="SourceID" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
|
||||||
|
* <attribute name="HullNum" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* <attribute name="ShortName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* <attribute name="ShapeID" type="{http://www.w3.org/2001/XMLSchema}int" />
|
||||||
|
* <attribute name="StoweName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* </restriction>
|
||||||
|
* </complexContent>
|
||||||
|
* </complexType>
|
||||||
|
* </element>
|
||||||
|
* </sequence>
|
||||||
|
* </restriction>
|
||||||
|
* </complexContent>
|
||||||
|
* </complexType>
|
||||||
|
* </element>
|
||||||
|
* </sequence>
|
||||||
|
* </restriction>
|
||||||
|
* </complexContent>
|
||||||
|
* </complexType>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@XmlType(name = "", propOrder = {
|
||||||
|
"boats"
|
||||||
|
})
|
||||||
|
@XmlRootElement(name = "BoatConfig")
|
||||||
|
public class BoatConfig {
|
||||||
|
|
||||||
|
@XmlElement(name = "Boats", required = true)
|
||||||
|
protected BoatConfig.Boats boats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the boats property.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* possible object is
|
||||||
|
* {@link BoatConfig.Boats }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public BoatConfig.Boats getBoats() {
|
||||||
|
return boats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the boats property.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* allowed object is
|
||||||
|
* {@link BoatConfig.Boats }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setBoats(BoatConfig.Boats value) {
|
||||||
|
this.boats = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Java class for anonymous complex type.
|
||||||
|
*
|
||||||
|
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <complexType>
|
||||||
|
* <complexContent>
|
||||||
|
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||||
|
* <sequence>
|
||||||
|
* <element name="Boat" maxOccurs="unbounded">
|
||||||
|
* <complexType>
|
||||||
|
* <complexContent>
|
||||||
|
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||||
|
* <sequence>
|
||||||
|
* <element name="GPSposition">
|
||||||
|
* <complexType>
|
||||||
|
* <complexContent>
|
||||||
|
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||||
|
* <attribute name="X" type="{http://www.w3.org/2001/XMLSchema}double" />
|
||||||
|
* <attribute name="Y" use="required" type="{http://www.w3.org/2001/XMLSchema}double" />
|
||||||
|
* <attribute name="Z" use="required" type="{http://www.w3.org/2001/XMLSchema}double" />
|
||||||
|
* </restriction>
|
||||||
|
* </complexContent>
|
||||||
|
* </complexType>
|
||||||
|
* </element>
|
||||||
|
* </sequence>
|
||||||
|
* <attribute name="Type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* <attribute name="BoatName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* <attribute name="SourceID" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
|
||||||
|
* <attribute name="HullNum" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* <attribute name="ShortName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* <attribute name="ShapeID" type="{http://www.w3.org/2001/XMLSchema}int" />
|
||||||
|
* <attribute name="StoweName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* </restriction>
|
||||||
|
* </complexContent>
|
||||||
|
* </complexType>
|
||||||
|
* </element>
|
||||||
|
* </sequence>
|
||||||
|
* </restriction>
|
||||||
|
* </complexContent>
|
||||||
|
* </complexType>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@XmlType(name = "", propOrder = {
|
||||||
|
"boat"
|
||||||
|
})
|
||||||
|
public static class Boats {
|
||||||
|
|
||||||
|
@XmlElement(name = "Boat", required = true)
|
||||||
|
protected List<BoatConfig.Boats.Boat> boat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the boat property.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This accessor method returns a reference to the live list,
|
||||||
|
* not a snapshot. Therefore any modification you make to the
|
||||||
|
* returned list will be present inside the JAXB object.
|
||||||
|
* This is why there is not a <CODE>set</CODE> method for the boat property.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* For example, to add a new item, do as follows:
|
||||||
|
* <pre>
|
||||||
|
* getBoat().add(newItem);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Objects of the following type(s) are allowed in the list
|
||||||
|
* {@link BoatConfig.Boats.Boat }
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public List<BoatConfig.Boats.Boat> getBoat() {
|
||||||
|
if (boat == null) {
|
||||||
|
boat = new ArrayList<BoatConfig.Boats.Boat>();
|
||||||
|
}
|
||||||
|
return this.boat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Java class for anonymous complex type.
|
||||||
|
*
|
||||||
|
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <complexType>
|
||||||
|
* <complexContent>
|
||||||
|
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||||
|
* <sequence>
|
||||||
|
* <element name="GPSposition">
|
||||||
|
* <complexType>
|
||||||
|
* <complexContent>
|
||||||
|
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||||
|
* <attribute name="X" type="{http://www.w3.org/2001/XMLSchema}double" />
|
||||||
|
* <attribute name="Y" use="required" type="{http://www.w3.org/2001/XMLSchema}double" />
|
||||||
|
* <attribute name="Z" use="required" type="{http://www.w3.org/2001/XMLSchema}double" />
|
||||||
|
* </restriction>
|
||||||
|
* </complexContent>
|
||||||
|
* </complexType>
|
||||||
|
* </element>
|
||||||
|
* </sequence>
|
||||||
|
* <attribute name="Type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* <attribute name="BoatName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* <attribute name="SourceID" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
|
||||||
|
* <attribute name="HullNum" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* <attribute name="ShortName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* <attribute name="ShapeID" type="{http://www.w3.org/2001/XMLSchema}int" />
|
||||||
|
* <attribute name="StoweName" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||||
|
* </restriction>
|
||||||
|
* </complexContent>
|
||||||
|
* </complexType>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@XmlType(name = "", propOrder = {
|
||||||
|
"gpSposition"
|
||||||
|
})
|
||||||
|
public static class Boat {
|
||||||
|
|
||||||
|
@XmlElement(name = "GPSposition", required = true)
|
||||||
|
protected BoatConfig.Boats.Boat.GPSposition gpSposition;
|
||||||
|
@XmlAttribute(name = "Type", required = true)
|
||||||
|
protected String type;
|
||||||
|
@XmlAttribute(name = "BoatName", required = true)
|
||||||
|
protected String boatName;
|
||||||
|
@XmlAttribute(name = "SourceID", required = true)
|
||||||
|
protected int sourceID;
|
||||||
|
@XmlAttribute(name = "HullNum")
|
||||||
|
protected String hullNum;
|
||||||
|
@XmlAttribute(name = "ShortName")
|
||||||
|
protected String shortName;
|
||||||
|
@XmlAttribute(name = "ShapeID")
|
||||||
|
protected Integer shapeID;
|
||||||
|
@XmlAttribute(name = "StoweName")
|
||||||
|
protected String stoweName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the gpSposition property.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* possible object is
|
||||||
|
* {@link BoatConfig.Boats.Boat.GPSposition }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public BoatConfig.Boats.Boat.GPSposition getGPSposition() {
|
||||||
|
return gpSposition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the gpSposition property.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* allowed object is
|
||||||
|
* {@link BoatConfig.Boats.Boat.GPSposition }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setGPSposition(BoatConfig.Boats.Boat.GPSposition value) {
|
||||||
|
this.gpSposition = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the type property.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* possible object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the type property.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* allowed object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setType(String value) {
|
||||||
|
this.type = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the boatName property.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* possible object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public String getBoatName() {
|
||||||
|
return boatName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the boatName property.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* allowed object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setBoatName(String value) {
|
||||||
|
this.boatName = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the sourceID property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public int getSourceID() {
|
||||||
|
return sourceID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the sourceID property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setSourceID(int value) {
|
||||||
|
this.sourceID = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the hullNum property.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* possible object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public String getHullNum() {
|
||||||
|
return hullNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the hullNum property.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* allowed object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setHullNum(String value) {
|
||||||
|
this.hullNum = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the shortName property.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* possible object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public String getShortName() {
|
||||||
|
return shortName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the shortName property.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* allowed object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setShortName(String value) {
|
||||||
|
this.shortName = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the shapeID property.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* possible object is
|
||||||
|
* {@link Integer }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public Integer getShapeID() {
|
||||||
|
return shapeID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the shapeID property.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* allowed object is
|
||||||
|
* {@link Integer }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setShapeID(Integer value) {
|
||||||
|
this.shapeID = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the stoweName property.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* possible object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public String getStoweName() {
|
||||||
|
return stoweName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the stoweName property.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* allowed object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setStoweName(String value) {
|
||||||
|
this.stoweName = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Java class for anonymous complex type.
|
||||||
|
*
|
||||||
|
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <complexType>
|
||||||
|
* <complexContent>
|
||||||
|
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||||
|
* <attribute name="X" type="{http://www.w3.org/2001/XMLSchema}double" />
|
||||||
|
* <attribute name="Y" use="required" type="{http://www.w3.org/2001/XMLSchema}double" />
|
||||||
|
* <attribute name="Z" use="required" type="{http://www.w3.org/2001/XMLSchema}double" />
|
||||||
|
* </restriction>
|
||||||
|
* </complexContent>
|
||||||
|
* </complexType>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@XmlType(name = "")
|
||||||
|
public static class GPSposition {
|
||||||
|
|
||||||
|
@XmlAttribute(name = "X", required = true)
|
||||||
|
protected Double x;
|
||||||
|
@XmlAttribute(name = "Y", required = true)
|
||||||
|
protected double y;
|
||||||
|
@XmlAttribute(name = "Z", required = true)
|
||||||
|
protected double z;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the x property.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* possible object is
|
||||||
|
* {@link Double }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public Double getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the x property.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* allowed object is
|
||||||
|
* {@link Double }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setX(Double value) {
|
||||||
|
this.x = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the y property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public double getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the y property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setY(double value) {
|
||||||
|
this.y = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the z property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public double getZ() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the z property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setZ(double value) {
|
||||||
|
this.z = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package shared.xml.boats;
|
||||||
|
|
||||||
|
import shared.dataInput.BoatDataSource;
|
||||||
|
import shared.dataInput.RaceDataSource;
|
||||||
|
import shared.enums.RoundingType;
|
||||||
|
import shared.model.Boat;
|
||||||
|
import shared.model.CompoundMark;
|
||||||
|
import shared.model.Leg;
|
||||||
|
import shared.model.Mark;
|
||||||
|
import shared.xml.Race.*;
|
||||||
|
import shared.xml.XMLUtilities;
|
||||||
|
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has functions to convert a {@link shared.dataInput.BoatDataSource} to an {@link BoatConfig} object.
|
||||||
|
*/
|
||||||
|
public class BoatDataSourceToXML {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a boat data source to an XMLRace object.
|
||||||
|
* @param boatDataSource The data source to convert.
|
||||||
|
* @return The XMLRace file.
|
||||||
|
*/
|
||||||
|
public static BoatConfig toXML(BoatDataSource boatDataSource) {
|
||||||
|
BoatConfig boatConfig = new BoatConfig();
|
||||||
|
|
||||||
|
boatConfig.boats = new BoatConfig.Boats();
|
||||||
|
boatConfig.boats.boat = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
for (Boat boat : boatDataSource.getBoats().values()) {
|
||||||
|
BoatConfig.Boats.Boat xmlBoat = new BoatConfig.Boats.Boat();
|
||||||
|
|
||||||
|
xmlBoat.setType("Yacht");
|
||||||
|
xmlBoat.setBoatName(boat.getName());
|
||||||
|
xmlBoat.setSourceID(boat.getSourceID());
|
||||||
|
xmlBoat.setStoweName(boat.getCountry());
|
||||||
|
xmlBoat.setShortName(boat.getCountry());
|
||||||
|
|
||||||
|
BoatConfig.Boats.Boat.GPSposition position = new BoatConfig.Boats.Boat.GPSposition();
|
||||||
|
position.setX(boat.getPosition().getLongitude());
|
||||||
|
position.setY(boat.getPosition().getLatitude());
|
||||||
|
position.setZ(0);
|
||||||
|
xmlBoat.setGPSposition(position);
|
||||||
|
|
||||||
|
boatConfig.boats.boat.add(xmlBoat);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (Mark mark : boatDataSource.getMarkerBoats().values()) {
|
||||||
|
BoatConfig.Boats.Boat xmlBoat = new BoatConfig.Boats.Boat();
|
||||||
|
|
||||||
|
xmlBoat.setType("Mark");
|
||||||
|
xmlBoat.setBoatName(mark.getName());
|
||||||
|
xmlBoat.setSourceID(mark.getSourceID());
|
||||||
|
|
||||||
|
BoatConfig.Boats.Boat.GPSposition position = new BoatConfig.Boats.Boat.GPSposition();
|
||||||
|
position.setX(mark.getPosition().getLongitude());
|
||||||
|
position.setY(mark.getPosition().getLatitude());
|
||||||
|
position.setZ(0);
|
||||||
|
xmlBoat.setGPSposition(position);
|
||||||
|
|
||||||
|
boatConfig.boats.boat.add(xmlBoat);
|
||||||
|
}
|
||||||
|
|
||||||
|
return boatConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a boat data source to an xml string.
|
||||||
|
* @param boatDataSource Data source to convert.
|
||||||
|
* @return String containing xml file.
|
||||||
|
* @throws JAXBException Thrown if it cannot be converted.
|
||||||
|
*/
|
||||||
|
public static String toString(BoatDataSource boatDataSource) throws JAXBException {
|
||||||
|
BoatConfig boats = toXML(boatDataSource);
|
||||||
|
return XMLUtilities.classToXML(boats);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
//
|
||||||
|
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802
|
||||||
|
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
|
||||||
|
// Any modifications to this file will be lost upon recompilation of the source schema.
|
||||||
|
// Generated on: 2017.09.01 at 11:12:43 PM NZST
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package shared.xml.boats;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlRegistry;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object contains factory methods for each
|
||||||
|
* Java content interface and Java element interface
|
||||||
|
* generated in the aaa package.
|
||||||
|
* <p>An ObjectFactory allows you to programatically
|
||||||
|
* construct new instances of the Java representation
|
||||||
|
* for XML content. The Java representation of XML
|
||||||
|
* content can consist of schema derived interfaces
|
||||||
|
* and classes representing the binding of schema
|
||||||
|
* type definitions, element declarations and model
|
||||||
|
* groups. Factory methods for each of these are
|
||||||
|
* provided in this class.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@XmlRegistry
|
||||||
|
public class ObjectFactory {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: aaa
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public ObjectFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of {@link BoatConfig }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public BoatConfig createBoatConfig() {
|
||||||
|
return new BoatConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of {@link BoatConfig.Boats }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public BoatConfig.Boats createBoatConfigBoats() {
|
||||||
|
return new BoatConfig.Boats();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of {@link BoatConfig.Boats.Boat }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public BoatConfig.Boats.Boat createBoatConfigBoatsBoat() {
|
||||||
|
return new BoatConfig.Boats.Boat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of {@link BoatConfig.Boats.Boat.GPSposition }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public BoatConfig.Boats.Boat.GPSposition createBoatConfigBoatsBoatGPSposition() {
|
||||||
|
return new BoatConfig.Boats.Boat.GPSposition();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802
|
||||||
|
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
|
||||||
|
// Any modifications to this file will be lost upon recompilation of the source schema.
|
||||||
|
// Generated on: 2017.09.01 at 10:37:23 PM NZST
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package shared.xml.regatta;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlRegistry;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object contains factory methods for each
|
||||||
|
* Java content interface and Java element interface
|
||||||
|
* generated in the aaa package.
|
||||||
|
* <p>An ObjectFactory allows you to programatically
|
||||||
|
* construct new instances of the Java representation
|
||||||
|
* for XML content. The Java representation of XML
|
||||||
|
* content can consist of schema derived interfaces
|
||||||
|
* and classes representing the binding of schema
|
||||||
|
* type definitions, element declarations and model
|
||||||
|
* groups. Factory methods for each of these are
|
||||||
|
* provided in this class.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@XmlRegistry
|
||||||
|
public class ObjectFactory {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: aaa
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public ObjectFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of {@link RegattaConfig }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public RegattaConfig createRegattaConfig() {
|
||||||
|
return new RegattaConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
//
|
||||||
|
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802
|
||||||
|
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
|
||||||
|
// Any modifications to this file will be lost upon recompilation of the source schema.
|
||||||
|
// Generated on: 2017.09.01 at 10:37:23 PM NZST
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package shared.xml.regatta;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
import javax.xml.bind.annotation.XmlType;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Java class for anonymous complex type.
|
||||||
|
*
|
||||||
|
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <complexType>
|
||||||
|
* <complexContent>
|
||||||
|
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||||
|
* <sequence>
|
||||||
|
* <element name="RegattaID" type="{http://www.w3.org/2001/XMLSchema}int"/>
|
||||||
|
* <element name="RegattaName" type="{http://www.w3.org/2001/XMLSchema}string"/>
|
||||||
|
* <element name="CourseName" type="{http://www.w3.org/2001/XMLSchema}string"/>
|
||||||
|
* <element name="CentralLatitude" type="{http://www.w3.org/2001/XMLSchema}double"/>
|
||||||
|
* <element name="CentralLongitude" type="{http://www.w3.org/2001/XMLSchema}double"/>
|
||||||
|
* <element name="CentralAltitude" type="{http://www.w3.org/2001/XMLSchema}double"/>
|
||||||
|
* <element name="UtcOffset" type="{http://www.w3.org/2001/XMLSchema}double"/>
|
||||||
|
* <element name="MagneticVariation" type="{http://www.w3.org/2001/XMLSchema}double"/>
|
||||||
|
* </sequence>
|
||||||
|
* </restriction>
|
||||||
|
* </complexContent>
|
||||||
|
* </complexType>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@XmlType(name = "", propOrder = {
|
||||||
|
"regattaID",
|
||||||
|
"regattaName",
|
||||||
|
"courseName",
|
||||||
|
"centralLatitude",
|
||||||
|
"centralLongitude",
|
||||||
|
"centralAltitude",
|
||||||
|
"utcOffset",
|
||||||
|
"magneticVariation"
|
||||||
|
})
|
||||||
|
@XmlRootElement(name = "RegattaConfig")
|
||||||
|
public class RegattaConfig {
|
||||||
|
|
||||||
|
@XmlElement(name = "RegattaID")
|
||||||
|
protected int regattaID;
|
||||||
|
@XmlElement(name = "RegattaName", required = true)
|
||||||
|
protected String regattaName;
|
||||||
|
@XmlElement(name = "CourseName", required = true)
|
||||||
|
protected String courseName;
|
||||||
|
@XmlElement(name = "CentralLatitude")
|
||||||
|
protected double centralLatitude;
|
||||||
|
@XmlElement(name = "CentralLongitude")
|
||||||
|
protected double centralLongitude;
|
||||||
|
@XmlElement(name = "CentralAltitude")
|
||||||
|
protected double centralAltitude;
|
||||||
|
@XmlElement(name = "UtcOffset")
|
||||||
|
protected double utcOffset;
|
||||||
|
@XmlElement(name = "MagneticVariation")
|
||||||
|
protected double magneticVariation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the regattaID property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public int getRegattaID() {
|
||||||
|
return regattaID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the regattaID property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setRegattaID(int value) {
|
||||||
|
this.regattaID = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the regattaName property.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* possible object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public String getRegattaName() {
|
||||||
|
return regattaName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the regattaName property.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* allowed object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setRegattaName(String value) {
|
||||||
|
this.regattaName = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the courseName property.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* possible object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public String getCourseName() {
|
||||||
|
return courseName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the courseName property.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* allowed object is
|
||||||
|
* {@link String }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setCourseName(String value) {
|
||||||
|
this.courseName = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the centralLatitude property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public double getCentralLatitude() {
|
||||||
|
return centralLatitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the centralLatitude property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setCentralLatitude(double value) {
|
||||||
|
this.centralLatitude = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the centralLongitude property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public double getCentralLongitude() {
|
||||||
|
return centralLongitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the centralLongitude property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setCentralLongitude(double value) {
|
||||||
|
this.centralLongitude = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the centralAltitude property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public double getCentralAltitude() {
|
||||||
|
return centralAltitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the centralAltitude property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setCentralAltitude(double value) {
|
||||||
|
this.centralAltitude = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the utcOffset property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public double getUtcOffset() {
|
||||||
|
return utcOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the utcOffset property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setUtcOffset(double value) {
|
||||||
|
this.utcOffset = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of the magneticVariation property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public double getMagneticVariation() {
|
||||||
|
return magneticVariation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the magneticVariation property.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void setMagneticVariation(double value) {
|
||||||
|
this.magneticVariation = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package shared.xml.regatta;
|
||||||
|
|
||||||
|
import shared.dataInput.RegattaDataSource;
|
||||||
|
import shared.xml.Race.XMLRace;
|
||||||
|
import shared.xml.XMLUtilities;
|
||||||
|
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has functions to convert a {@link shared.dataInput.RegattaDataSource} to an {@link RegattaConfig} object.
|
||||||
|
*/
|
||||||
|
public class RegattaDataSourceToXML {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a regatta data source to an XMLRace object.
|
||||||
|
* @param regattaDataSource The data source to convert.
|
||||||
|
* @return The XMLRace file.
|
||||||
|
*/
|
||||||
|
public static RegattaConfig toXML(RegattaDataSource regattaDataSource) {
|
||||||
|
|
||||||
|
RegattaConfig regatta = new RegattaConfig();
|
||||||
|
|
||||||
|
regatta.setCentralAltitude(regattaDataSource.getCentralAltitude());
|
||||||
|
regatta.setCentralLatitude(regattaDataSource.getCentralLatitude());
|
||||||
|
regatta.setCentralLongitude(regattaDataSource.getCentralLongitude());
|
||||||
|
|
||||||
|
regatta.setCourseName(regattaDataSource.getCourseName());
|
||||||
|
|
||||||
|
regatta.setRegattaName(regattaDataSource.getRegattaName());
|
||||||
|
|
||||||
|
regatta.setMagneticVariation(regattaDataSource.getMagneticVariation());
|
||||||
|
|
||||||
|
regatta.setRegattaID(regattaDataSource.getRegattaID());
|
||||||
|
|
||||||
|
regatta.setUtcOffset(regattaDataSource.getUtcOffset());
|
||||||
|
|
||||||
|
return regatta;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a regatta data source to an xml string.
|
||||||
|
* @param regattaDataSource Data source to convert.
|
||||||
|
* @return String containing xml file.
|
||||||
|
* @throws JAXBException Thrown if it cannot be converted.
|
||||||
|
*/
|
||||||
|
public static String toString(RegattaDataSource regattaDataSource) throws JAXBException {
|
||||||
|
RegattaConfig regatta = toXML(regattaDataSource);
|
||||||
|
return XMLUtilities.classToXML(regatta);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -71,8 +71,10 @@ public class FinishController extends Controller {
|
|||||||
|
|
||||||
|
|
||||||
//Winner label.
|
//Winner label.
|
||||||
raceWinnerLabel.setText("Winner: "+ boatNameColumn.getCellObservableValue(0).getValue());
|
if (boats.size() > 0) {
|
||||||
|
raceWinnerLabel.setText("Winner: " + boatNameColumn.getCellObservableValue(0).getValue());
|
||||||
raceWinnerLabel.setWrapText(true);
|
raceWinnerLabel.setWrapText(true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
package visualiser.Controllers;
|
package visualiser.Controllers;
|
||||||
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.stage.Stage;
|
||||||
import javafx.scene.media.AudioClip;
|
import javafx.scene.media.AudioClip;
|
||||||
import mock.app.Event;
|
import mock.app.Event;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
@@ -17,6 +22,8 @@ import javax.xml.parsers.ParserConfigurationException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -36,11 +43,30 @@ public class HostController extends Controller {
|
|||||||
@FXML
|
@FXML
|
||||||
AnchorPane hostWrapper;
|
AnchorPane hostWrapper;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
Button previousButton;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
Button nextButton;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
ImageView mapImage;
|
||||||
|
|
||||||
private Event game;
|
private Event game;
|
||||||
|
|
||||||
|
private ArrayList<Image> listOfMaps;
|
||||||
|
private int currentMapIndex = 0;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +75,7 @@ public class HostController extends Controller {
|
|||||||
*/
|
*/
|
||||||
public void hostGamePressed() throws IOException{
|
public void hostGamePressed() throws IOException{
|
||||||
try {
|
try {
|
||||||
this.game = new Event(false);
|
this.game = new Event(false, currentMapIndex);
|
||||||
connectSocket("localhost", 4942);
|
connectSocket("localhost", 4942);
|
||||||
} catch (EventConstructionException e) {
|
} catch (EventConstructionException e) {
|
||||||
Logger.getGlobal().log(Level.SEVERE, "Could not create Event.", e);
|
Logger.getGlobal().log(Level.SEVERE, "Could not create Event.", e);
|
||||||
@@ -82,9 +108,13 @@ public class HostController extends Controller {
|
|||||||
* Hosts a game.
|
* Hosts a game.
|
||||||
*/
|
*/
|
||||||
public void hostGame(){
|
public void hostGame(){
|
||||||
|
mapImage.fitWidthProperty().bind(((Stage) mapImage.getScene().getWindow()).widthProperty().multiply(0.6));
|
||||||
hostWrapper.setVisible(true);
|
hostWrapper.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu button pressed. Prompt alert then return to menu
|
||||||
|
*/
|
||||||
public void menuBtnPressed(){
|
public void menuBtnPressed(){
|
||||||
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
||||||
sound.play();
|
sound.play();
|
||||||
@@ -92,4 +122,27 @@ public class HostController extends Controller {
|
|||||||
parent.enterTitle();
|
parent.enterTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void nextImage(){
|
||||||
|
increaseIndex();
|
||||||
|
mapImage.setImage(listOfMaps.get(currentMapIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
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; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,247 @@
|
|||||||
|
package visualiser.Controllers;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyEvent;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import javafx.stage.WindowEvent;
|
||||||
|
import visualiser.gameController.Keys.ControlKey;
|
||||||
|
import visualiser.gameController.Keys.KeyFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static visualiser.app.App.keyFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for the scene used to display and update current key bindings.
|
||||||
|
*/
|
||||||
|
public class KeyBindingsController {
|
||||||
|
private @FXML Button btnSave;
|
||||||
|
private @FXML Button btnCancel;
|
||||||
|
private @FXML Button btnReset;
|
||||||
|
private @FXML ListView lstControl;
|
||||||
|
private @FXML ListView lstKey;
|
||||||
|
private @FXML ListView lstDescription;
|
||||||
|
private @FXML AnchorPane anchor;
|
||||||
|
private KeyFactory newKeyFactory;
|
||||||
|
private Boolean changed = false; // keyBindings have been modified
|
||||||
|
private Button currentButton = null; // last button clicked
|
||||||
|
|
||||||
|
public void initialize(){
|
||||||
|
// create new key factory to modify, keeping the existing one safe
|
||||||
|
newKeyFactory = copyExistingFactory();
|
||||||
|
initializeTable();
|
||||||
|
populateTable();
|
||||||
|
setKeyListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up table before populating it.
|
||||||
|
* Set up includes headings, CSS styling and modifying default properties.
|
||||||
|
*/
|
||||||
|
public void initializeTable(){
|
||||||
|
// set the headings for each column
|
||||||
|
lstKey.getItems().add("Key");
|
||||||
|
lstControl.getItems().add("Command");
|
||||||
|
lstDescription.getItems().add("Description");
|
||||||
|
lstKey.getSelectionModel().select(0);
|
||||||
|
lstControl.getSelectionModel().select(0);
|
||||||
|
lstDescription.getSelectionModel().select(0);
|
||||||
|
|
||||||
|
// add CSS stylesheet once the scene has been created
|
||||||
|
lstKey.sceneProperty().addListener((obs, oldScene, newScene) -> {
|
||||||
|
if (newScene != null) {
|
||||||
|
newScene.getStylesheets().add("/css/keyBindings.css");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// stop the columns from being selectable, so only the buttons are
|
||||||
|
lstKey.getSelectionModel().selectedItemProperty()
|
||||||
|
.addListener((observable, oldvalue, newValue) ->
|
||||||
|
Platform.runLater(() ->
|
||||||
|
lstKey.getSelectionModel().select(0)));
|
||||||
|
lstDescription.getSelectionModel().selectedItemProperty()
|
||||||
|
.addListener((observable, oldvalue, newValue) ->
|
||||||
|
Platform.runLater(() ->
|
||||||
|
lstDescription.getSelectionModel().select(0)));
|
||||||
|
lstControl.getSelectionModel().selectedItemProperty()
|
||||||
|
.addListener((observable, oldvalue, newValue) ->
|
||||||
|
Platform.runLater(() ->
|
||||||
|
lstControl.getSelectionModel().select(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates the table with commands and their key binding details.
|
||||||
|
*/
|
||||||
|
public void populateTable(){
|
||||||
|
// add each command to the table
|
||||||
|
for (Map.Entry<String, ControlKey> entry : newKeyFactory.getKeyState().entrySet()) {
|
||||||
|
// create button for command
|
||||||
|
Button button = new Button(entry.getKey());
|
||||||
|
button.setMinWidth(120);
|
||||||
|
button.setId(entry.getValue().toString());
|
||||||
|
button.setOnAction(e -> currentButton = button);
|
||||||
|
// display details for command in table
|
||||||
|
lstControl.getItems().add(entry.getValue());
|
||||||
|
lstKey.getItems().add(button);
|
||||||
|
lstDescription.getItems().add(entry.getValue().getProtocolCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a copy of the {@link KeyFactory} that does not modify the original.
|
||||||
|
* @return new keyfactory to be modified
|
||||||
|
*/
|
||||||
|
public KeyFactory copyExistingFactory(){
|
||||||
|
newKeyFactory = new KeyFactory();
|
||||||
|
Map<String, ControlKey> oldKeyState = keyFactory.getKeyState();
|
||||||
|
Map<String, ControlKey> newKeyState = new HashMap<>();
|
||||||
|
|
||||||
|
// copy over commands and their keys
|
||||||
|
for (Map.Entry<String, ControlKey> entry : oldKeyState.entrySet()){
|
||||||
|
newKeyState.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
newKeyFactory.setKeyState(newKeyState);
|
||||||
|
return newKeyFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a listener for the base anchorpane for key presses.
|
||||||
|
* It updates the current key bindings of the {@link KeyFactory} if
|
||||||
|
* required.
|
||||||
|
*/
|
||||||
|
public void setKeyListener(){
|
||||||
|
anchor.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
|
||||||
|
// if esc, cancel current button click
|
||||||
|
if (event.getCode() == KeyCode.ESCAPE){
|
||||||
|
btnCancel.requestFocus();
|
||||||
|
currentButton = null;
|
||||||
|
}
|
||||||
|
// if a button was clicked
|
||||||
|
else if (currentButton != null) {
|
||||||
|
// check if a button is already mapped to this key
|
||||||
|
for (int i = 1; i < lstKey.getItems().size(); i++) {
|
||||||
|
Button button = (Button)lstKey.getItems().get(i);
|
||||||
|
// update buttons text and remove key binding from command
|
||||||
|
if (button.getText().equals(event.getCode().toString())) {
|
||||||
|
button.setText("");
|
||||||
|
newKeyFactory.updateKey(button.getId(), button.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update text on the button
|
||||||
|
currentButton.setText(event.getCode().toString());
|
||||||
|
// update the control key
|
||||||
|
newKeyFactory.updateKey(event.getCode().toString(),
|
||||||
|
currentButton.getId());
|
||||||
|
// remove current button selection
|
||||||
|
currentButton = null;
|
||||||
|
changed = true;
|
||||||
|
btnCancel.requestFocus();
|
||||||
|
}
|
||||||
|
event.consume();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel and exits the key bindings menu. Changes are not forced to be
|
||||||
|
* saved or fixed if invalid, and instead are defaulted back to the last
|
||||||
|
* successful saved state.
|
||||||
|
*/
|
||||||
|
public void cancel(){
|
||||||
|
((Stage)btnCancel.getScene().getWindow()).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets all key bindings to the built-in defaults.
|
||||||
|
*/
|
||||||
|
public void reset(){
|
||||||
|
lstKey.getItems().clear();
|
||||||
|
lstControl.getItems().clear();
|
||||||
|
lstDescription.getItems().clear();
|
||||||
|
newKeyFactory = new KeyFactory();
|
||||||
|
initializeTable();
|
||||||
|
populateTable();
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace existing {@link KeyFactory} with the modified key bindings.
|
||||||
|
*/
|
||||||
|
public void save(){
|
||||||
|
if (isFactoryValid()) {
|
||||||
|
keyFactory = newKeyFactory;
|
||||||
|
newKeyFactory = new KeyFactory();
|
||||||
|
changed = false;
|
||||||
|
keyFactory.save(); // save persistently
|
||||||
|
loadNotification("Key bindings were successfully saved.", false);
|
||||||
|
} else {
|
||||||
|
loadNotification("One or more key bindings are missing. " +
|
||||||
|
"Failed to save.", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the {@link KeyFactory} being modified is valid and that no
|
||||||
|
* commands are missing a key binding.
|
||||||
|
* @return True if valid, false if invalid
|
||||||
|
*/
|
||||||
|
public Boolean isFactoryValid(){
|
||||||
|
for (Map.Entry<String, ControlKey> entry : newKeyFactory.getKeyState().entrySet
|
||||||
|
()) {
|
||||||
|
if (entry.getKey().toString()==entry.getValue().toString()){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method used to stop a user from exiting key bindings without saving
|
||||||
|
* their changes to the {@link KeyFactory}.
|
||||||
|
* @param we {@link WindowEvent} close request to be consumed if settings
|
||||||
|
* have not been successfully saved.
|
||||||
|
*/
|
||||||
|
public void onExit(WindowEvent we){
|
||||||
|
// if modified KeyFactory hasn't been saved
|
||||||
|
if (changed){
|
||||||
|
loadNotification("Please cancel or save your changes before exiting" +
|
||||||
|
".", true);
|
||||||
|
we.consume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a popup window giving confirmation/warning of user activity.
|
||||||
|
* @param message the message to be displayed to the user
|
||||||
|
* @param warning true if the message to be displayed is due to user error
|
||||||
|
*/
|
||||||
|
public void loadNotification(String message, Boolean warning){
|
||||||
|
Parent root = null;
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource
|
||||||
|
("/visualiser/scenes/notification.fxml"));
|
||||||
|
try {
|
||||||
|
root = loader.load();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
NotificationController controller = loader.getController();
|
||||||
|
Stage stage = new Stage();
|
||||||
|
stage.setScene(new Scene(root));
|
||||||
|
stage.centerOnScreen();
|
||||||
|
stage.initModality(Modality.APPLICATION_MODAL);
|
||||||
|
stage.show();
|
||||||
|
// displays given message in the window
|
||||||
|
controller.setMessage(message, warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ import java.util.ResourceBundle;
|
|||||||
public class LobbyController extends Controller {
|
public class LobbyController extends Controller {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
AnchorPane lobbyWrapper;
|
private AnchorPane lobbyWrapper;
|
||||||
@FXML
|
@FXML
|
||||||
private TableView<RaceConnection> lobbyTable;
|
private TableView<RaceConnection> lobbyTable;
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public class MainController extends Controller {
|
|||||||
* Transitions from the StartController screen (displays pre-race information) to the RaceController (displays the actual race).
|
* Transitions from the StartController screen (displays pre-race information) to the RaceController (displays the actual race).
|
||||||
* @param visualiserRace The object modelling the race.
|
* @param visualiserRace The object modelling the race.
|
||||||
* @param controllerClient Socket Client that manipulates the controller.
|
* @param controllerClient Socket Client that manipulates the controller.
|
||||||
|
* @param isHost if the client is the host of a race or not.
|
||||||
*/
|
*/
|
||||||
public void beginRace(VisualiserRaceEvent visualiserRace, ControllerClient controllerClient, Boolean isHost) {
|
public void beginRace(VisualiserRaceEvent visualiserRace, ControllerClient controllerClient, Boolean isHost) {
|
||||||
raceController.startRace(visualiserRace, controllerClient, isHost);
|
raceController.startRace(visualiserRace, controllerClient, isHost);
|
||||||
@@ -97,6 +98,20 @@ public class MainController extends Controller {
|
|||||||
*/
|
*/
|
||||||
public void startCss(){titleController.setDayMode();}
|
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.
|
* Main Controller for the applications will house the menu and the displayed pane.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package visualiser.Controllers;
|
||||||
|
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for a popup notification regarding user activity.
|
||||||
|
*/
|
||||||
|
public class NotificationController {
|
||||||
|
private @FXML Label lblDescription;
|
||||||
|
private @FXML Text txtMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the popup window once clicked.
|
||||||
|
*/
|
||||||
|
public void ok(){
|
||||||
|
((Stage)lblDescription.getScene().getWindow()).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the appropriate popup notification.
|
||||||
|
* @param message message for the user
|
||||||
|
* @param warning if true warning text shown, if false success text shown
|
||||||
|
*/
|
||||||
|
public void setMessage(String message, Boolean warning){
|
||||||
|
lblDescription.setText(message);
|
||||||
|
if (!warning){
|
||||||
|
txtMessage.setText("Success!");
|
||||||
|
txtMessage.setFill(Color.GREEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package visualiser.Controllers;
|
package visualiser.Controllers;
|
||||||
|
|
||||||
|
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
|
||||||
import javafx.animation.AnimationTimer;
|
import javafx.animation.AnimationTimer;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
@@ -10,82 +10,81 @@ import javafx.collections.transformation.SortedList;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.chart.LineChart;
|
import javafx.scene.chart.LineChart;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.Pane;
|
|
||||||
import javafx.scene.layout.StackPane;
|
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 javafx.util.Callback;
|
||||||
import network.Messages.Enums.RaceStatusEnum;
|
import network.Messages.Enums.RaceStatusEnum;
|
||||||
|
import shared.dataInput.RaceDataSource;
|
||||||
import shared.model.Leg;
|
import shared.model.Leg;
|
||||||
|
import shared.model.Mark;
|
||||||
import visualiser.app.App;
|
import visualiser.app.App;
|
||||||
|
import visualiser.enums.TutorialState;
|
||||||
import visualiser.gameController.ControllerClient;
|
import visualiser.gameController.ControllerClient;
|
||||||
import visualiser.gameController.Keys.ControlKey;
|
import visualiser.gameController.Keys.ControlKey;
|
||||||
import visualiser.gameController.Keys.KeyFactory;
|
import visualiser.layout.Subject3D;
|
||||||
|
import visualiser.layout.View3D;
|
||||||
import visualiser.model.*;
|
import visualiser.model.*;
|
||||||
|
import visualiser.utils.GPSConverter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Optional;
|
import java.util.*;
|
||||||
import java.util.ResourceBundle;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static visualiser.app.App.keyFactory;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller used to display a running race.
|
* Controller used to display a running race.
|
||||||
*/
|
*/
|
||||||
public class RaceController extends Controller {
|
public class RaceController extends Controller {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The race object which describes the currently occurring race.
|
* The race object which describes the currently occurring race.
|
||||||
*/
|
*/
|
||||||
private VisualiserRaceEvent visualiserRace;
|
private VisualiserRaceEvent visualiserRace;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for sending keystrokes to server
|
* Service for sending keystrokes to server
|
||||||
*/
|
*/
|
||||||
private ControllerClient controllerClient;
|
private ControllerClient controllerClient;
|
||||||
|
|
||||||
|
|
||||||
private boolean isHost;
|
private boolean isHost;
|
||||||
|
|
||||||
/**
|
private TutorialState currentState;
|
||||||
* The canvas that draws the race.
|
|
||||||
*/
|
private ArrayList<TutorialState> tutorialStates;
|
||||||
private ResizableRaceCanvas raceCanvas;
|
|
||||||
|
private boolean isTutorial = false;
|
||||||
|
|
||||||
|
private String keyToPress;
|
||||||
|
|
||||||
/**
|
|
||||||
* The sparkline graph.
|
|
||||||
*/
|
|
||||||
private Sparkline sparkline;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* state of the info table
|
* state of the info table
|
||||||
*/
|
*/
|
||||||
private boolean infoTableShow;
|
private boolean infoTableShow;
|
||||||
|
|
||||||
|
private View3D view3D;
|
||||||
|
private ObservableList<Subject3D> viewSubjects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The arrow controller.
|
* The arrow controller.
|
||||||
*/
|
*/
|
||||||
@FXML private ArrowController arrowController;
|
@FXML private ArrowController arrowController;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@FXML private GridPane canvasBase;
|
@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.
|
* This is the pane we place the actual arrow control inside of.
|
||||||
@@ -101,9 +100,6 @@ public class RaceController extends Controller {
|
|||||||
@FXML private TableColumn<VisualiserBoat, Leg> boatMarkColumn;
|
@FXML private TableColumn<VisualiserBoat, Leg> boatMarkColumn;
|
||||||
@FXML private TableColumn<VisualiserBoat, Number> boatSpeedColumn;
|
@FXML private TableColumn<VisualiserBoat, Number> boatSpeedColumn;
|
||||||
@FXML private LineChart<Number, Number> sparklineChart;
|
@FXML private LineChart<Number, Number> sparklineChart;
|
||||||
@FXML private AnchorPane annotationPane;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ctor.
|
* Ctor.
|
||||||
@@ -113,11 +109,10 @@ public class RaceController extends Controller {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
KeyFactory keyFactory = KeyFactory.getFactory();
|
|
||||||
infoTableShow = true;
|
infoTableShow = true;
|
||||||
|
|
||||||
// Initialise keyboard handler
|
// Initialise keyboard handler
|
||||||
race.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
|
racePane.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
|
||||||
String codeString = event.getCode().toString();
|
String codeString = event.getCode().toString();
|
||||||
|
|
||||||
if (codeString.equals("TAB")){toggleTable();}
|
if (codeString.equals("TAB")){toggleTable();}
|
||||||
@@ -126,13 +121,27 @@ public class RaceController extends Controller {
|
|||||||
if(controlKey != null) {
|
if(controlKey != null) {
|
||||||
try {
|
try {
|
||||||
controlKey.onAction(); // Change key state if applicable
|
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);
|
controllerClient.sendKey(controlKey);
|
||||||
event.consume();
|
event.consume();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
Logger.getGlobal().log(Level.WARNING, "RaceController was interrupted on thread: " + Thread.currentThread() + "while sending: " + controlKey, e);
|
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) {
|
if(event.getCode() == KeyCode.ESCAPE) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -143,7 +152,7 @@ public class RaceController extends Controller {
|
|||||||
Optional<ButtonType> result = alert.showAndWait();
|
Optional<ButtonType> result = alert.showAndWait();
|
||||||
if (result.get() == ButtonType.OK) {
|
if (result.get() == ButtonType.OK) {
|
||||||
parent.endEvent();
|
parent.endEvent();
|
||||||
race.setVisible(false);
|
racePane.setVisible(false);
|
||||||
App.app.showMainStage(App.getStage());
|
App.app.showMainStage(App.getStage());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -152,7 +161,7 @@ public class RaceController extends Controller {
|
|||||||
alert.setContentText("Do you wish to quit the race?");
|
alert.setContentText("Do you wish to quit the race?");
|
||||||
Optional<ButtonType> result = alert.showAndWait();
|
Optional<ButtonType> result = alert.showAndWait();
|
||||||
if (result.get() == ButtonType.OK) {
|
if (result.get() == ButtonType.OK) {
|
||||||
race.setVisible(false);
|
racePane.setVisible(false);
|
||||||
App.app.showMainStage(App.getStage());
|
App.app.showMainStage(App.getStage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,14 +185,10 @@ public class RaceController extends Controller {
|
|||||||
//Information table.
|
//Information table.
|
||||||
initialiseInfoTable(this.visualiserRace);
|
initialiseInfoTable(this.visualiserRace);
|
||||||
|
|
||||||
//Sparkline.
|
|
||||||
initialiseSparkline(this.visualiserRace);
|
|
||||||
|
|
||||||
//Arrow.
|
//Arrow.
|
||||||
initialiseArrow(this.visualiserRace);
|
initialiseArrow(this.visualiserRace);
|
||||||
|
|
||||||
//Race canvas.
|
initialiseView3D(this.visualiserRace);
|
||||||
initialiseRaceCanvas(this.visualiserRace);
|
|
||||||
|
|
||||||
//Race timezone label.
|
//Race timezone label.
|
||||||
initialiseRaceTimezoneLabel(this.visualiserRace);
|
initialiseRaceTimezoneLabel(this.visualiserRace);
|
||||||
@@ -191,11 +196,106 @@ public class RaceController extends Controller {
|
|||||||
//Race clock.
|
//Race clock.
|
||||||
initialiseRaceClock(this.visualiserRace);
|
initialiseRaceClock(this.visualiserRace);
|
||||||
|
|
||||||
|
|
||||||
//Start the race animation timer.
|
//Start the race animation timer.
|
||||||
raceTimer();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -338,42 +438,8 @@ public class RaceController extends Controller {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialises the {@link Sparkline}, and listens to a specified {@link VisualiserRaceEvent}.
|
* Initialises the race time zone label with the race's time zone.
|
||||||
* @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);
|
|
||||||
|
|
||||||
//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.
|
|
||||||
* @param race The race to get time zone from.
|
* @param race The race to get time zone from.
|
||||||
*/
|
*/
|
||||||
private void initialiseRaceTimezoneLabel(VisualiserRaceEvent race) {
|
private void initialiseRaceTimezoneLabel(VisualiserRaceEvent race) {
|
||||||
@@ -408,14 +474,29 @@ public class RaceController extends Controller {
|
|||||||
this.controllerClient = controllerClient;
|
this.controllerClient = controllerClient;
|
||||||
this.isHost = isHost;
|
this.isHost = isHost;
|
||||||
|
|
||||||
|
|
||||||
|
//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");
|
||||||
|
|
||||||
|
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();
|
initialiseRace();
|
||||||
|
|
||||||
//Display this controller.
|
//Display this controller.
|
||||||
race.setVisible(true);
|
racePane.setVisible(true);
|
||||||
|
|
||||||
|
|
||||||
// set up annotation displays
|
|
||||||
new Annotations(annotationPane, raceCanvas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -423,7 +504,7 @@ public class RaceController extends Controller {
|
|||||||
* @param boats boats there are in the race.
|
* @param boats boats there are in the race.
|
||||||
*/
|
*/
|
||||||
public void finishRace(ObservableList<VisualiserBoat> boats) {
|
public void finishRace(ObservableList<VisualiserBoat> boats) {
|
||||||
race.setVisible(false);
|
racePane.setVisible(false);
|
||||||
parent.enterFinish(boats);
|
parent.enterFinish(boats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,18 +539,13 @@ public class RaceController extends Controller {
|
|||||||
finishRace(visualiserRace.getVisualiserRaceState().getBoats());
|
finishRace(visualiserRace.getVisualiserRaceState().getBoats());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
//Otherwise, render the canvas.
|
|
||||||
raceCanvas.drawRace();
|
|
||||||
|
|
||||||
|
|
||||||
//Sort the tableview. Doesn't automatically work for all columns.
|
//Sort the tableview. Doesn't automatically work for all columns.
|
||||||
boatInfoTable.sort();
|
boatInfoTable.sort();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Return to main screen if we lose connection.
|
//Return to main screen if we lose connection.
|
||||||
if (!visualiserRace.getServerConnection().isAlive()) {
|
if (!visualiserRace.getServerConnection().isAlive()) {
|
||||||
race.setVisible(false);
|
racePane.setVisible(false);
|
||||||
//parent.enterTitle();
|
//parent.enterTitle();
|
||||||
try {
|
try {
|
||||||
App.app.showMainStage(App.getStage());
|
App.app.showMainStage(App.getStage());
|
||||||
@@ -491,10 +567,10 @@ public class RaceController extends Controller {
|
|||||||
* toggles if the info table is shown
|
* toggles if the info table is shown
|
||||||
*/
|
*/
|
||||||
private void toggleTable() {
|
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){
|
if (infoTableShow){
|
||||||
race.setDividerPositions(tablePercent);
|
racePane.setDividerPositions(tablePercent);
|
||||||
|
|
||||||
arrowPane.setScaleX(0.5);
|
arrowPane.setScaleX(0.5);
|
||||||
arrowPane.setScaleY(0.5);
|
arrowPane.setScaleY(0.5);
|
||||||
@@ -502,7 +578,7 @@ public class RaceController extends Controller {
|
|||||||
arrowPane.setTranslateY(0 - arrowPane.getScene().getHeight()/4);
|
arrowPane.setTranslateY(0 - arrowPane.getScene().getHeight()/4);
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
race.setDividerPositions(1);
|
racePane.setDividerPositions(1);
|
||||||
|
|
||||||
arrowPane.setScaleX(1);
|
arrowPane.setScaleX(1);
|
||||||
arrowPane.setScaleY(1);
|
arrowPane.setScaleY(1);
|
||||||
@@ -514,4 +590,108 @@ public class RaceController extends Controller {
|
|||||||
infoTableShow = !infoTableShow;
|
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.
|
* Ctor.
|
||||||
*/
|
*/
|
||||||
@@ -225,12 +222,10 @@ public class StartController extends Controller {
|
|||||||
//Get the current race status.
|
//Get the current race status.
|
||||||
RaceStatusEnum raceStatus = visualiserRaceEvent.getVisualiserRaceState().getRaceStatusEnum();
|
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 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 this timer.
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
@@ -243,6 +238,7 @@ public class StartController extends Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.start();
|
}.start();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
package visualiser.Controllers;
|
package visualiser.Controllers;
|
||||||
|
|
||||||
|
import javafx.event.EventHandler;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.RadioButton;
|
import javafx.scene.control.RadioButton;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.media.AudioClip;
|
import javafx.scene.media.AudioClip;
|
||||||
import javafx.scene.media.Media;
|
import javafx.scene.media.Media;
|
||||||
import javafx.scene.media.MediaPlayer;
|
import javafx.scene.media.MediaPlayer;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import mock.app.Event;
|
||||||
|
import mock.exceptions.EventConstructionException;
|
||||||
|
import javafx.stage.WindowEvent;
|
||||||
import visualiser.app.App;
|
import visualiser.app.App;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -19,6 +27,8 @@ import java.io.IOException;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for the opening title window.
|
* Controller for the opening title window.
|
||||||
@@ -36,6 +46,12 @@ public class TitleController extends Controller {
|
|||||||
RadioButton dayModeRD;
|
RadioButton dayModeRD;
|
||||||
@FXML
|
@FXML
|
||||||
RadioButton nightModeRD;
|
RadioButton nightModeRD;
|
||||||
|
@FXML
|
||||||
|
Label tutorialLabel;
|
||||||
|
@FXML
|
||||||
|
Pane menuPane;
|
||||||
|
@FXML
|
||||||
|
ImageView imgSun;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method called when the 'host a game' button is pressed.
|
* Method called when the 'host a game' button is pressed.
|
||||||
@@ -44,8 +60,8 @@ public class TitleController extends Controller {
|
|||||||
* @throws IOException if main has problems
|
* @throws IOException if main has problems
|
||||||
*/
|
*/
|
||||||
public void hostAGame() throws IOException, URISyntaxException {
|
public void hostAGame() throws IOException, URISyntaxException {
|
||||||
|
|
||||||
titleWrapper.setVisible(false);
|
titleWrapper.setVisible(false);
|
||||||
|
parent.setGameType(0);
|
||||||
parent.hostGame();
|
parent.hostGame();
|
||||||
App.getStage().setResizable(true);
|
App.getStage().setResizable(true);
|
||||||
}
|
}
|
||||||
@@ -72,7 +88,10 @@ public class TitleController extends Controller {
|
|||||||
*/
|
*/
|
||||||
public void setDayMode(){
|
public void setDayMode(){
|
||||||
dayModeRD.getScene().getStylesheets().clear();
|
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");
|
dayModeRD.getScene().getStylesheets().add("/css/dayMode.css");
|
||||||
|
menuPane.setStyle("-fx-background-color: #6be6ff;");
|
||||||
nightModeRD.setSelected(false);
|
nightModeRD.setSelected(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,12 +100,16 @@ public class TitleController extends Controller {
|
|||||||
*/
|
*/
|
||||||
public void setNightMode(){
|
public void setNightMode(){
|
||||||
nightModeRD.getScene().getStylesheets().clear();
|
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");
|
nightModeRD.getScene().getStylesheets().add("/css/nightMode.css");
|
||||||
|
menuPane.setStyle("-fx-background-color: #1f2c60;");
|
||||||
dayModeRD.setSelected(false);
|
dayModeRD.setSelected(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
tutorialLabel.setWrapText(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,23 +117,39 @@ public class TitleController extends Controller {
|
|||||||
* Called when control button is pressed. New pop up window displaying controls
|
* Called when control button is pressed. New pop up window displaying controls
|
||||||
*/
|
*/
|
||||||
public void controlBtnPressed(){
|
public void controlBtnPressed(){
|
||||||
|
try {
|
||||||
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
|
||||||
sound.play();
|
sound.play();
|
||||||
FXMLLoader loader = new FXMLLoader();
|
FXMLLoader loader = new FXMLLoader();
|
||||||
loader.setLocation(getClass().getResource("/visualiser/scenes/controls.fxml"));
|
loader.setLocation(getClass().getResource("/visualiser/scenes/keyBindings.fxml"));
|
||||||
Parent layout;
|
Parent layout = loader.load();
|
||||||
try {
|
|
||||||
layout = loader.load();
|
|
||||||
Scene scene = new Scene(layout);
|
Scene scene = new Scene(layout);
|
||||||
Stage popupStage = new Stage();
|
Stage popupStage = new Stage();
|
||||||
popupStage.setResizable(false);
|
popupStage.setResizable(false);
|
||||||
popupStage.setTitle("Game Controls");
|
popupStage.setTitle("Game Controls");
|
||||||
popupStage.initModality(Modality.WINDOW_MODAL);
|
popupStage.initModality(Modality.WINDOW_MODAL);
|
||||||
|
popupStage.centerOnScreen();
|
||||||
popupStage.setScene(scene);
|
popupStage.setScene(scene);
|
||||||
popupStage.showAndWait();
|
popupStage.show();
|
||||||
|
KeyBindingsController controller = loader.getController();
|
||||||
|
popupStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
|
||||||
|
public void handle(WindowEvent we) {
|
||||||
|
if (we.getEventType() == WindowEvent.WINDOW_CLOSE_REQUEST) {
|
||||||
|
controller.onExit(we);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void tutorialStartPressed() throws IOException {
|
||||||
|
titleWrapper.setVisible(false);
|
||||||
|
parent.setGameType(4);
|
||||||
|
parent.beginGame();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,19 +29,17 @@ import javafx.stage.StageStyle;
|
|||||||
import javafx.stage.WindowEvent;
|
import javafx.stage.WindowEvent;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import visualiser.Controllers.MainController;
|
import visualiser.Controllers.MainController;
|
||||||
|
import visualiser.gameController.Keys.KeyFactory;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class App extends Application {
|
public class App extends Application {
|
||||||
|
|
||||||
|
|
||||||
private static Stage stage;
|
private static Stage stage;
|
||||||
private Pane splashLayout;
|
private Pane splashLayout;
|
||||||
private ProgressBar loadProgress;
|
private ProgressBar loadProgress;
|
||||||
private Label progressText;
|
private Label progressText;
|
||||||
private static final int SPLASH_WIDTH = 676;
|
private static final int SPLASH_WIDTH = 676;
|
||||||
private static final int SPLASH_HEIGHT = 227;
|
private static final int SPLASH_HEIGHT = 227;
|
||||||
|
public static KeyFactory keyFactory = new KeyFactory();
|
||||||
public static App app;
|
public static App app;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,6 +53,9 @@ public class App extends Application {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
|
// load the user's personalised key bindings
|
||||||
|
keyFactory.load();
|
||||||
|
|
||||||
ImageView splash = new ImageView(new Image(
|
ImageView splash = new ImageView(new Image(
|
||||||
getClass().getClassLoader().getResourceAsStream("images/splashScreen.png")
|
getClass().getClassLoader().getResourceAsStream("images/splashScreen.png")
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,10 +3,11 @@ package visualiser.gameController;
|
|||||||
import javafx.animation.AnimationTimer;
|
import javafx.animation.AnimationTimer;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import visualiser.gameController.Keys.ControlKey;
|
import visualiser.gameController.Keys.ControlKey;
|
||||||
import visualiser.gameController.Keys.KeyFactory;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import static visualiser.app.App.keyFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for checking what keys are currently being used
|
* Class for checking what keys are currently being used
|
||||||
*/
|
*/
|
||||||
@@ -18,7 +19,7 @@ public class InputChecker {
|
|||||||
* @param scene Scene the controller is to run in parallel with.
|
* @param scene Scene the controller is to run in parallel with.
|
||||||
*/
|
*/
|
||||||
public void runWithScene(Scene scene){
|
public void runWithScene(Scene scene){
|
||||||
KeyFactory keyFactory = KeyFactory.getFactory();
|
// KeyFactory keyFactory = KeyFactory.getFactory();
|
||||||
|
|
||||||
scene.setOnKeyPressed(event -> {
|
scene.setOnKeyPressed(event -> {
|
||||||
String codeString = event.getCode().toString();
|
String codeString = event.getCode().toString();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package visualiser.gameController.Keys;
|
package visualiser.gameController.Keys;
|
||||||
|
|
||||||
import javafx.scene.input.KeyCode;
|
|
||||||
import network.Messages.Enums.BoatActionEnum;
|
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.
|
* 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
|
* What to do when the key is held
|
||||||
|
|||||||
@@ -9,11 +9,9 @@ public class DownWindKey extends ControlKey {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for Control
|
* Constructor for Control
|
||||||
* @param name name of the key
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public DownWindKey(String name) {
|
public DownWindKey() {
|
||||||
super(name, BoatActionEnum.DOWNWIND);
|
super("Downwind", BoatActionEnum.DOWNWIND);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package visualiser.gameController.Keys;
|
package visualiser.gameController.Keys;
|
||||||
|
|
||||||
|
import java.beans.XMLDecoder;
|
||||||
|
import java.beans.XMLEncoder;
|
||||||
|
import java.io.*;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -13,30 +16,17 @@ public class KeyFactory {
|
|||||||
private Map<String, ControlKey> keyState;
|
private Map<String, ControlKey> keyState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton instance to enforce consistent key state
|
* Constructor for key state, set up initial state of each action.
|
||||||
*/
|
*/
|
||||||
private static KeyFactory theFactory = new KeyFactory();
|
public KeyFactory() {
|
||||||
|
|
||||||
/**
|
|
||||||
* Singleton constructor for key state, set up initial state of each action.
|
|
||||||
*/
|
|
||||||
private KeyFactory() {
|
|
||||||
this.keyState = new HashMap<>();
|
this.keyState = new HashMap<>();
|
||||||
keyState.put("Z", new ZoomInKey("Zoom In"));
|
keyState.put("Z", new ZoomInKey());
|
||||||
keyState.put("X", new ZoomOutKey("Zoom Out"));
|
keyState.put("X", new ZoomOutKey());
|
||||||
keyState.put("SPACE", new VMGKey("VMG"));
|
keyState.put("SPACE", new VMGKey());
|
||||||
keyState.put("SHIFT", new SailsToggleKey("Toggle Sails"));
|
keyState.put("SHIFT", new SailsToggleKey());
|
||||||
keyState.put("ENTER", new TackGybeKey("Tack/Gybe"));
|
keyState.put("ENTER", new TackGybeKey());
|
||||||
keyState.put("UP", new UpWindKey("Upwind"));
|
keyState.put("UP", new UpWindKey());
|
||||||
keyState.put("DOWN", new DownWindKey("Downwind"));
|
keyState.put("DOWN", new DownWindKey());
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get singleton instance of KeyFactory to interact with key state
|
|
||||||
* @return automatically constructed KeyFactory
|
|
||||||
*/
|
|
||||||
public static KeyFactory getFactory() {
|
|
||||||
return theFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,4 +38,86 @@ public class KeyFactory {
|
|||||||
return keyState.get(key);
|
return keyState.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, ControlKey> getKeyState() {
|
||||||
|
return keyState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeyState(Map<String, ControlKey> keyState) {
|
||||||
|
this.keyState = keyState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the key bound to a particular command in the keystate.
|
||||||
|
* @param newKey the new key value for the command
|
||||||
|
* @param command the command to be updated
|
||||||
|
*/
|
||||||
|
public void updateKey(String newKey, String command){
|
||||||
|
ControlKey controlKey = null;
|
||||||
|
String oldKey = null;
|
||||||
|
for (Map.Entry<String, ControlKey> entry : keyState.entrySet()) {
|
||||||
|
// if this is the correct command
|
||||||
|
if (entry.getValue().toString()==command){
|
||||||
|
controlKey = entry.getValue();
|
||||||
|
oldKey = entry.getKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keyState.remove(oldKey, controlKey);
|
||||||
|
keyState.put(newKey, controlKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persistently saves the keybindings the user has set.
|
||||||
|
*/
|
||||||
|
public void save(){
|
||||||
|
try {
|
||||||
|
// open the filestream and write to it
|
||||||
|
FileOutputStream fos = new FileOutputStream(
|
||||||
|
System.getProperty("user.dir")+
|
||||||
|
"/settings/keyBindings.xml");
|
||||||
|
XMLEncoder xmlEncoder = new XMLEncoder(fos);
|
||||||
|
xmlEncoder.writeObject(this.keyState);
|
||||||
|
xmlEncoder.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the persistently saved keybindings the user has set.
|
||||||
|
*/
|
||||||
|
public void load(){
|
||||||
|
try {
|
||||||
|
// access settings folder, create if it doesn't exist
|
||||||
|
File settingsFolder = new File(
|
||||||
|
System.getProperty("user.dir")+"/settings");
|
||||||
|
if (!settingsFolder.exists()){
|
||||||
|
settingsFolder.mkdir();
|
||||||
|
}
|
||||||
|
|
||||||
|
// access keybindings xml file, create if it doesn't exist
|
||||||
|
File savedFile = new File(
|
||||||
|
settingsFolder+"/keyBindings.xml");
|
||||||
|
if (!savedFile.exists()){
|
||||||
|
savedFile.createNewFile();
|
||||||
|
FileOutputStream fos = new FileOutputStream(savedFile);
|
||||||
|
XMLEncoder xmlEncoder = new XMLEncoder(fos);
|
||||||
|
xmlEncoder.writeObject(this.keyState);
|
||||||
|
xmlEncoder.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the saved settings into the game
|
||||||
|
InputStream is = new FileInputStream(savedFile);
|
||||||
|
XMLDecoder xmlDecoder = new XMLDecoder(is);
|
||||||
|
Map<String, ControlKey> savedKeyState
|
||||||
|
= (Map<String, ControlKey>)xmlDecoder.readObject();
|
||||||
|
xmlDecoder.close();
|
||||||
|
this.keyState = savedKeyState;
|
||||||
|
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ public class SailsToggleKey extends ControlKey {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for Control
|
* Constructor for Control
|
||||||
* @param name name of the key
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public SailsToggleKey(String name) {
|
public SailsToggleKey() {
|
||||||
super(name, BoatActionEnum.NOT_A_STATUS);
|
super("Toggle Sails", BoatActionEnum.NOT_A_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,11 +9,9 @@ public class TackGybeKey extends ControlKey {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for Control
|
* Constructor for Control
|
||||||
* @param name name of the key
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public TackGybeKey(String name) {
|
public TackGybeKey() {
|
||||||
super(name, BoatActionEnum.TACK_GYBE);
|
super("Tack/Gybe", BoatActionEnum.TACK_GYBE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -9,11 +9,9 @@ public class UpWindKey extends ControlKey {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for Control
|
* Constructor for Control
|
||||||
* @param name name of the key
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public UpWindKey(String name) {
|
public UpWindKey() {
|
||||||
super(name, BoatActionEnum.UPWIND);
|
super("Upwind", BoatActionEnum.UPWIND);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package visualiser.gameController.Keys;
|
package visualiser.gameController.Keys;
|
||||||
|
|
||||||
import javafx.scene.input.KeyCode;
|
|
||||||
import network.Messages.Enums.BoatActionEnum;
|
import network.Messages.Enums.BoatActionEnum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10,11 +9,9 @@ public class VMGKey extends ControlKey{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for Control
|
* Constructor for Control
|
||||||
*
|
|
||||||
* @param name name of the key
|
|
||||||
*/
|
*/
|
||||||
public VMGKey(String name) {
|
public VMGKey() {
|
||||||
super(name, BoatActionEnum.AUTO_PILOT);
|
super("VMG", BoatActionEnum.AUTO_PILOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ package visualiser.gameController.Keys;
|
|||||||
*/
|
*/
|
||||||
public class ZoomInKey extends ControlKey {
|
public class ZoomInKey extends ControlKey {
|
||||||
|
|
||||||
public ZoomInKey(String name) {
|
public ZoomInKey() {
|
||||||
super(name);
|
super("Zoom In");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -7,11 +7,9 @@ public class ZoomOutKey extends ControlKey{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for Control
|
* Constructor for Control
|
||||||
* @param name name of the key
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public ZoomOutKey(String name) {
|
public ZoomOutKey() {
|
||||||
super(name);
|
super("Zoom Out");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,9 +39,9 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
private Image sailsLuff = new Image("/images/sailsLuff.gif", 25, 10, false, false);
|
private Image sailsLuff = new Image("/images/sailsLuff.gif", 25, 10, false, false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The race we read data from and draw.
|
* The race state we read data from and draw.
|
||||||
*/
|
*/
|
||||||
private VisualiserRaceEvent visualiserRace;
|
private VisualiserRaceState raceState;
|
||||||
|
|
||||||
|
|
||||||
private boolean annoName = true;
|
private boolean annoName = true;
|
||||||
@@ -56,14 +56,14 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a {@link ResizableRaceCanvas} using a given {@link VisualiserRaceEvent}.
|
* Constructs a {@link ResizableRaceCanvas} using a given {@link VisualiserRaceEvent}.
|
||||||
* @param visualiserRace The race that data is read from in order to be drawn.
|
* @param raceState The race state to be drawn.
|
||||||
*/
|
*/
|
||||||
public ResizableRaceCanvas(VisualiserRaceEvent visualiserRace) {
|
public ResizableRaceCanvas(VisualiserRaceState raceState) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.visualiserRace = visualiserRace;
|
this.raceState = raceState;
|
||||||
|
|
||||||
RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource();
|
RaceDataSource raceData = raceState.getRaceDataSource();
|
||||||
|
|
||||||
double lat1 = raceData.getMapTopLeft().getLatitude();
|
double lat1 = raceData.getMapTopLeft().getLatitude();
|
||||||
double long1 = raceData.getMapTopLeft().getLongitude();
|
double long1 = raceData.getMapTopLeft().getLongitude();
|
||||||
@@ -276,8 +276,8 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
boat.getCountry(),
|
boat.getCountry(),
|
||||||
boat.getCurrentSpeed(),
|
boat.getCurrentSpeed(),
|
||||||
this.map.convertGPS(boat.getPosition()),
|
this.map.convertGPS(boat.getPosition()),
|
||||||
boat.getTimeToNextMarkFormatted(this.visualiserRace.getVisualiserRaceState().getRaceClock().getCurrentTime()),
|
boat.getTimeToNextMarkFormatted(raceState.getRaceClock().getCurrentTime()),
|
||||||
boat.getTimeSinceLastMarkFormatted(this.visualiserRace.getVisualiserRaceState().getRaceClock().getCurrentTime()),
|
boat.getTimeSinceLastMarkFormatted(raceState.getRaceClock().getCurrentTime()),
|
||||||
Color.BLACK,
|
Color.BLACK,
|
||||||
20 );
|
20 );
|
||||||
|
|
||||||
@@ -291,7 +291,7 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
*/
|
*/
|
||||||
private void drawBoats() {
|
private void drawBoats() {
|
||||||
|
|
||||||
List<VisualiserBoat> boats = new ArrayList<>(visualiserRace.getVisualiserRaceState().getBoats());
|
List<VisualiserBoat> boats = new ArrayList<>(raceState.getBoats());
|
||||||
//Sort to ensure we draw boats in consistent order.
|
//Sort to ensure we draw boats in consistent order.
|
||||||
boats.sort(Comparator.comparingInt(Boat::getSourceID));
|
boats.sort(Comparator.comparingInt(Boat::getSourceID));
|
||||||
|
|
||||||
@@ -510,7 +510,7 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
*/
|
*/
|
||||||
private void drawMarks() {
|
private void drawMarks() {
|
||||||
|
|
||||||
for (Mark mark : new ArrayList<>(visualiserRace.getVisualiserRaceState().getMarks())) {
|
for (Mark mark : new ArrayList<>(raceState.getMarks())) {
|
||||||
drawMark(mark);
|
drawMark(mark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -574,7 +574,7 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
|
|
||||||
|
|
||||||
//Calculate the screen coordinates of the boundary.
|
//Calculate the screen coordinates of the boundary.
|
||||||
List<GPSCoordinate> boundary = new ArrayList<>(visualiserRace.getVisualiserRaceState().getBoundary());
|
List<GPSCoordinate> boundary = new ArrayList<>(raceState.getBoundary());
|
||||||
double[] xpoints = new double[boundary.size()];
|
double[] xpoints = new double[boundary.size()];
|
||||||
double[] ypoints = new double[boundary.size()];
|
double[] ypoints = new double[boundary.size()];
|
||||||
|
|
||||||
@@ -601,8 +601,8 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
public void drawRace() {
|
public void drawRace() {
|
||||||
|
|
||||||
//Update RaceMap with new GPS values of race.
|
//Update RaceMap with new GPS values of race.
|
||||||
this.map.setGPSTopLeft(visualiserRace.getVisualiserRaceState().getRaceDataSource().getMapTopLeft());
|
this.map.setGPSTopLeft(raceState.getRaceDataSource().getMapTopLeft());
|
||||||
this.map.setGPSBotRight(visualiserRace.getVisualiserRaceState().getRaceDataSource().getMapBottomRight());
|
this.map.setGPSBotRight(raceState.getRaceDataSource().getMapBottomRight());
|
||||||
|
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
@@ -627,7 +627,7 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
* draws a transparent line around the course that shows the paths boats must travel
|
* draws a transparent line around the course that shows the paths boats must travel
|
||||||
*/
|
*/
|
||||||
public void drawRaceLine(){
|
public void drawRaceLine(){
|
||||||
List<Leg> legs = this.visualiserRace.getVisualiserRaceState().getLegs();
|
List<Leg> legs = raceState.getLegs();
|
||||||
GPSCoordinate legStartPoint = legs.get(0).getStartCompoundMark().getAverageGPSCoordinate();
|
GPSCoordinate legStartPoint = legs.get(0).getStartCompoundMark().getAverageGPSCoordinate();
|
||||||
GPSCoordinate nextStartPoint;
|
GPSCoordinate nextStartPoint;
|
||||||
for (int i = 0; i < legs.size() -1; i++) {
|
for (int i = 0; i < legs.size() -1; i++) {
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package visualiser.model;
|
package visualiser.model;
|
||||||
|
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import network.Messages.Enums.BoatStatusEnum;
|
import network.Messages.Enums.BoatStatusEnum;
|
||||||
import shared.model.Azimuth;
|
import shared.model.*;
|
||||||
import shared.model.Boat;
|
|
||||||
import shared.model.Constants;
|
|
||||||
import shared.model.GPSCoordinate;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
@@ -61,7 +60,8 @@ public class VisualiserBoat extends Boat {
|
|||||||
private boolean isClientBoat = false;
|
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) {
|
public void setClientBoat(boolean clientBoat) {
|
||||||
isClientBoat = 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 {
|
#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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
.list-view .list-cell {
|
||||||
|
-fx-cell-size: 40;
|
||||||
|
-fx-font-family: "Tahoma";
|
||||||
|
-fx-background-color: #fffff0;
|
||||||
|
-fx-alignment: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-view .list-cell:even {
|
||||||
|
-fx-background-color: #ffffdc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-view .list-cell:selected {
|
||||||
|
-fx-background-color: #f9e5c3;
|
||||||
|
-fx-font-family: "Comic Sans MS";
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
-fx-font-size: 18;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
-fx-background-color: linear-gradient(#acdeff 50%, #a9c8ff 100%);
|
||||||
|
-fx-background-radius: 4px;
|
||||||
|
-fx-border-radius: 4px;
|
||||||
|
-fx-text-fill: #242d35;
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-font-family: "Courier New";
|
||||||
|
-fx-border-color: #a9c8ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:focused {
|
||||||
|
-fx-background-color: white;
|
||||||
|
-fx-border-color: #0056bd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#anchor {
|
||||||
|
-fx-background-color: #fdfac3;
|
||||||
|
}
|
||||||
|
#menu{
|
||||||
|
-fx-background-color: linear-gradient(#acdeff 50%, #a9c8ff 100%);
|
||||||
|
-fx-background-radius: 4px;
|
||||||
|
-fx-border-radius: 4px;
|
||||||
|
-fx-text-fill: #242d35;
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-font-family: "Verdana";
|
||||||
|
-fx-border-color: #a9c8ff;
|
||||||
|
}
|
||||||
@@ -57,3 +57,37 @@
|
|||||||
#arrowImage {
|
#arrowImage {
|
||||||
-fx-image: url("/visualiser/images/arrowLight.png");
|
-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;
|
||||||
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 2.7 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 13 KiB |
@@ -32,7 +32,6 @@
|
|||||||
<GPSposition X= "-64.83626" Y="32.317257" Z="0"/>
|
<GPSposition X= "-64.83626" Y="32.317257" Z="0"/>
|
||||||
</Boat>
|
</Boat>
|
||||||
|
|
||||||
<!--Participants-->
|
|
||||||
<!--Participants-->
|
<!--Participants-->
|
||||||
<Boat BoatName="Emirates Team New Zealand" HullNum="RG01" ShapeID="0" ShortName="NZL" SourceID="121" StoweName="NZL" Type="Yacht">
|
<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"/>
|
<GPSposition X="-64.854304" Y="32.296577" Z="0"/>
|
||||||
|
|||||||
@@ -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>
|
<CreationTimeDate>RACE_CREATION_TIME</CreationTimeDate>
|
||||||
<RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
|
<RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
|
||||||
<Participants>
|
<Participants>
|
||||||
<Yacht SourceID="126"/>
|
|
||||||
</Participants>
|
</Participants>
|
||||||
<CompoundMarkSequence>
|
<CompoundMarkSequence>
|
||||||
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />
|
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />
|
||||||
|
|||||||
@@ -5,9 +5,7 @@
|
|||||||
<CreationTimeDate>RACE_CREATION_TIME</CreationTimeDate>
|
<CreationTimeDate>RACE_CREATION_TIME</CreationTimeDate>
|
||||||
<RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
|
<RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
|
||||||
<Participants>
|
<Participants>
|
||||||
<Yacht SourceID="124"/>
|
|
||||||
<Yacht SourceID="125"/>
|
|
||||||
<Yacht SourceID="126"/>
|
|
||||||
</Participants>
|
</Participants>
|
||||||
<CompoundMarkSequence>
|
<CompoundMarkSequence>
|
||||||
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />
|
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />
|
||||||
@@ -1,57 +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="121"/>
|
|
||||||
<Yacht SourceID="122"/>
|
|
||||||
<Yacht SourceID="123"/>
|
|
||||||
<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>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:element name="BoatConfig">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Boats">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="Boat" maxOccurs="unbounded">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="GPSposition" maxOccurs="1">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute name="X" type="xs:double" use="required"/>
|
||||||
|
<xs:attribute name="Y" type="xs:double" use="required"/>
|
||||||
|
<xs:attribute name="Z" type="xs:double" use="required"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="Type" type="xs:string" use="required"/>
|
||||||
|
<xs:attribute name="BoatName" type="xs:string" use="required"/>
|
||||||
|
<xs:attribute name="SourceID" type="xs:int" use="required"/>
|
||||||
|
<xs:attribute name="HullNum" type="xs:string"/>
|
||||||
|
<xs:attribute name="ShortName" type="xs:string"/>
|
||||||
|
<xs:attribute name="ShapeID" type="xs:int"/>
|
||||||
|
<xs:attribute name="StoweName" type="xs:string"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:schema>
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
<xs:element name="RegattaConfig">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element type="xs:int" name="RegattaID" minOccurs="1"/>
|
||||||
|
<xs:element type="xs:string" name="RegattaName" minOccurs="1"/>
|
||||||
|
<xs:element type="xs:string" name="CourseName" minOccurs="1"/>
|
||||||
|
<xs:element name="CentralLatitude" type="xs:double" minOccurs="1"/>
|
||||||
|
<xs:element name="CentralLongitude" type="xs:double" minOccurs="1"/>
|
||||||
|
<xs:element name="CentralAltitude" type="xs:double" minOccurs="1"/>
|
||||||
|
<xs:element type="xs:double" name="UtcOffset" minOccurs="1"/>
|
||||||
|
<xs:element name="MagneticVariation" type="xs:double" minOccurs="1"/>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:schema>
|
||||||
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
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,39 +1,60 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?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.Button?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?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?>
|
<?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" fx:controller="visualiser.Controllers.HostController">
|
<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>
|
<children>
|
||||||
<GridPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<GridPane layoutY="14.0" AnchorPane.bottomAnchor="-14.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="14.0">
|
||||||
<columnConstraints>
|
<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" minWidth="10.0" prefWidth="100.0" />
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="170.0" minWidth="10.0" prefWidth="170.0" />
|
||||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
<rowConstraints>
|
<rowConstraints>
|
||||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
<RowConstraints maxHeight="60.0" minHeight="60.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
<RowConstraints minHeight="10.0" prefHeight="435.0" vgrow="SOMETIMES" />
|
||||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
<RowConstraints maxHeight="80.0" minHeight="80.0" prefHeight="80.0" vgrow="SOMETIMES" />
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
<children>
|
<children>
|
||||||
<Button fx:id="hostGameBtn" mnemonicParsing="false" onAction="#hostGamePressed" text="Start Game" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="2">
|
<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>
|
||||||
<Font size="20.0" />
|
<Font size="20.0" />
|
||||||
</font>
|
</font>
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
|
||||||
|
</GridPane.margin>
|
||||||
</Button>
|
</Button>
|
||||||
<Label text="Address: 127.0.0.1" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP">
|
<Label text="Address: 127.0.0.1" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="TOP">
|
||||||
<font>
|
<font>
|
||||||
<Font size="17.0" />
|
<Font size="17.0" />
|
||||||
</font>
|
</font>
|
||||||
</Label>
|
</Label>
|
||||||
<Label text="Port: 4942" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1">
|
<Label text="Port: 4942" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
|
||||||
<font>
|
<font>
|
||||||
<Font size="17.0" />
|
<Font size="17.0" />
|
||||||
</font>
|
</font>
|
||||||
</Label>
|
</Label>
|
||||||
<Button mnemonicParsing="false" onAction="#menuBtnPressed" text="Main Menu" GridPane.halignment="CENTER" />
|
<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>
|
</children>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
</children>
|
</children>
|
||||||
|
|||||||