mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Compare commits
99 Commits
sprint_1.1
...
sprint_2.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 7022be1979 | |||
| 971a3920a3 | |||
| b252797e9b | |||
| c758afe3e3 | |||
| f8d3f53158 | |||
| 4a8672a20b | |||
| 32109e8565 | |||
| 5d6060c690 | |||
| a95d030817 | |||
| ffa84c6e87 | |||
| 4a6978ff79 | |||
| 1497858cc0 | |||
| 2c125d4ce0 | |||
| 8bf5455f42 | |||
| 48f7e41905 | |||
| b50ac62a4b | |||
| 6fc55bb82c | |||
| 65ac864bf2 | |||
| cf6bbdd1f1 | |||
| bbe7cbee8f | |||
| 55ba343426 | |||
| e6ace5fb2f | |||
| 550ab59231 | |||
| 213303d674 | |||
| 74c81eb7b3 | |||
| c33586e7f5 | |||
| 5dd5e50738 | |||
| 304f30ece6 | |||
| e8b1720fee | |||
| e0cea098c1 | |||
| 50ab481b18 | |||
| d39aacba83 | |||
| 798fe4da0e | |||
| e19f110f19 | |||
| 59a4a74a97 | |||
| 590ef557d3 | |||
| 7bc5c8f8a4 | |||
| bf8244ce49 | |||
| a860cc0ec1 | |||
| bb8c681270 | |||
| 9872095e50 | |||
| 403aaa76ae | |||
| 8578bc4a5b | |||
| ed004d1423 | |||
| a91b2b4f7e | |||
| 24f9607e5a | |||
| 2384013139 | |||
| 42ffd1b1f8 | |||
| a2d06909c9 | |||
| c8b7b95df8 | |||
| 9e22eac4d8 | |||
| a41f2e4bde | |||
| 039e61def6 | |||
| 54d329c5cf | |||
| f46a98fad9 | |||
| 6a6816dda1 | |||
| f6ea2953e9 | |||
| 7591a79323 | |||
| c12760b70a | |||
| a526b0d65a | |||
| ef098e63d7 | |||
| 29a0b23670 | |||
| 44b5b0b771 | |||
| 00f9cc4698 | |||
| 3d4d5a3dab | |||
| ed577fad6a | |||
| ae61260665 | |||
| 0e4bb0f942 | |||
| ee34e5028f | |||
| 3b8dd11758 | |||
| cc04e2dd6d | |||
| b88cf6a101 | |||
| d10985f890 | |||
| c08504293b | |||
| 4bc49da10d | |||
| 683f4ba94e | |||
| 8fd06c84ac | |||
| 23b163e6c1 | |||
| 6383b9a6f8 | |||
| 0b3ebf229f | |||
| d6fe155d4d | |||
| 8829728f19 | |||
| a6c9156ae5 | |||
| 121f996a43 | |||
| 44d4f25413 | |||
| 16abfcffda | |||
| e7ba9d962d | |||
| b7631c0b46 | |||
| 0039703f03 | |||
| d439c9673f | |||
| f255b0ea6d | |||
| 9d01ed8eb3 | |||
| 94e4e853c3 | |||
| ffa39e6a9c | |||
| 9e4ae60885 | |||
| 2cd4366d07 | |||
| 11c5e1e9ba | |||
| 550812d8e1 | |||
| 9ca5f5e7fd |
@@ -84,6 +84,9 @@ nbactions.xml
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
# get rid of these annoying files
|
||||
.idea/
|
||||
*.iml
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
@@ -1,2 +1,16 @@
|
||||
# Design Decisions
|
||||
- Code structure
|
||||
|
||||
App creates a race instance which can:
|
||||
instantiate a file parser to extract race setting and team information;
|
||||
creates and passes legs and teams/boats into event generator to create events;
|
||||
runs a race and iterates all events that returned from the generator;
|
||||
prints out event details, including time, involved boats and legs.
|
||||
|
||||
- Configuration file
|
||||
|
||||
We decided to store the team information including team names and boat velocity, as well as race configuration setting in external file.
|
||||
|
||||
To read external files, "Json-simple" library has been used to parse information.
|
||||
By using this library, we did not have to write our json parser and benefited from the flexibility of json files.
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"race-name": "AC35",
|
||||
"time-scale": 2.0,
|
||||
"race-size": 6,
|
||||
"teams": [
|
||||
{
|
||||
"team-name": "Oracle Team USA",
|
||||
"velocity": 20.9
|
||||
},
|
||||
{
|
||||
"team-name": "Artemis Racing",
|
||||
"velocity": 18.3
|
||||
},
|
||||
{
|
||||
"team-name": "Emirates Team New Zealand",
|
||||
"velocity": 21.5
|
||||
},
|
||||
{
|
||||
"team-name": "Groupama Team France",
|
||||
"velocity": 19.9
|
||||
},
|
||||
{
|
||||
"team-name": "Land Rover BAR",
|
||||
"velocity": 17.6
|
||||
},
|
||||
{
|
||||
"team-name": "SoftBank Team Japan",
|
||||
"velocity": 16.6
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -25,6 +25,12 @@
|
||||
<artifactId>json-simple</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>2.7.13</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
+16
-102
@@ -1,111 +1,25 @@
|
||||
package seng302;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.io.FileNotFoundException;
|
||||
import javafx.application.Application;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
public class App {
|
||||
public class App extends Application
|
||||
{
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception {
|
||||
Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml"));
|
||||
primaryStage.setTitle("RaceVision");
|
||||
primaryStage.setScene(new Scene(root));
|
||||
|
||||
/**
|
||||
* Builds a race object for the AC35 course
|
||||
*
|
||||
* @return a Race object for the AC35 course
|
||||
*/
|
||||
public static Race createRace(String configFile) throws Exception {
|
||||
Race race = new Race();
|
||||
FileParser fp;
|
||||
|
||||
// Read team names from file
|
||||
try{
|
||||
fp = new FileParser(configFile);
|
||||
}
|
||||
catch (FileNotFoundException e){
|
||||
System.out.println("Config file does not exist");
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayList<String> boatNames = new ArrayList<>();
|
||||
ArrayList<Map<String, Object>> teams = fp.getTeams();
|
||||
|
||||
//get race size
|
||||
int numberOfBoats = (int) fp.getRaceSize();
|
||||
|
||||
//get time scale
|
||||
double timeScale = fp.getTimeScale();
|
||||
race.setTimeScale(timeScale);
|
||||
|
||||
for (Map<String, Object> team : teams) {
|
||||
boatNames.add((String) team.get("team-name"));
|
||||
}
|
||||
|
||||
// Shuffle team names
|
||||
long seed = System.nanoTime();
|
||||
Collections.shuffle(boatNames, new Random(seed));
|
||||
|
||||
if (numberOfBoats > Array.getLength(boatNames.toArray())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add boats to the race
|
||||
for (int i = 0; i < numberOfBoats; i++) {
|
||||
race.addBoat(new Boat(boatNames.get(i), (Double) (teams.get(i).get("velocity"))));
|
||||
}
|
||||
|
||||
race.addLeg(new Leg(35, 100, "Start"));
|
||||
race.addLeg(new Leg(10, 300, "Marker 1"));
|
||||
race.addLeg(new Leg(350, 400, "Leeward Gate"));
|
||||
race.addLeg(new Leg(10, 400, "Windward Gate"));
|
||||
|
||||
Leg finishingLeg = new Leg(10, 400, "Leeward Gate");
|
||||
finishingLeg.setFinishingLeg(true);
|
||||
|
||||
race.addLeg(finishingLeg);
|
||||
|
||||
return race;
|
||||
primaryStage.show();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Race race = null;
|
||||
String raceConfigFile;
|
||||
|
||||
if (args.length == 2 && args[0].equals("-f")){
|
||||
raceConfigFile = args[1];
|
||||
}
|
||||
else{
|
||||
// Use default config
|
||||
raceConfigFile = "doc/examples/config.json";
|
||||
}
|
||||
|
||||
try {
|
||||
race = createRace(raceConfigFile);
|
||||
} catch (Exception e) {
|
||||
System.out.println("There was an error creating the race.");
|
||||
}
|
||||
|
||||
// If race was created
|
||||
if (race != null) {
|
||||
race.displayStartingBoats();
|
||||
|
||||
System.out.println("\n\n");
|
||||
System.out.println("######################");
|
||||
System.out.println("# Live Race Updates ");
|
||||
System.out.println("######################");
|
||||
|
||||
race.startRace();
|
||||
|
||||
System.out.println("\n\n");
|
||||
System.out.println("######################");
|
||||
System.out.println("# Race Results ");
|
||||
System.out.println("######################");
|
||||
|
||||
race.showRaceMarkerResults();
|
||||
race.displayFinishingOrder();
|
||||
|
||||
} else {
|
||||
System.out.println("There was an error creating the race. Exiting.");
|
||||
}
|
||||
launch(args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
package seng302;
|
||||
|
||||
/**
|
||||
* Represents a boat in the race.
|
||||
*/
|
||||
public class Boat {
|
||||
|
||||
private String teamName; // The name of the team, this is also the name of the boat
|
||||
private double velocity; // In meters/second
|
||||
|
||||
public Boat(String teamName) {
|
||||
this.teamName = teamName;
|
||||
this.velocity = 10; // Default velocity
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a boat in the race.
|
||||
*
|
||||
* @param teamName The name of the team sailing the boat
|
||||
* @param boatVelocity The speed of the boat in meters/second
|
||||
*/
|
||||
public Boat(String teamName, double boatVelocity) {
|
||||
this.teamName = teamName;
|
||||
this.velocity = boatVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the team sailing the boat
|
||||
*
|
||||
* @return The name of the team
|
||||
*/
|
||||
public String getTeamName() {
|
||||
return this.teamName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the team sailing the boat
|
||||
*
|
||||
* @param teamName The name of the team
|
||||
*/
|
||||
public void setTeamName(String teamName) {
|
||||
this.teamName = teamName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets velocity of the boat
|
||||
*
|
||||
* @return a float number of the boat velocity
|
||||
*/
|
||||
public double getVelocity() {
|
||||
return this.velocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets velocity of the boat
|
||||
*
|
||||
* @param velocity The velocity of boat
|
||||
*/
|
||||
public void setVelocity(double velocity) {
|
||||
this.velocity = velocity;
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
package seng302;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Event class containing the time of specific event, related team/boat, and
|
||||
* event location such as leg.
|
||||
*/
|
||||
public class Event {
|
||||
|
||||
private long time; // Time the event occurs
|
||||
private Boat boat;
|
||||
private Leg leg; // Leg of the race the event occurs on
|
||||
private boolean isFinishingEvent = false; // This event occurs when a boat finishes the race
|
||||
|
||||
/**
|
||||
* Event class containing the time of specific event, related team/boat, and
|
||||
* event location such as leg.
|
||||
*
|
||||
* @param eventTime, what time the event happens
|
||||
* @param eventBoat, the boat that the event belongs to
|
||||
* @param eventLeg, the leg the event happens on
|
||||
*/
|
||||
public Event(long eventTime, Boat eventBoat, Leg eventLeg) {
|
||||
this.time = eventTime;
|
||||
this.boat = eventBoat;
|
||||
this.leg = eventLeg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event class containing the time of specific event, related team/boat, and
|
||||
* event location such as leg.
|
||||
*
|
||||
* @param eventTime, what time the event happens
|
||||
* @param eventBoat, the boat that the event belongs to
|
||||
* @param eventLeg, the leg the event happens on
|
||||
* @param isFinishingEvent, true if this event is the boat crossing the finishing line
|
||||
*/
|
||||
public Event(long eventTime, Boat eventBoat, Leg eventLeg, boolean isFinishingEvent) {
|
||||
this.time = eventTime;
|
||||
this.boat = eventBoat;
|
||||
this.leg = eventLeg;
|
||||
this.isFinishingEvent = isFinishingEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time for the event
|
||||
*
|
||||
* @return the time for event in millisecond
|
||||
*/
|
||||
public long getTime() {
|
||||
return this.time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time for the event
|
||||
*
|
||||
* @param eventTime the time for event in millisecond
|
||||
*/
|
||||
public void setTime(long eventTime) {
|
||||
this.time = eventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time in a formatted string
|
||||
*
|
||||
* @return the string of time
|
||||
*/
|
||||
public String getTimeString() {
|
||||
return (new SimpleDateFormat("mm:ss:SSS")).format(new Date(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the involved boat
|
||||
*
|
||||
* @return the boat involved in the event
|
||||
*/
|
||||
public Boat getBoat() {
|
||||
return this.boat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the involved boat
|
||||
*
|
||||
* @param eventBoat the involved boat
|
||||
*/
|
||||
public void setBoat(Boat eventBoat) {
|
||||
this.boat = eventBoat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the involved location/leg
|
||||
*
|
||||
* @return the leg involved in the event
|
||||
*/
|
||||
public Leg getLeg() {
|
||||
return this.leg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the involved location/leg
|
||||
*
|
||||
* @param eventLeg the involved leg
|
||||
*/
|
||||
public void setLeg(Leg eventLeg) {
|
||||
this.leg = eventLeg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the boat in this event passes
|
||||
* the marker.
|
||||
*/
|
||||
public void boatPassedMarker() {
|
||||
this.leg.addBoatToMarker(boat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this event is the boat finishing the race
|
||||
*/
|
||||
public boolean getIsFinishingEvent() {
|
||||
return this.isFinishingEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string that contains the timestamp and course information for this event
|
||||
*
|
||||
* @return A string that details what happened in this event
|
||||
*/
|
||||
public String getEventString() {
|
||||
String currentHeading = Integer.toString(this.getLeg().getHeading());
|
||||
|
||||
// This event is a boat finishing the race
|
||||
if (this.isFinishingEvent) {
|
||||
return (this.getTimeString() + ", " + this.getBoat().getTeamName() + " finished the race");
|
||||
}
|
||||
|
||||
return (this.getTimeString() + ", " + this.getBoat().getTeamName() + " passed " + this.getLeg().getMarkerLabel() + " going heading " + currentHeading + "°");
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
package seng302;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Read team name from a given Json file. So that user can extract information
|
||||
* efficiently from external files.
|
||||
*/
|
||||
|
||||
public class FileParser {
|
||||
|
||||
private String filePath;
|
||||
private JSONObject content;
|
||||
|
||||
/**
|
||||
* used to construct an instance of file parser
|
||||
*
|
||||
* @param filePath a string like path to show location of desired file to
|
||||
* be parsed
|
||||
*/
|
||||
public FileParser(String filePath) throws Exception {
|
||||
this.filePath = filePath;
|
||||
this.readFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads content from a given file, and return the content as JSONObject.
|
||||
* Throws FileNotFoundException, if the given file cannot be found.
|
||||
*/
|
||||
private void readFile() throws FileNotFoundException {
|
||||
JSONParser parser = new JSONParser();
|
||||
try {
|
||||
this.content = (JSONObject) parser.parse(new FileReader(filePath));
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets time scale setting parameter.
|
||||
*
|
||||
* @return long time scale. -1 if parameter is invalid (eg. scale is
|
||||
* negative number, or containing non numeric character) or cannot be found.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public double getTimeScale() {
|
||||
try {
|
||||
double timeScale = (double) this.content.get("time-scale");
|
||||
return timeScale >= 0 ? timeScale : -1;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets race name in the setting file.
|
||||
*
|
||||
* @return a string of race name. null if race name is invalid or cannot
|
||||
* be found.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public String getRaceName() {
|
||||
try {
|
||||
return (String) this.content.get("race-name");
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of teams who participate the race.
|
||||
*
|
||||
* @return an ArrayList containing strings of team names. null if teams
|
||||
* setting is invalid or there is no team.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ArrayList<Map<String, Object>> getTeams() {
|
||||
try {
|
||||
return (ArrayList<Map<String, Object>>) this.content.get("teams");
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of teams.
|
||||
*
|
||||
* @return the number of teams. 0 if no teams or anything goes wrong.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public long getTotalNumberOfTeams() {
|
||||
ArrayList<Map<String, Object>> teams = getTeams();
|
||||
try {
|
||||
return teams.size();
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of boats that would compete during a race. Returns the
|
||||
* total number of race size if parameter is invalid or cannot be found.
|
||||
*
|
||||
* @return an int of the race size.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public long getRaceSize() {
|
||||
long totalTeams = this.getTotalNumberOfTeams();
|
||||
try {
|
||||
long raceSize = (long) this.content.get("race-size");
|
||||
return raceSize >= 0 && raceSize <= totalTeams ? raceSize : totalTeams;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return totalTeams;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package seng302;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Represents the marker at the beginning of a leg
|
||||
*/
|
||||
class Marker{
|
||||
private String name;
|
||||
private ArrayList<Boat> boatOrder;
|
||||
|
||||
/**
|
||||
* Represents the marker at the beginning of a leg
|
||||
*
|
||||
* @param name, the name of the marker
|
||||
*/
|
||||
public Marker(String name){
|
||||
this.name = name;
|
||||
this.boatOrder = new ArrayList<Boat>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the marker
|
||||
*
|
||||
* @param name, the name of the marker
|
||||
*/
|
||||
public void setName(String name){
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the marker
|
||||
*
|
||||
* @return the name of the marker
|
||||
*/
|
||||
public String getName(){
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boat as they pass the marker
|
||||
*
|
||||
* @param boat, the boat that passed the marker
|
||||
*/
|
||||
public void addBoat(Boat boat){
|
||||
this.boatOrder.add(boat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of boats in the order they passed the marker
|
||||
*
|
||||
* @return An array of boats in the order they passed the marker
|
||||
*/
|
||||
public Boat[] getBoats(){
|
||||
return this.boatOrder.toArray(new Boat[this.boatOrder.size()]);
|
||||
}
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
package seng302;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Race class containing the boats and legs in the race
|
||||
*/
|
||||
public class Race {
|
||||
private ArrayList<Boat> boats; // The boats in the race
|
||||
private ArrayList<Leg> legs; // The legs in the race
|
||||
private ArrayList<Boat> finishingOrder; // The order in which the boats finish the race
|
||||
private PriorityQueue<Event> events; // The events that occur in the race
|
||||
private int numberOfBoats = 0;
|
||||
private long startTime = 0;
|
||||
private double timeScale = 1;
|
||||
|
||||
/**
|
||||
* Race class containing the boats and legs in the race
|
||||
*/
|
||||
public Race() {
|
||||
this.boats = new ArrayList<Boat>();
|
||||
this.legs = new ArrayList<Leg>();
|
||||
this.finishingOrder = new ArrayList<Boat>();
|
||||
|
||||
// create a priority queue with a custom Comparator to order events
|
||||
this.events = new PriorityQueue<Event>(new Comparator<Event>() {
|
||||
@Override
|
||||
public int compare(Event o1, Event o2) {
|
||||
Long time1 = o1.getTime();
|
||||
Long time2 = o2.getTime();
|
||||
|
||||
// order event asc. by time. if tie appears, then order team
|
||||
// name alphabetically.
|
||||
if (time1 != time2) {
|
||||
return time1.compareTo(time2);
|
||||
} else {
|
||||
return o1.getBoat().getTeamName().compareTo(o2.getBoat().getTeamName());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boat to the race
|
||||
*
|
||||
* @param boat, the boat to add
|
||||
*/
|
||||
public void addBoat(Boat boat) {
|
||||
boats.add(boat);
|
||||
numberOfBoats += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of boats in a random order
|
||||
*
|
||||
* @returns a list of boats
|
||||
*/
|
||||
public Boat[] getShuffledBoats() {
|
||||
// Shuffle the list of boats
|
||||
long seed = System.nanoTime();
|
||||
Collections.shuffle(this.boats, new Random(seed));
|
||||
|
||||
return boats.toArray(new Boat[boats.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of boats in the order that they
|
||||
* finished the race (position 0 is first place)
|
||||
*
|
||||
* @returns a list of boats
|
||||
*/
|
||||
public Boat[] getFinishedBoats() {
|
||||
return this.finishingOrder.toArray(new Boat[this.finishingOrder.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of boats in the race
|
||||
*
|
||||
* @returns the number of boats in the race
|
||||
*/
|
||||
public int getNumberOfBoats() {
|
||||
return numberOfBoats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of boats in the race
|
||||
*
|
||||
* @return a list of the boats competing in the race
|
||||
*/
|
||||
public Boat[] getBoats() {
|
||||
return boats.toArray(new Boat[boats.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the order in which the boats finished the race
|
||||
*/
|
||||
public void displayFinishingOrder() {
|
||||
int numberOfBoats = this.getNumberOfBoats();
|
||||
Boat[] boats = this.getFinishedBoats();
|
||||
|
||||
System.out.println("--- Finishing Order ---");
|
||||
|
||||
for (int i = 0; i < Array.getLength(boats); i++) {
|
||||
System.out.println("#" + Integer.toString(i + 1) + " - " + boats[i].getTeamName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the list of boats competing in the race
|
||||
*/
|
||||
public void displayStartingBoats() {
|
||||
int numberOfBoats = this.getNumberOfBoats();
|
||||
Boat[] boats = this.getBoats();
|
||||
|
||||
System.out.println("######################");
|
||||
System.out.println("# Competing Boats ");
|
||||
System.out.println("######################");
|
||||
|
||||
for (int i = 0; i < numberOfBoats; i++) {
|
||||
String velocityKnots = String.format("%1.2f", boats[i].getVelocity() * 1.943844492);
|
||||
|
||||
System.out.println(boats[i].getTeamName() + " Velocity: " + velocityKnots + " Knots/s");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a leg to the race
|
||||
*
|
||||
* @param leg, the leg to add to the race
|
||||
*/
|
||||
public void addLeg(Leg leg) {
|
||||
this.legs.add(leg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets legs array
|
||||
*
|
||||
* @return an array of legs
|
||||
*/
|
||||
public ArrayList<Leg> getLegs() {
|
||||
return this.legs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets time scale
|
||||
*
|
||||
* @param timeScale
|
||||
*/
|
||||
public void setTimeScale(double timeScale) {
|
||||
this.timeScale = timeScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate all events that will happen during the race.
|
||||
*/
|
||||
private void generateEvents() {
|
||||
//calculate the time every boat passes each leg, and create an event
|
||||
for (Boat boat : this.boats) {
|
||||
long totalDistance = 0;
|
||||
for (Leg leg : this.legs) {
|
||||
long time = (long) (1000 * totalDistance / boat.getVelocity());
|
||||
Event event = new Event(time, boat, leg);
|
||||
events.add(event);
|
||||
totalDistance += leg.getDistance();
|
||||
|
||||
// If finishing leg, add another event for when the boat finishes the race
|
||||
if (leg.getIsFinishingLeg()) {
|
||||
time = (long) (1000 * totalDistance / boat.getVelocity());
|
||||
event = new Event(time, boat, leg, true);
|
||||
events.add(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates how far a boat has travelled in meters
|
||||
*
|
||||
* @param velocity the velocity of boat
|
||||
* @return a float number of distance the boat has been travelled
|
||||
*/
|
||||
public float getDistanceTravelled(long velocity) {
|
||||
long timeDiff = System.currentTimeMillis() - this.startTime;
|
||||
long timeElapse = (long) (timeDiff / 1000 * this.timeScale);
|
||||
return timeElapse * velocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over events in the race and print the
|
||||
* event string for each event
|
||||
*/
|
||||
public void iterateEvents() {
|
||||
// iterates all events. ends when no event in events.
|
||||
while (!events.isEmpty()) {
|
||||
Event peekEvent = events.peek();
|
||||
long currentTime = (long) ((System.currentTimeMillis() - this.startTime) * this.timeScale);
|
||||
|
||||
if (currentTime > peekEvent.getTime()) {
|
||||
Event nextEvent = events.poll();
|
||||
|
||||
// Display a summary of the event
|
||||
System.out.println(nextEvent.getEventString());
|
||||
nextEvent.boatPassedMarker();
|
||||
|
||||
// If event is a boat finishing the race
|
||||
if (nextEvent.getIsFinishingEvent()) {
|
||||
this.finishingOrder.add(nextEvent.getBoat());
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for 100ms to throttle the while loop
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (java.lang.InterruptedException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the race and print each marker with the order
|
||||
* in which the boats passed that marker
|
||||
*/
|
||||
public void startRace() {
|
||||
// record start time.
|
||||
generateEvents();
|
||||
this.startTime = System.currentTimeMillis();
|
||||
iterateEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the order in which the boats passed each marker
|
||||
*/
|
||||
public void showRaceMarkerResults() {
|
||||
for (Leg leg : this.legs) {
|
||||
Boat[] boats = leg.getMarker().getBoats();
|
||||
|
||||
System.out.println("--- " + leg.getMarkerLabel() + " ---");
|
||||
|
||||
// Print the order in which the boats passed the marker
|
||||
for (int i = 0; i < this.getNumberOfBoats(); i++) {
|
||||
System.out.println("#" + Integer.toString(i + 1) + " - " + boats[i].getTeamName());
|
||||
}
|
||||
|
||||
System.out.println("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
package seng302.controllers;
|
||||
|
||||
import javafx.animation.*;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.canvas.Canvas;
|
||||
import javafx.scene.canvas.GraphicsContext;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import seng302.models.Boat;
|
||||
import seng302.models.TimelineInfo;
|
||||
import seng302.models.mark.GateMark;
|
||||
import seng302.models.mark.Mark;
|
||||
import seng302.models.mark.MarkType;
|
||||
import seng302.models.mark.SingleMark;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by ptg19 on 15/03/17.
|
||||
* Modified by Haoming Yin (hyi25) on 20/3/2017.
|
||||
*/
|
||||
public class CanvasController {
|
||||
|
||||
@FXML
|
||||
private AnchorPane canvasPane;
|
||||
|
||||
private RaceViewController raceViewController;
|
||||
private ResizableCanvas canvas;
|
||||
private GraphicsContext gc;
|
||||
|
||||
private final double ORIGIN_LAT = 32.321504;
|
||||
private final double ORIGIN_LON = -64.857063;
|
||||
private final int SCALE = 16000;
|
||||
|
||||
public void setup(RaceViewController raceViewController){
|
||||
this.raceViewController = raceViewController;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
canvas = new ResizableCanvas();
|
||||
canvasPane.getChildren().add(canvas);
|
||||
// Bind canvas size to stack pane size.
|
||||
canvas.widthProperty().bind(canvasPane.widthProperty());
|
||||
canvas.heightProperty().bind(canvasPane.heightProperty());
|
||||
gc = canvas.getGraphicsContext2D();
|
||||
|
||||
|
||||
// overriding the handle so that it can clean canvas and redraw boats and course marks
|
||||
AnimationTimer timer = new AnimationTimer() {
|
||||
private long lastUpdate = 0;
|
||||
private long lastFpsUpdate = 0;
|
||||
private int lastFpsCount = 0;
|
||||
private int fpsCount = 0;
|
||||
|
||||
@Override
|
||||
public void handle(long now) {
|
||||
if (true){ //if statement for limiting refresh rate if needed
|
||||
gc.clearRect(0, 0, canvas.getWidth(),canvas.getHeight());
|
||||
gc.setFill(Color.SKYBLUE);
|
||||
gc.fillRect(0,0,canvas.getWidth(),canvas.getHeight());
|
||||
drawCourse();
|
||||
drawBoats();
|
||||
drawFps(lastFpsCount);
|
||||
|
||||
// If race has started, draw the boats and play the timeline
|
||||
if (raceViewController.getRace().getRaceTime() > 1){
|
||||
raceViewController.playTimelines();
|
||||
}
|
||||
// Race has not started, pause the timelines
|
||||
else {
|
||||
raceViewController.pauseTimelines();
|
||||
}
|
||||
lastUpdate = now;
|
||||
fpsCount ++;
|
||||
if (now - lastFpsUpdate >= 1000000000){
|
||||
lastFpsCount = fpsCount;
|
||||
fpsCount = 0;
|
||||
lastFpsUpdate = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
timer.start();
|
||||
}
|
||||
|
||||
class ResizableCanvas extends Canvas {
|
||||
|
||||
public ResizableCanvas() {
|
||||
// Redraw canvas when size changes.
|
||||
widthProperty().addListener(evt -> draw());
|
||||
heightProperty().addListener(evt -> draw());
|
||||
}
|
||||
|
||||
private void draw() {
|
||||
double width = getWidth();
|
||||
double height = getHeight();
|
||||
|
||||
GraphicsContext gc = getGraphicsContext2D();
|
||||
gc.clearRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResizable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double prefWidth(double height) {
|
||||
return getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double prefHeight(double width) {
|
||||
return getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
private void drawFps(int fps){
|
||||
if (raceViewController.isDisplayFps()){
|
||||
gc.setFill(Color.BLACK);
|
||||
gc.setFont(new Font(14));
|
||||
gc.setLineWidth(3);
|
||||
gc.fillText(fps + " FPS", 5, 20);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws all the boats.
|
||||
*/
|
||||
private void drawBoats() {
|
||||
Map<Boat, TimelineInfo> timelineInfos = raceViewController.getTimelineInfos();
|
||||
for (Boat boat : timelineInfos.keySet()) {
|
||||
TimelineInfo timelineInfo = timelineInfos.get(boat);
|
||||
|
||||
boat.setLocation(timelineInfo.getY().doubleValue(), timelineInfo.getX().doubleValue());
|
||||
|
||||
drawBoat(boat.getLongitude(), boat.getLatitude(), boat.getColor(), boat.getShortName(), boat.getSpeedInKnots(), boat.getHeading());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the wake line behind a boat
|
||||
* @param gc The graphics context used for drawing the wake
|
||||
* @param x the x position of the boat
|
||||
* @param y the y position of the boat
|
||||
* @param speed the speed of the boat
|
||||
* @param color the color of the wake line
|
||||
* @param heading the heading of the boat
|
||||
*/
|
||||
private void drawWake(GraphicsContext gc, double x, double y, double speed, Color color, double heading){
|
||||
double angle = Math.toRadians(heading);
|
||||
speed = speed * 2;
|
||||
Point newP = new Point(0, speed);
|
||||
newP.rotate(angle);
|
||||
|
||||
gc.setStroke(color);
|
||||
gc.setLineWidth(1.0);
|
||||
gc.strokeLine(x, y, newP.x + x, newP.y + y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a boat with given (x, y) position in the given color
|
||||
*
|
||||
* @param lat
|
||||
* @param lon
|
||||
* @param color
|
||||
* @param name
|
||||
* @param speed
|
||||
*/
|
||||
private void drawBoat(double lat, double lon, Color color, String name, double speed, double heading) {
|
||||
// Latitude
|
||||
double x = (lon - ORIGIN_LON) * SCALE;
|
||||
double y = (ORIGIN_LAT - lat) * SCALE;
|
||||
|
||||
gc.setFill(color);
|
||||
|
||||
if (raceViewController.isDisplayAnnotations()) {
|
||||
// Set boat text
|
||||
gc.setFont(new Font(14));
|
||||
gc.setLineWidth(3);
|
||||
gc.fillText(name + ", " + speed + " knots", x + 15, y + 15);
|
||||
}
|
||||
// double diameter = 9;
|
||||
// gc.fillOval(x, y, diameter, diameter);
|
||||
double angle = Math.toRadians(heading);
|
||||
|
||||
Point p1 = new Point(0, -15); // apex point
|
||||
Point p2 = new Point(7, 4); // base point
|
||||
Point p3 = new Point(-7, 4); // base point
|
||||
p1.rotate(angle);
|
||||
p2.rotate(angle);
|
||||
p3.rotate(angle);
|
||||
double[] xx = new double[] {p1.x + x, p2.x + x, x, p3.x + x};
|
||||
double[] yy = new double[] {p1.y + y, p2.y + y, y, p3.y + y};
|
||||
gc.fillPolygon(xx, yy, 4);
|
||||
|
||||
if (raceViewController.isDisplayAnnotations()){
|
||||
drawWake(gc, x, y, speed, color, heading);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class for creating point so that you can rotate it around origin point.
|
||||
*/
|
||||
class Point {
|
||||
|
||||
double x, y;
|
||||
|
||||
Point (double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
void rotate(double angle) {
|
||||
double oldX = x;
|
||||
double oldY = y;
|
||||
this.x = oldX * Math.cos(angle) - oldY * Math.sin(angle);
|
||||
this.y = oldX * Math.sin(angle) + oldY * Math.cos(angle);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the course.
|
||||
*/
|
||||
private void drawCourse() {
|
||||
for (Mark mark : raceViewController.getRace().getCourse()) {
|
||||
if (mark.getMarkType() == MarkType.SINGLE_MARK) {
|
||||
drawSingleMark((SingleMark) mark, Color.BLACK);
|
||||
} else if (mark.getMarkType() == MarkType.GATE_MARK) {
|
||||
drawGateMark((GateMark) mark);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a given mark on canvas
|
||||
*
|
||||
* @param singleMark
|
||||
*/
|
||||
private void drawSingleMark(SingleMark singleMark, Color color) {
|
||||
double x = (singleMark.getLongitude() - ORIGIN_LON) * SCALE;
|
||||
double y = (ORIGIN_LAT - singleMark.getLatitude()) * SCALE;
|
||||
|
||||
gc.setFill(color);
|
||||
gc.fillRect(x,y,5.5,5.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a gate mark which contains two single marks
|
||||
*
|
||||
* @param gateMark
|
||||
*/
|
||||
private void drawGateMark(GateMark gateMark) {
|
||||
Color color = Color.BLUE;
|
||||
|
||||
if (gateMark.getName().equals("Start")){
|
||||
color = Color.RED;
|
||||
}
|
||||
|
||||
if (gateMark.getName().equals("Finish")){
|
||||
color = Color.GREEN;
|
||||
}
|
||||
|
||||
drawSingleMark(gateMark.getSingleMark1(), color);
|
||||
drawSingleMark(gateMark.getSingleMark2(), color);
|
||||
|
||||
GraphicsContext gc = canvas.getGraphicsContext2D();
|
||||
|
||||
gc.setStroke(color);
|
||||
|
||||
// Convert lat/lon to x,y
|
||||
double x1 = (gateMark.getSingleMark1().getLongitude()- ORIGIN_LON) * SCALE;
|
||||
double y1 = (ORIGIN_LAT - gateMark.getSingleMark1().getLatitude()) * SCALE;
|
||||
|
||||
double x2 = (gateMark.getSingleMark2().getLongitude() - ORIGIN_LON) * SCALE;
|
||||
double y2 = (ORIGIN_LAT - gateMark.getSingleMark2().getLatitude()) * SCALE;
|
||||
|
||||
gc.setLineWidth(1);
|
||||
gc.strokeLine(x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package seng302.controllers;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Created by michaelrausch on 21/03/17.
|
||||
*/
|
||||
public class Controller implements Initializable {
|
||||
@FXML
|
||||
private AnchorPane contentPane;
|
||||
|
||||
private void setContentPane(String jfxUrl){
|
||||
try{
|
||||
contentPane.getChildren().removeAll();
|
||||
contentPane.getChildren().clear();
|
||||
contentPane.getChildren().addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl)));
|
||||
}
|
||||
catch(javafx.fxml.LoadException e){
|
||||
System.err.println(e.getCause());
|
||||
}
|
||||
catch(IOException e){
|
||||
System.err.println(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
setContentPane("/views/RaceView.fxml");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package seng302.controllers;
|
||||
|
||||
import seng302.models.Boat;
|
||||
import seng302.models.Race;
|
||||
import seng302.models.parsers.ConfigParser;
|
||||
import seng302.models.parsers.CourseParser;
|
||||
import seng302.models.parsers.TeamsParser;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by zyt10 on 17/03/17.
|
||||
* run before CanvasController to initialize race events
|
||||
* the CanvasController then uses the event data to make the animations
|
||||
*/
|
||||
public class RaceController {
|
||||
Race race = null;
|
||||
|
||||
public void initializeRace() {
|
||||
String raceConfigFile = "/config/config.xml";
|
||||
String teamsConfigFile = "/config/teams.xml";
|
||||
|
||||
try {
|
||||
race = createRace(raceConfigFile, teamsConfigFile);
|
||||
} catch (Exception e) {
|
||||
System.out.println("There was an error creating the race.");
|
||||
}
|
||||
|
||||
if (race != null) {
|
||||
race.startRace();
|
||||
} else {
|
||||
System.out.println("There was an error creating the race. Exiting.");
|
||||
}
|
||||
}
|
||||
|
||||
public Race createRace(String configFile, String teamsConfigFile) throws Exception {
|
||||
Race race = new Race();
|
||||
|
||||
// Read team names from file
|
||||
TeamsParser tp = new TeamsParser(teamsConfigFile);
|
||||
|
||||
// Read course from file
|
||||
ConfigParser config = new ConfigParser(configFile);
|
||||
|
||||
ArrayList<String> boatNames = new ArrayList<>();
|
||||
ArrayList<Boat> teams = tp.getBoats();
|
||||
|
||||
//get race size
|
||||
int numberOfBoats = teams.size();
|
||||
|
||||
//get time scale
|
||||
double timeScale = config.getTimeScale();
|
||||
race.setTimeScale(timeScale);
|
||||
|
||||
for (Boat boat : teams) {
|
||||
boatNames.add(boat.getTeamName());
|
||||
race.addBoat(boat);
|
||||
}
|
||||
|
||||
// Shuffle team names
|
||||
long seed = System.nanoTime();
|
||||
Collections.shuffle(boatNames, new Random(seed));
|
||||
|
||||
if (numberOfBoats > Array.getLength(boatNames.toArray())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CourseParser course = new CourseParser("/config/course.xml");
|
||||
race.addCourse(course.getCourse());
|
||||
|
||||
return race;
|
||||
}
|
||||
|
||||
public Race getRace() {
|
||||
return race;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package seng302.controllers;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Text;
|
||||
import seng302.models.Race;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Created by ptg19 on 20/03/17.
|
||||
*/
|
||||
public class RaceResultController implements Initializable{
|
||||
@FXML private AnchorPane window;
|
||||
@FXML private VBox resultsVBox;
|
||||
private Race race;
|
||||
|
||||
RaceResultController(Race race){
|
||||
this.race = race;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
int boatPosition = this.race.getFinishedBoats().length;
|
||||
|
||||
for (int i = this.race.getFinishedBoats().length - 1; i >= 0; i--){
|
||||
resultsVBox.getChildren().add(0, new Text(boatPosition + ": " + this.race.getFinishedBoats()[i].getTeamName()));
|
||||
boatPosition--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
package seng302.controllers;
|
||||
|
||||
import javafx.animation.Animation;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import javafx.beans.property.SimpleDoubleProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.util.Duration;
|
||||
import seng302.models.Boat;
|
||||
import seng302.models.Event;
|
||||
import seng302.models.Race;
|
||||
import seng302.models.TimelineInfo;
|
||||
import seng302.models.parsers.ConfigParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by ptg19 on 29/03/17.
|
||||
*/
|
||||
public class RaceViewController {
|
||||
@FXML
|
||||
private VBox positionVbox;
|
||||
@FXML
|
||||
private CheckBox toggleAnnotation, toggleFps;
|
||||
@FXML
|
||||
private Text timerLabel;
|
||||
@FXML
|
||||
private AnchorPane contentAnchorPane;
|
||||
@FXML
|
||||
private Text windArrowText, windDirectionText;
|
||||
@FXML
|
||||
private CanvasController includedCanvasController;
|
||||
|
||||
private boolean displayAnnotations;
|
||||
private boolean displayFps;
|
||||
private Timeline timerTimeline;
|
||||
private Map<Boat, TimelineInfo> timelineInfos = new HashMap<>();
|
||||
private ArrayList<Boat> boatOrder = new ArrayList<>();
|
||||
private Race race;
|
||||
|
||||
public void initialize() {
|
||||
includedCanvasController.setup(this);
|
||||
RaceController raceController = new RaceController();
|
||||
raceController.initializeRace();
|
||||
race = raceController.getRace();
|
||||
|
||||
initializeTimer();
|
||||
initializeSettings();
|
||||
try{
|
||||
initializeTimelines();
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
//set wind direction!!!!!!! can't find another place to put my code --haoming
|
||||
double windDirection = new ConfigParser("/config/config.xml").getWindDirection();
|
||||
windDirectionText.setText(String.format("%.1f°", windDirection));
|
||||
windArrowText.setRotate(windDirection);
|
||||
}
|
||||
|
||||
private void initializeSettings(){
|
||||
displayAnnotations = true;
|
||||
displayFps = true;
|
||||
|
||||
toggleAnnotation.selectedProperty().addListener(new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
||||
displayAnnotations = !displayAnnotations;
|
||||
}
|
||||
});
|
||||
toggleFps.selectedProperty().addListener(new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
||||
displayFps = !displayFps;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initializeTimer(){
|
||||
timerTimeline = new Timeline();
|
||||
timerTimeline.setCycleCount(Timeline.INDEFINITE);
|
||||
// Run timer update every second
|
||||
timerTimeline.getKeyFrames().add(
|
||||
new KeyFrame(Duration.seconds(1),
|
||||
event -> {
|
||||
// Stop timer if race is finished
|
||||
if (this.race.isRaceFinished()) {
|
||||
this.timerTimeline.stop();
|
||||
} else {
|
||||
timerLabel.setText(convertTimeToMinutesSeconds(race.getRaceTime()));
|
||||
this.race.incrementRaceTime();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Start the timer
|
||||
timerTimeline.playFromStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates time line for each boat, and stores time time into timelineInfos hash map
|
||||
*/
|
||||
private void initializeTimelines() {
|
||||
HashMap<Boat, List> boat_events = race.getEvents();
|
||||
|
||||
for (Boat boat : boat_events.keySet()) {
|
||||
// x, y are the real time coordinates
|
||||
DoubleProperty x = new SimpleDoubleProperty();
|
||||
DoubleProperty y = new SimpleDoubleProperty();
|
||||
|
||||
List<KeyFrame> keyFrames = new ArrayList<>();
|
||||
List<Event> events = boat_events.get(boat);
|
||||
|
||||
// iterates all events and convert each event to keyFrame, then add them into a list
|
||||
for (Event event : events) {
|
||||
if (event.getIsFinishingEvent()) {
|
||||
keyFrames.add(
|
||||
new KeyFrame(Duration.seconds(event.getTime()),
|
||||
onFinished -> {race.setBoatFinished(boat); handleEvent(event);},
|
||||
new KeyValue(x, event.getThisMark().getLatitude()),
|
||||
new KeyValue(y, event.getThisMark().getLongitude())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
keyFrames.add(
|
||||
new KeyFrame(Duration.seconds(event.getTime()),
|
||||
onFinished ->{
|
||||
handleEvent(event);
|
||||
boat.setHeading(event.getBoatHeading());
|
||||
},
|
||||
new KeyValue(x, event.getThisMark().getLatitude()),
|
||||
new KeyValue(y, event.getThisMark().getLongitude())
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
timelineInfos.put(boat, new TimelineInfo(new Timeline(keyFrames.toArray(new KeyFrame[keyFrames.size()])), x, y));
|
||||
}
|
||||
setRaceDuration();
|
||||
}
|
||||
|
||||
private void setRaceDuration(){
|
||||
Double maxDuration = 0.0;
|
||||
Timeline maxTimeline = null;
|
||||
|
||||
for (TimelineInfo timelineInfo : timelineInfos.values()) {
|
||||
|
||||
Timeline timeline = timelineInfo.getTimeline();
|
||||
if (timeline.getTotalDuration().toMillis() >= maxDuration) {
|
||||
maxDuration = timeline.getTotalDuration().toMillis();
|
||||
maxTimeline = timeline;
|
||||
}
|
||||
|
||||
// Timelines are paused by default
|
||||
timeline.play();
|
||||
timeline.pause();
|
||||
}
|
||||
|
||||
maxTimeline.setOnFinished(event -> {
|
||||
race.setRaceFinished();
|
||||
loadRaceResultView();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Play each boats timerTimeline
|
||||
*/
|
||||
public void playTimelines(){
|
||||
for (TimelineInfo timelineInfo : timelineInfos.values()){
|
||||
Timeline timeline = timelineInfo.getTimeline();
|
||||
|
||||
if (timeline.getStatus() == Animation.Status.PAUSED){
|
||||
timeline.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause each boats timerTimeline
|
||||
*/
|
||||
public void pauseTimelines(){
|
||||
for (TimelineInfo timelineInfo : timelineInfos.values()){
|
||||
Timeline timeline = timelineInfo.getTimeline();
|
||||
|
||||
if (timeline.getStatus() == Animation.Status.RUNNING){
|
||||
timeline.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the list of boats in the order they finished the race
|
||||
*/
|
||||
private void loadRaceResultView() {
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/FinishView.fxml"));
|
||||
loader.setController(new RaceResultController(race));
|
||||
|
||||
try {
|
||||
contentAnchorPane.getChildren().removeAll();
|
||||
contentAnchorPane.getChildren().clear();
|
||||
contentAnchorPane.getChildren().addAll((Pane) loader.load());
|
||||
|
||||
} catch (javafx.fxml.LoadException e) {
|
||||
System.err.println(e.getCause());
|
||||
} catch (IOException e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleEvent(Event event) {
|
||||
Boat boat = event.getBoat();
|
||||
boatOrder.remove(boat);
|
||||
boat.setMarkLastPast(event.getMarkPosInRace());
|
||||
boatOrder.add(boat);
|
||||
boatOrder.sort(new Comparator<Boat>() {
|
||||
@Override
|
||||
public int compare(Boat b1, Boat b2) {
|
||||
return b2.getMarkLastPast() - b1.getMarkLastPast();
|
||||
}
|
||||
});
|
||||
showOrder();
|
||||
}
|
||||
|
||||
private void showOrder() {
|
||||
positionVbox.getChildren().clear();
|
||||
positionVbox.getChildren().removeAll();
|
||||
|
||||
for (Boat boat : boatOrder) {
|
||||
positionVbox.getChildren().add(new Text(boat.getShortName() + " " + boat.getSpeedInKnots() + " Knots"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert seconds to a string of the format mm:ss
|
||||
*
|
||||
* @param time the time in seconds
|
||||
* @return a formatted string
|
||||
*/
|
||||
public String convertTimeToMinutesSeconds(int time) {
|
||||
if (time < 0) {
|
||||
return String.format("-%02d:%02d", (time * -1) / 60, (time * -1) % 60);
|
||||
}
|
||||
return String.format("%02d:%02d", time / 60, time % 60);
|
||||
}
|
||||
|
||||
public void stopTimer() {
|
||||
timerTimeline.stop();
|
||||
}
|
||||
public void startTimer() {
|
||||
timerTimeline.play();
|
||||
}
|
||||
|
||||
public boolean isDisplayFps() {
|
||||
return displayFps;
|
||||
}
|
||||
|
||||
public boolean isDisplayAnnotations() {
|
||||
return displayAnnotations;
|
||||
}
|
||||
|
||||
public Race getRace() {
|
||||
return race;
|
||||
}
|
||||
|
||||
public Map<Boat, TimelineInfo> getTimelineInfos() {
|
||||
return timelineInfos;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package seng302.models;
|
||||
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
/**
|
||||
* Represents a boat in the race.
|
||||
*/
|
||||
public class Boat {
|
||||
|
||||
private String teamName; // The name of the team, this is also the name of the boat
|
||||
private double velocity; // In meters/second
|
||||
private double lat; // Boats position
|
||||
private double lon; // -
|
||||
private double distanceToNextMark;
|
||||
private Color color;
|
||||
private int markLastPast;
|
||||
private double heading;
|
||||
private String shortName;
|
||||
|
||||
public Boat(String teamName) {
|
||||
this.teamName = teamName;
|
||||
this.velocity = 10; // Default velocity
|
||||
this.lat = 0.0;
|
||||
this.lon = 0.0;
|
||||
this.distanceToNextMark = 0.0;
|
||||
this.shortName = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a boat in the race.
|
||||
*
|
||||
* @param teamName The name of the team sailing the boat
|
||||
* @param boatVelocity The speed of the boat in meters/second
|
||||
* @param shortName A shorter version of the teams name
|
||||
*/
|
||||
public Boat(String teamName, double boatVelocity, String shortName) {
|
||||
this.teamName = teamName;
|
||||
this.velocity = boatVelocity;
|
||||
this.distanceToNextMark = 0.0;
|
||||
this.color = Colors.getColor();
|
||||
this.shortName = shortName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the team sailing the boat
|
||||
*
|
||||
* @return The name of the team
|
||||
*/
|
||||
public String getTeamName() {
|
||||
return this.teamName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the team sailing the boat
|
||||
*
|
||||
* @param teamName The name of the team
|
||||
*/
|
||||
public void setTeamName(String teamName) {
|
||||
this.teamName = teamName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets velocity of the boat
|
||||
*
|
||||
* @return a float number of the boat velocity
|
||||
*/
|
||||
public double getVelocity() {
|
||||
return this.velocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets velocity of the boat
|
||||
*
|
||||
* @param velocity The velocity of boat
|
||||
*/
|
||||
public void setVelocity(double velocity) {
|
||||
this.velocity = velocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the boats location
|
||||
*
|
||||
* @param lat, the boats latitude
|
||||
* @param lon, the boats longitude
|
||||
*/
|
||||
public void setLocation(double lat, double lon) {
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
}
|
||||
|
||||
public void setDistanceToNextMark(double distance){
|
||||
this.distanceToNextMark = distance;
|
||||
}
|
||||
|
||||
public double getLatitude(){
|
||||
return this.lat;
|
||||
}
|
||||
|
||||
public double getLongitude(){
|
||||
return this.lon;
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public double getSpeedInKnots(){
|
||||
return Math.round((this.velocity * 1.94384) * 100d) / 100d;
|
||||
}
|
||||
|
||||
public void setMarkLastPast(int markLastPast) {
|
||||
this.markLastPast = markLastPast;
|
||||
}
|
||||
|
||||
public int getMarkLastPast() {
|
||||
return markLastPast;
|
||||
}
|
||||
|
||||
public void setHeading(double heading){
|
||||
this.heading = heading;
|
||||
}
|
||||
|
||||
public double getHeading(){
|
||||
return this.heading;
|
||||
}
|
||||
|
||||
public String getShortName(){
|
||||
return this.shortName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package seng302.models;
|
||||
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
/**
|
||||
* Created by ryan_ on 16/03/2017.
|
||||
*/
|
||||
public enum Colors {
|
||||
RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE;
|
||||
|
||||
static Integer index = 0;
|
||||
|
||||
public static Color getColor() {
|
||||
index++;
|
||||
if (index > 6) {
|
||||
index = 1;
|
||||
}
|
||||
return Color.valueOf(values()[index-1].toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package seng302.models;
|
||||
|
||||
import seng302.models.mark.Mark;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Event class containing the time of specific event, related team/boat, and
|
||||
* event location such as leg.
|
||||
*/
|
||||
public class Event {
|
||||
private Double time; // Time the event occurs
|
||||
private Boat boat;
|
||||
private boolean isFinishingEvent = false; // This event occurs when a boat finishes the race
|
||||
private Mark mark1; // This mark
|
||||
private Mark mark2; // Next mark
|
||||
private int markPosInRace; // the position of the current mark in the race course
|
||||
|
||||
private final double ORIGIN_LAT = 32.320504;
|
||||
private final double ORIGIN_LON = -64.857063;
|
||||
private final double SCALE = 16000;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Event class containing the time of specific event, related team/boat, and
|
||||
* event location such as leg.
|
||||
*
|
||||
* @param eventTime, what time the event happens
|
||||
* @param eventBoat, the boat that the event belongs to
|
||||
*/
|
||||
public Event(Double eventTime, Boat eventBoat, Mark mark1, Mark mark2, int markPosInRace) {
|
||||
this.time = eventTime;
|
||||
this.boat = eventBoat;
|
||||
this.mark1 = mark1;
|
||||
this.mark2 = mark2;
|
||||
this.markPosInRace = markPosInRace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event class containing the time of specific event, related team/boat, and
|
||||
* event location such as leg.
|
||||
*
|
||||
* @param eventTime, what time the event happens
|
||||
* @param eventBoat, the boat that the event belongs to
|
||||
*/
|
||||
public Event(Double eventTime, Boat eventBoat, Mark mark1, int markPosInRace) {
|
||||
this.time = eventTime;
|
||||
this.boat = eventBoat;
|
||||
this.mark1 = mark1;
|
||||
this.markPosInRace = markPosInRace;
|
||||
this.isFinishingEvent = true;
|
||||
}
|
||||
|
||||
public double getTime() {
|
||||
return this.time;
|
||||
}
|
||||
|
||||
public void setTime(double eventTime) {
|
||||
this.time = eventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time in a formatted string
|
||||
*
|
||||
* @return the string of time
|
||||
*/
|
||||
public String getTimeString() {
|
||||
return (new SimpleDateFormat("mm:ss:SSS")).format(new Date(time.longValue()));
|
||||
}
|
||||
|
||||
public Boat getBoat() {
|
||||
return this.boat;
|
||||
}
|
||||
|
||||
public void setBoat(Boat eventBoat) {
|
||||
this.boat = eventBoat;
|
||||
}
|
||||
|
||||
public boolean getIsFinishingEvent() {
|
||||
return this.isFinishingEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string that contains the timestamp and course information for this event
|
||||
*
|
||||
* @return A string that details what happened in this event
|
||||
*/
|
||||
public String getEventString() {
|
||||
// This event is a boat finishing the race
|
||||
if (this.isFinishingEvent) {
|
||||
return (this.getTimeString() + ", " + this.getBoat().getTeamName() + " finished the race");
|
||||
}
|
||||
System.out.println(this.getDistanceBetweenMarks());
|
||||
return (this.getTimeString() + ", " + this.getBoat().getTeamName() + " passed " + this.mark1.getName() + " going heading " + this.getBoatHeading() + "°");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the distance between the two marks
|
||||
*/
|
||||
public double getDistanceBetweenMarks() {
|
||||
double earth_radius = 6378.137;
|
||||
double dLat = this.mark2.getLatitude() * Math.PI / 180 - this.mark1.getLatitude() * Math.PI / 180;
|
||||
double dLon = this.mark2.getLongitude() * Math.PI / 180 - this.mark1.getLongitude() * Math.PI / 180;
|
||||
|
||||
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(this.mark1.getLatitude() * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
double d = earth_radius * c;
|
||||
|
||||
return d * 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates current boat heading direction.
|
||||
* @return the boats heading as degree. vertical upward is 0 degree, and degree goes up clockwise.
|
||||
*/
|
||||
public double getBoatHeading() {
|
||||
if (mark2 == null){
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double x1 = (mark1.getLongitude() - ORIGIN_LON) * SCALE;
|
||||
double y1 = (ORIGIN_LAT - mark1.getLatitude()) * SCALE;
|
||||
double x2 = (mark2.getLongitude() - ORIGIN_LON) * SCALE;
|
||||
double y2 = (ORIGIN_LAT - mark2.getLatitude()) * SCALE;
|
||||
|
||||
double headingRadians = Math.atan2(y2-y1, x2-x1);
|
||||
|
||||
if (headingRadians < 0){
|
||||
headingRadians += 2 * Math.PI;
|
||||
}
|
||||
|
||||
// Convert back to degrees, and flip 180 degrees
|
||||
// return ((headingRadians) * 180) / Math.PI;
|
||||
return (Math.toDegrees(headingRadians) + 90) % 360;
|
||||
|
||||
}
|
||||
|
||||
public Mark getThisMark() {
|
||||
return this.mark1;
|
||||
}
|
||||
|
||||
public int getMarkPosInRace() {
|
||||
return markPosInRace;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
package seng302;
|
||||
package seng302.models;
|
||||
|
||||
import seng302.models.mark.SingleMark;
|
||||
|
||||
/**
|
||||
* Represents the leg of a race.
|
||||
@@ -7,19 +9,19 @@ public class Leg {
|
||||
private int heading;
|
||||
private int distance;
|
||||
private boolean isFinishingLeg;
|
||||
private Marker startingMarker;
|
||||
private SingleMark startingSingleMark;
|
||||
|
||||
/**
|
||||
* Create a new leg
|
||||
*
|
||||
* @param heading, the magnetic heading of this leg
|
||||
* @param distance, the total distance of this leg in meters
|
||||
* @param marker, the marker this leg starts on
|
||||
* @param singleMark, the singleMark this leg starts on
|
||||
*/
|
||||
public Leg(int heading, int distance, Marker marker) {
|
||||
public Leg(int heading, int distance, SingleMark singleMark) {
|
||||
this.heading = heading;
|
||||
this.distance = distance;
|
||||
this.startingMarker = marker;
|
||||
this.startingSingleMark = singleMark;
|
||||
this.isFinishingLeg = false;
|
||||
}
|
||||
|
||||
@@ -33,7 +35,7 @@ public class Leg {
|
||||
public Leg(int heading, int distance, String markerName) {
|
||||
this.heading = heading;
|
||||
this.distance = distance;
|
||||
this.startingMarker = new Marker(markerName);
|
||||
this.startingSingleMark = new SingleMark(markerName);
|
||||
this.isFinishingLeg = false;
|
||||
}
|
||||
|
||||
@@ -68,30 +70,25 @@ public class Leg {
|
||||
/**
|
||||
* Returns the marker this leg started on
|
||||
*/
|
||||
public Marker getMarker() {
|
||||
return this.startingMarker;
|
||||
public SingleMark getMarker() {
|
||||
return this.startingSingleMark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the marker this leg starts on
|
||||
* Set the singleMark this leg starts on
|
||||
*/
|
||||
public void setMarker(Marker marker) {
|
||||
this.startingMarker = marker;
|
||||
public void setMarker(SingleMark singleMark) {
|
||||
this.startingSingleMark = singleMark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the marker this leg started on
|
||||
*/
|
||||
public String getMarkerLabel() {
|
||||
return this.startingMarker.getName();
|
||||
return this.startingSingleMark.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the marker that the boat has passed it
|
||||
*/
|
||||
public void addBoatToMarker(Boat boat) {
|
||||
this.startingMarker.addBoat(boat);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify whether or not the race finishes on this leg
|
||||
@@ -0,0 +1,197 @@
|
||||
package seng302.models;
|
||||
|
||||
import seng302.models.mark.Mark;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Race class containing the boats and legs in the race
|
||||
* Created by mra106 on 8/3/2017.
|
||||
*/
|
||||
public class Race {
|
||||
private ArrayList<Boat> boats; // The boats in the race
|
||||
private ArrayList<Boat> finishingOrder; // The order in which the boats finish the race
|
||||
private HashMap<Boat, List> events = new HashMap<>(); // The events that occur in the race
|
||||
private List<Mark> course; // Marks in the race
|
||||
private long startTime = 0;
|
||||
private double timeScale = 1;
|
||||
private boolean raceFinished = false; // Race is finished
|
||||
private int raceTime = -2; // Current time in the race
|
||||
|
||||
/**
|
||||
* Race class containing the boats and legs in the race
|
||||
*/
|
||||
public Race() {
|
||||
this.boats = new ArrayList<>();
|
||||
this.finishingOrder = new ArrayList<>();
|
||||
this.course = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boat to the race
|
||||
*
|
||||
* @param boat, the boat to add
|
||||
*/
|
||||
public void addBoat(Boat boat) {
|
||||
boats.add(boat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of boats in a random order
|
||||
*
|
||||
* @returns a list of boats
|
||||
*/
|
||||
public Boat[] getShuffledBoats() {
|
||||
// Shuffle the list of boats
|
||||
long seed = System.nanoTime();
|
||||
Collections.shuffle(this.boats, new Random(seed));
|
||||
|
||||
return boats.toArray(new Boat[boats.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of boats in the order that they
|
||||
* finished the race (position 0 is first place)
|
||||
*
|
||||
* @returns a list of boats
|
||||
*/
|
||||
public Boat[] getFinishedBoats() {
|
||||
return this.finishingOrder.toArray(new Boat[this.finishingOrder.size()]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of boats in the race
|
||||
*
|
||||
* @return a list of the boats competing in the race
|
||||
*/
|
||||
public Boat[] getBoats() {
|
||||
return boats.toArray(new Boat[boats.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets time scale
|
||||
*
|
||||
* @param timeScale
|
||||
*/
|
||||
public void setTimeScale(double timeScale) {
|
||||
this.timeScale = timeScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate all events that will happen during the race.
|
||||
*/
|
||||
private void generateEvents() {
|
||||
|
||||
for (Boat boat : this.boats) {
|
||||
double totalDistance = 0;
|
||||
int numberOfMarks = this.course.size();
|
||||
|
||||
for (int i = 0; i < numberOfMarks; i++) {
|
||||
Double time = (totalDistance / boat.getVelocity() / timeScale);
|
||||
|
||||
// If there are singleMarks after this event
|
||||
if (i < numberOfMarks - 1) {
|
||||
Event event = new Event(time, boat, course.get(i), course.get(i + 1), i);
|
||||
|
||||
try {
|
||||
events.get(boat).add(event);
|
||||
|
||||
} catch (NullPointerException e) {
|
||||
events.put(boat, new ArrayList<>(Arrays.asList(event)));
|
||||
}
|
||||
totalDistance += event.getDistanceBetweenMarks();
|
||||
System.out.println(totalDistance);
|
||||
System.out.println(boat.getVelocity());
|
||||
}
|
||||
|
||||
// There are no more marks after this event
|
||||
|
||||
else{
|
||||
Event event = new Event(time, boat, course.get(i), i);
|
||||
events.get(boat).add(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts a race and generates all events for the race.
|
||||
*/
|
||||
public void startRace() {
|
||||
// record start time.
|
||||
this.startTime = System.currentTimeMillis();
|
||||
generateEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the race course
|
||||
* @param course a list of marks in the course
|
||||
*/
|
||||
public void addCourse(List<Mark> course) {
|
||||
this.course = course;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of marks in the course
|
||||
* @return
|
||||
*/
|
||||
public List<Mark> getCourse() {
|
||||
return course;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map of the events in the race
|
||||
* @return
|
||||
*/
|
||||
public HashMap<Boat, List> getEvents() {
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a boat as finished
|
||||
* @param boat The boat that has finished the race
|
||||
*/
|
||||
public void setBoatFinished(Boat boat){
|
||||
this.finishingOrder.add(boat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the race as finished
|
||||
*/
|
||||
public void setRaceFinished(){
|
||||
this.raceFinished = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether or not the race is finished
|
||||
* @return true if the race is finished
|
||||
*/
|
||||
public boolean isRaceFinished(){
|
||||
return this.raceFinished;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the race time
|
||||
* @param raceTime the race time in seconds
|
||||
*/
|
||||
public void setRaceTime(int raceTime){
|
||||
this.raceTime = raceTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the race time
|
||||
* @return the race time in seconds
|
||||
*/
|
||||
public int getRaceTime(){
|
||||
return this.raceTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the race time by one second
|
||||
*/
|
||||
public void incrementRaceTime(){
|
||||
this.raceTime += this.timeScale;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package seng302.models;
|
||||
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
|
||||
|
||||
/**
|
||||
* Created by zyt10 on 17/03/17.
|
||||
* this class is literally just to associate a timeline with a DoubleProperty x and y
|
||||
*/
|
||||
public class TimelineInfo {
|
||||
private Timeline timeline;
|
||||
private DoubleProperty x;
|
||||
private DoubleProperty y;
|
||||
|
||||
public TimelineInfo(Timeline timeline, DoubleProperty x, DoubleProperty y) {
|
||||
this.timeline = timeline;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public Timeline getTimeline() {
|
||||
return timeline;
|
||||
}
|
||||
public DoubleProperty getX() {
|
||||
return x;
|
||||
}
|
||||
public DoubleProperty getY() {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package seng302.models.mark;
|
||||
|
||||
/**
|
||||
* To represent a gate mark which contains two single marks.
|
||||
* Created by ptg19 on 16/03/17.
|
||||
* Modified by Haoming Yin (hyi25) on 17/3/2017.
|
||||
*/
|
||||
public class GateMark extends Mark {
|
||||
|
||||
private SingleMark singleMark1;
|
||||
private SingleMark singleMark2;
|
||||
|
||||
/**
|
||||
* Create an instance of Gate Mark which contains two single mark
|
||||
* @param name the name of the gate mark
|
||||
* @param singleMark1 one single mark inside of the gate mark
|
||||
* @param singleMark2 the second mark inside of the gate mark
|
||||
*/
|
||||
public GateMark(String name, SingleMark singleMark1, SingleMark singleMark2, double latitude, double longitude) {
|
||||
super(name, MarkType.GATE_MARK, latitude, longitude);
|
||||
this.singleMark1 = singleMark1;
|
||||
this.singleMark2 = singleMark2;
|
||||
}
|
||||
|
||||
public SingleMark getSingleMark1() {
|
||||
return singleMark1;
|
||||
}
|
||||
|
||||
public void setSingleMark1(SingleMark singleMark1) {
|
||||
this.singleMark1 = singleMark1;
|
||||
}
|
||||
|
||||
public SingleMark getSingleMark2() {
|
||||
return singleMark2;
|
||||
}
|
||||
|
||||
public void setSingleMark2(SingleMark singleMark2) {
|
||||
this.singleMark2 = singleMark2;
|
||||
}
|
||||
|
||||
public double getLatitude(){
|
||||
//return (this.getSingleMark1().getLatitude() + this.getSingleMark2().getLatitude()) / 2;
|
||||
return (this.getSingleMark1().getLatitude());
|
||||
}
|
||||
|
||||
public double getLongitude(){
|
||||
//return (this.getSingleMark1().getLongitude() + this.getSingleMark2().getLongitude()) / 2;
|
||||
return (this.getSingleMark1().getLongitude());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package seng302.models.mark;
|
||||
|
||||
/**
|
||||
* An abstract class to represent general marks
|
||||
* Created by Haoming Yin (hyi25) on 17/3/17.
|
||||
*/
|
||||
public abstract class Mark {
|
||||
|
||||
private String name;
|
||||
private MarkType markType;
|
||||
private double latitude;
|
||||
private double longitude;
|
||||
|
||||
/**
|
||||
* Create a mark instance by passing its name and type
|
||||
* @param name the name of the mark
|
||||
* @param markType the type of mark. either GATE_MARK or SINGLE_MARK.
|
||||
*/
|
||||
public Mark (String name, MarkType markType) {
|
||||
this.name = name;
|
||||
this.markType = markType;
|
||||
}
|
||||
|
||||
public Mark(String name, MarkType markType, double latitude, double longitude) {
|
||||
this.name = name;
|
||||
this.markType = markType;
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public MarkType getMarkType() {
|
||||
return markType;
|
||||
}
|
||||
|
||||
public void setMarkType(MarkType markType) {
|
||||
this.markType = markType;
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package seng302.models.mark;
|
||||
|
||||
/**
|
||||
* To represent two types of mark
|
||||
* Created by Haoming Yin (hyi25) on 17/3/17.
|
||||
*/
|
||||
public enum MarkType {
|
||||
SINGLE_MARK, GATE_MARK
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package seng302.models.mark;
|
||||
|
||||
/**
|
||||
* Represents the marker as a single mark
|
||||
*
|
||||
* Created by Haoming Yin (hyi25) on 17/3/2017
|
||||
*/
|
||||
public class SingleMark extends Mark {
|
||||
private double lat;
|
||||
private double lon;
|
||||
private String name;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a marker
|
||||
*
|
||||
* @param name, the name of the marker*
|
||||
* @param lat, the latitude of the marker
|
||||
* @param lon, the longitude of the marker
|
||||
*/
|
||||
public SingleMark(String name, double lat, double lon) {
|
||||
super(name, MarkType.SINGLE_MARK);
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the marker at the beginning of a leg
|
||||
*
|
||||
* @param name, the name of the marker
|
||||
*/
|
||||
public SingleMark(String name) {
|
||||
super(name, MarkType.SINGLE_MARK);
|
||||
this.lat = 0;
|
||||
this.lon = 0;
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return this.lat;
|
||||
}
|
||||
|
||||
public double getLongitude() {
|
||||
return this.lon;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package seng302.models.parsers;
|
||||
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import java.util.DoubleSummaryStatistics;
|
||||
|
||||
public class ConfigParser extends FileParser {
|
||||
|
||||
private Document doc;
|
||||
|
||||
public ConfigParser(String path) {
|
||||
super(path);
|
||||
this.doc = this.parseFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets wind direction from config file.
|
||||
*
|
||||
* @return a double type degree, or 0 if no value or invalid value is found
|
||||
*/
|
||||
public double getWindDirection() {
|
||||
return getDoubleByTagName("wind-direction", 0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a non negative time scale for the race
|
||||
*
|
||||
* @return a double type scale, or 0 if no scale or invalid scale is found
|
||||
*/
|
||||
public double getTimeScale() {
|
||||
return getDoubleByTagName("time-scale", 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a double type number by given tag name found in xml file
|
||||
*
|
||||
* @param tagName a string of tag name
|
||||
* @param defaultVal value returned if no value or invalid value is found
|
||||
* @return value found
|
||||
*/
|
||||
public double getDoubleByTagName(String tagName, double defaultVal) {
|
||||
double val = defaultVal;
|
||||
try {
|
||||
Node node = this.doc.getElementsByTagName(tagName).item(0);
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element element = (Element) node;
|
||||
val = Double.valueOf(element.getTextContent());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} finally {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string by given tag name found in xml file
|
||||
*
|
||||
* @param tagName a string of tag name
|
||||
* @param defaultVal a string returned if no value or invalid value is found
|
||||
* @return string found
|
||||
*/
|
||||
public String getStringByTagName(String tagName, String defaultVal) {
|
||||
String string = defaultVal;
|
||||
try {
|
||||
Node node = this.doc.getElementsByTagName(tagName).item(0);
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element element = (Element) node;
|
||||
string = element.getTextContent();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} finally {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package seng302.models.parsers;
|
||||
|
||||
import org.w3c.dom.*;
|
||||
import seng302.models.mark.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* parse a course xml file
|
||||
* Created by Haoming Yin (hyi25) on 16/3/2017
|
||||
*/
|
||||
public class CourseParser extends FileParser {
|
||||
|
||||
private Document doc;
|
||||
private HashMap<String, Mark> marks = new HashMap<>();
|
||||
|
||||
public CourseParser(String path) {
|
||||
super(path);
|
||||
this.doc = this.parseFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* create a mark by given node
|
||||
*
|
||||
* @param node
|
||||
* @return a mark, or null if fails to create a mark
|
||||
*/
|
||||
private SingleMark generateSingleMark(Node node) {
|
||||
try {
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element element = (Element) node;
|
||||
String name = element.getElementsByTagName("name").item(0).getTextContent();
|
||||
double lat = Double.valueOf(element.getElementsByTagName("latitude").item(0).getTextContent());
|
||||
double lon = Double.valueOf(element.getElementsByTagName("longitude").item(0).getTextContent());
|
||||
SingleMark singleMark = new SingleMark(name, lat, lon);
|
||||
return singleMark;
|
||||
} else {
|
||||
throw new NoSuchElementException("Cannot generate a mark by given node.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generate an arrayList of gates
|
||||
*
|
||||
* @return an arrayList of gates, or null if no gate has been found.
|
||||
*/
|
||||
private void generateGateMarks() {
|
||||
ArrayList<GateMark> gateMarks = new ArrayList<>();
|
||||
|
||||
try {
|
||||
NodeList nodes = doc.getElementsByTagName("gate");
|
||||
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
Node node = nodes.item(i);
|
||||
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element element = (Element) node;
|
||||
String name = element.getElementsByTagName("name").item(0).getTextContent();
|
||||
SingleMark mark1 = generateSingleMark(element.getElementsByTagName("mark").item(0));
|
||||
SingleMark mark2 = generateSingleMark(element.getElementsByTagName("mark").item(1));
|
||||
GateMark gateMark = new GateMark(name, mark1, mark2, mark1.getLatitude(), mark1.getLongitude());
|
||||
marks.put(name, gateMark);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generate an arrayList of marks
|
||||
*
|
||||
* @return an arrayList of marks, or null if no gate has been found.
|
||||
*/
|
||||
private void generateSingleMarks() {
|
||||
ArrayList<SingleMark> singleMarks = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// find the "marks" tag
|
||||
Node node = doc.getElementsByTagName("marks").item(0);
|
||||
// iterate all "marks"'s children
|
||||
for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) {
|
||||
// if node's tag name is "mark"
|
||||
if (n.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element element = (Element) n;
|
||||
if (element.getNodeName() == "mark") {
|
||||
Mark mark = generateSingleMark(n);
|
||||
marks.put(mark.getName(), mark);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return the order of all the marks along a course
|
||||
*
|
||||
* @return an arrayList of the names of ordered course marks
|
||||
*/
|
||||
private ArrayList<String> getOrder() {
|
||||
ArrayList<String> markOrder = new ArrayList<>();
|
||||
|
||||
try {
|
||||
Node orderNode = doc.getElementsByTagName("order").item(0);
|
||||
for (Node node = orderNode.getFirstChild(); node != null; node = node.getNextSibling()) {
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element element = (Element) node;
|
||||
String name = element.getTextContent();
|
||||
markOrder.add(name);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return markOrder;
|
||||
}
|
||||
|
||||
public ArrayList<Mark> getCourse() {
|
||||
generateSingleMarks();
|
||||
generateGateMarks();
|
||||
ArrayList<Mark> course = new ArrayList<>();
|
||||
try {
|
||||
for (String mark : getOrder()) {
|
||||
course.add(marks.get(mark));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return course;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package seng302.models.parsers;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Created by Haoming Yin (hyi25) on 16/3/2017
|
||||
*/
|
||||
public abstract class FileParser {
|
||||
|
||||
private String filePath;
|
||||
|
||||
public FileParser(String path) {
|
||||
this.filePath = path;
|
||||
}
|
||||
|
||||
protected Document parseFile() {
|
||||
try {
|
||||
InputStream is = getClass().getResourceAsStream(this.filePath);
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(is);
|
||||
// optional, in order to recover info from broken line.
|
||||
doc.getDocumentElement().normalize();
|
||||
return doc;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package seng302.models.parsers;
|
||||
|
||||
import org.w3c.dom.*;
|
||||
import seng302.models.Boat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class TeamsParser extends FileParser {
|
||||
|
||||
private Document doc;
|
||||
|
||||
public TeamsParser(String path) {
|
||||
super(path);
|
||||
this.doc = this.parseFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a boat instance by a given team node
|
||||
* @param node a boat node containing name, alias and velocity
|
||||
* @return an instance of Boat
|
||||
*/
|
||||
private Boat parseBoat(Node node) {
|
||||
try {
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element element = (Element) node;
|
||||
String name = element.getElementsByTagName("name").item(0).getTextContent();
|
||||
String alias = element.getElementsByTagName("alias").item(0).getTextContent();
|
||||
double velocity = Double.valueOf(element.getElementsByTagName("velocity").item(0).getTextContent());
|
||||
Boat boat = new Boat(name, velocity, alias);
|
||||
return boat;
|
||||
} else {
|
||||
throw new NoSuchElementException("Cannot generate a boat by given node");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an arraylist of boats instance.
|
||||
* @return an arraylist of boats in teams file
|
||||
*/
|
||||
public ArrayList<Boat> getBoats() {
|
||||
ArrayList<Boat> boats = new ArrayList<>();
|
||||
try {
|
||||
NodeList nodes = this.doc.getElementsByTagName("team");
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
Node node = nodes.item(i);
|
||||
|
||||
boats.add(parseBoat(node));
|
||||
}
|
||||
return boats;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<configurations>
|
||||
<race-name>AC35</race-name>
|
||||
<race-size>6</race-size>
|
||||
<time-scale>10.0</time-scale>
|
||||
<wind-direction>135</wind-direction>
|
||||
</configurations>
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<course>
|
||||
<marks>
|
||||
<gate>
|
||||
<name type="start-line">Start</name>
|
||||
<mark>
|
||||
<name>Start1</name>
|
||||
<latitude>32.296577</latitude>
|
||||
<longitude>-64.854304</longitude>
|
||||
</mark>
|
||||
<mark>
|
||||
<name>Start2</name>
|
||||
<latitude>32.293771</latitude>
|
||||
<longitude>-64.855242</longitude>
|
||||
</mark>
|
||||
</gate>
|
||||
<mark>
|
||||
<name>Mid Mark</name>
|
||||
<latitude>32.293039</latitude>
|
||||
<longitude>-64.843983</longitude>
|
||||
</mark>
|
||||
<gate>
|
||||
<name>Leeward Gate</name>
|
||||
<mark>
|
||||
<name>Leeward Gate1</name>
|
||||
<latitude>32.284680</latitude>
|
||||
<longitude>-64.850045</longitude>
|
||||
</mark>
|
||||
<mark>
|
||||
<name>Leeward Gate2</name>
|
||||
<latitude>32.280164</latitude>
|
||||
<longitude>-64.847591</longitude>
|
||||
</mark>
|
||||
</gate>
|
||||
<gate>
|
||||
<name>Windward Gate</name>
|
||||
<mark>
|
||||
<name>Windward Gate1</name>
|
||||
<latitude>32.309693</latitude>
|
||||
<longitude>-64.835249</longitude>
|
||||
</mark>
|
||||
<mark>
|
||||
<name>Windward Gate2</name>
|
||||
<latitude>32.308046</latitude>
|
||||
<longitude>-64.831785</longitude>
|
||||
</mark>
|
||||
</gate>
|
||||
<gate type="finish-line">
|
||||
<name>Finish</name>
|
||||
<mark>
|
||||
<name>Finish1</name>
|
||||
<latitude>32.317379</latitude>
|
||||
<longitude>-64.839291</longitude>
|
||||
</mark>
|
||||
<mark>
|
||||
<name>Finish2</name>
|
||||
<latitude>32.317257</latitude>
|
||||
<longitude>-64.836260</longitude>
|
||||
</mark>
|
||||
</gate>
|
||||
</marks>
|
||||
<order>
|
||||
<one>Start</one>
|
||||
<two>Mid Mark</two>
|
||||
<three>Leeward Gate</three>
|
||||
<four>Windward Gate</four>
|
||||
<five>Leeward Gate</five>
|
||||
<six>Finish</six>
|
||||
</order>
|
||||
</course>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<teams>
|
||||
<team>
|
||||
<name>Oracle Team USA</name>
|
||||
<alias>USA</alias>
|
||||
<velocity>12.9</velocity>
|
||||
</team>
|
||||
<team>
|
||||
<name>Artemis Racing</name>
|
||||
<alias>ART</alias>
|
||||
<velocity>13.1</velocity>
|
||||
</team>
|
||||
<team>
|
||||
<name>Emirates Team New Zealand</name>
|
||||
<alias>NZL</alias>
|
||||
<velocity>15.6</velocity>
|
||||
</team>
|
||||
<team>
|
||||
<name>Land Rover BAR</name>
|
||||
<alias>BAR</alias>
|
||||
<velocity>13.3</velocity>
|
||||
</team>
|
||||
<team>
|
||||
<name>SoftBank Team Japan</name>
|
||||
<alias>JAP</alias>
|
||||
<velocity>14.7</velocity>
|
||||
</team>
|
||||
<team>
|
||||
<name>Groupama Team France</name>
|
||||
<alias>FRC</alias>
|
||||
<velocity>11.4</velocity>
|
||||
</team>
|
||||
</teams>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
|
||||
<AnchorPane fx:id="canvasPane" prefHeight="960.0" prefWidth="1280.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.CanvasController" />
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import java.lang.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
<AnchorPane fx:id="raceResults" maxHeight="1080.0" maxWidth="1920.0" minHeight="1080.0" minWidth="1920.0" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<GridPane layoutX="444.0" layoutY="256.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="620.1734008789062" minWidth="10.0" prefWidth="493.2829895019531" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="862.0581665039062" minWidth="10.0" prefWidth="786.7170104980469" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints maxHeight="348.0" minHeight="10.0" prefHeight="112.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="348.0" minHeight="10.0" prefHeight="99.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="348.0" minHeight="7.0" prefHeight="88.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="348.0" minHeight="0.0" prefHeight="278.3376770019531" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="653.0" minHeight="0.0" prefHeight="98.66232299804688" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="812.0" minHeight="10.0" prefHeight="418.4577941894531" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Race Results:" wrappingWidth="616.5260620117188" GridPane.rowIndex="2">
|
||||
<font>
|
||||
<Font size="45.0" />
|
||||
</font>
|
||||
<GridPane.margin>
|
||||
<Insets left="50.0" />
|
||||
</GridPane.margin>
|
||||
</Text>
|
||||
<VBox fx:id="resultsVBox" prefHeight="44.0" prefWidth="571.0" GridPane.rowIndex="3">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<GridPane.margin>
|
||||
<Insets left="50.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets top="60.0" />
|
||||
</padding></VBox>
|
||||
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Finish!" wrappingWidth="623.9530334472656" GridPane.rowIndex="1">
|
||||
<font>
|
||||
<Font size="75.0" />
|
||||
</font>
|
||||
<GridPane.margin>
|
||||
<Insets left="50.0" />
|
||||
</GridPane.margin>
|
||||
</Text>
|
||||
</children>
|
||||
</GridPane>
|
||||
</children>
|
||||
</AnchorPane>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.canvas.*?>
|
||||
<?import java.lang.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
<AnchorPane fx:id="contentPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.Controller">
|
||||
<children>
|
||||
<!--<fx:include source="RaceView.fxml" fx:id="raceView"/>-->
|
||||
</children>
|
||||
</AnchorPane>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import javafx.scene.shape.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import java.lang.*?>
|
||||
<?import javafx.scene.canvas.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.canvas.Canvas?>
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
|
||||
<GridPane prefHeight="1080.0" prefWidth="1920.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.controllers.RaceViewController">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints maxWidth="246.0" minWidth="246.0" prefWidth="246.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="1034.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="500.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints />
|
||||
<RowConstraints />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowSpan="3">
|
||||
<children>
|
||||
<Label layoutX="11.0" layoutY="259.0" text="Team Position" />
|
||||
<Label layoutX="13.0" layoutY="432.0" text="Settings" />
|
||||
<Label layoutX="11.0" layoutY="14.0" text="Timer" />
|
||||
<Label layoutX="11.0" layoutY="88.0" text="Wind direction" />
|
||||
<Circle fx:id="windBackgroundCircle" blendMode="DARKEN" fill="#76baf8" layoutX="110.0" layoutY="166.0" radius="35.0" stroke="#686868" strokeType="INSIDE" strokeWidth="3.0" />
|
||||
<Text fx:id="windArrowText" fill="#686868" layoutX="86.0" layoutY="186.0" strokeType="OUTSIDE" strokeWidth="0.0" text="↑">
|
||||
<font>
|
||||
<Font name="AdobeArabic-Regular" size="55.0" />
|
||||
</font>
|
||||
</Text>
|
||||
<Text fx:id="windDirectionText" fill="#a8a7a7" layoutX="171.0" layoutY="214.0" strokeType="OUTSIDE" strokeWidth="0.0" text="0.0°" textAlignment="RIGHT">
|
||||
<font>
|
||||
<Font name="System Bold" size="13.0" />
|
||||
</font>
|
||||
</Text>
|
||||
<CheckBox fx:id="toggleAnnotation" layoutX="27.0" layoutY="462.0" mnemonicParsing="false" selected="true" text="Show annotations" />
|
||||
<CheckBox fx:id="toggleFps" layoutX="27.0" layoutY="488.0" mnemonicParsing="false" prefHeight="18.0" prefWidth="143.0" selected="true" text="Show FPS" />
|
||||
<VBox fx:id="positionVbox" layoutX="12.0" layoutY="280.0" prefHeight="140.0" prefWidth="200.0" />
|
||||
<Pane layoutX="11.0" layoutY="30.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="51.0" prefWidth="193.0">
|
||||
<children>
|
||||
<Text fx:id="timerLabel" layoutX="6.0" layoutY="37.0" strokeType="OUTSIDE" strokeWidth="0.0" text="00:00" textAlignment="CENTER" wrappingWidth="181.0">
|
||||
<font>
|
||||
<Font size="34.0" />
|
||||
</font>
|
||||
</Text>
|
||||
</children>
|
||||
</Pane>
|
||||
</children>
|
||||
</AnchorPane>
|
||||
<AnchorPane fx:id="contentAnchorPane" prefHeight="960.0" prefWidth="1280.0" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2147483647" GridPane.valignment="TOP">
|
||||
<children>
|
||||
<fx:include fx:id="includedCanvas" source="CanvasView.fxml" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
|
||||
</children></AnchorPane>
|
||||
</children>
|
||||
</GridPane>
|
||||
@@ -1,15 +0,0 @@
|
||||
package seng302;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
*/
|
||||
public class AppTest {
|
||||
@Test
|
||||
public void testApp() {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package seng302;
|
||||
|
||||
import org.junit.Test;
|
||||
import seng302.models.Boat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@@ -25,7 +26,7 @@ public class BoatTest {
|
||||
|
||||
@Test
|
||||
public void testSetVelocity() {
|
||||
Boat boat1 = new Boat("Team 1", 29.0);
|
||||
Boat boat1 = new Boat("Team 1", 29.0, "");
|
||||
assertEquals(boat1.getVelocity(), (double) 29.0, 1e-15);
|
||||
|
||||
boat1.setVelocity(12.0);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package seng302;
|
||||
|
||||
import javafx.scene.paint.Color;
|
||||
import org.junit.Test;
|
||||
import seng302.models.Boat;
|
||||
import seng302.models.Colors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Created by ryan_ on 16/03/2017.
|
||||
*/
|
||||
public class ColorsTest {
|
||||
//@Test
|
||||
public void testNextColor() {
|
||||
List<Boat> boats = new ArrayList<>();
|
||||
boats.add(new Boat("Team 1"));
|
||||
boats.add(new Boat("Team 2"));
|
||||
boats.add(new Boat("Team 3"));
|
||||
boats.add(new Boat("Team 4"));
|
||||
boats.add(new Boat("Team 5"));
|
||||
boats.add(new Boat("Team 6"));
|
||||
|
||||
int count = 0;
|
||||
List<Color> enumColors = new ArrayList<>();
|
||||
while (count < 6) {
|
||||
Color color = Colors.getColor();
|
||||
enumColors.add(color);
|
||||
count++;
|
||||
}
|
||||
|
||||
List<Color> boatColors = new ArrayList<>();
|
||||
for (Boat boat : boats) {
|
||||
Color color = boat.getColor();
|
||||
boatColors.add(color);
|
||||
}
|
||||
|
||||
assertEquals(enumColors, boatColors);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package seng302;
|
||||
|
||||
import org.junit.Test;
|
||||
import seng302.models.Boat;
|
||||
import seng302.models.Event;
|
||||
import seng302.models.mark.SingleMark;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@@ -12,29 +15,24 @@ public class EventTest {
|
||||
|
||||
@Test
|
||||
public void getTimeString() throws Exception {
|
||||
Leg leg = new Leg(35, 100, "Start");
|
||||
Boat boat = new Boat("testBoat");
|
||||
Event event = new Event(1231242, boat, leg);
|
||||
Event event = new Event(1231242.2, boat, new SingleMark("mark1"), new SingleMark("mark2"), 0);
|
||||
assertEquals("20:31:242", event.getTimeString());
|
||||
}
|
||||
|
||||
/**
|
||||
* ensure all boats are added as they pass the marker
|
||||
*/
|
||||
@Test
|
||||
public void boatOrderTest() throws Exception {
|
||||
Leg leg = new Leg(35, 100, "1");
|
||||
public void testBoatHeading() throws Exception {
|
||||
Boat boat = new Boat("testBoat");
|
||||
Event event = new Event(1231242.2, boat, new SingleMark("mark1", 142.5, 122.1), new SingleMark("mark2", 121.9,99.2), 0);
|
||||
|
||||
Boat boat1 = new Boat("testBoat");
|
||||
Boat boat2 = new Boat("testBoat2");
|
||||
assertEquals(event.getBoatHeading(), 228.0266137055349, 1e-15);
|
||||
}
|
||||
|
||||
Event event1 = new Event(1231242, boat1, leg);
|
||||
Event event2 = new Event(1231242, boat2, leg);
|
||||
@Test
|
||||
public void testDistanceBetweenMarks() throws Exception {
|
||||
Boat boat = new Boat("testBoat");
|
||||
Event event = new Event(1231242.2, boat, new SingleMark("mark1", 142.5, 122.1), new SingleMark("mark2", 121.9,99.2), 0);
|
||||
|
||||
event1.boatPassedMarker();
|
||||
event2.boatPassedMarker();
|
||||
|
||||
assertEquals(event1.getLeg().getMarker().getBoats()[0].getTeamName(), "testBoat");
|
||||
assertEquals(event2.getLeg().getMarker().getBoats()[1].getTeamName(), "testBoat2");
|
||||
assertEquals(event.getDistanceBetweenMarks(), 339059.653830461, 1e-15);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package seng302;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Unit test for FileParser class
|
||||
* Created by Haoming on 5/03/17.
|
||||
*/
|
||||
public class FileParserTest {
|
||||
|
||||
/**
|
||||
* test if it fails from reading non existed file
|
||||
*/
|
||||
@Test(expected = FileNotFoundException.class)
|
||||
public void readNonExistedFile() throws Exception {
|
||||
FileParser fileParser = new FileParser("test/java/seng302/non-existed.json");
|
||||
}
|
||||
|
||||
/**
|
||||
* test a valid json file with valid content.
|
||||
*/
|
||||
@Test
|
||||
public void readValidFile() throws Exception {
|
||||
FileParser fileParser = new FileParser("src/test/java/seng302/valid.json");
|
||||
|
||||
assertEquals("AC35", fileParser.getRaceName());
|
||||
|
||||
assertEquals("Oracle Team USA", fileParser.getTeams().get(0).get("team-name"));
|
||||
assertEquals(20.9, fileParser.getTeams().get(0).get("velocity"));
|
||||
assertEquals(2, fileParser.getRaceSize());
|
||||
assertEquals(6, fileParser.getTotalNumberOfTeams());
|
||||
}
|
||||
|
||||
/**
|
||||
* test an invalid json file within wrong type value and misnamed
|
||||
* variable name.
|
||||
*/
|
||||
@Test
|
||||
public void readInvalidFile() throws Exception {
|
||||
FileParser fileParser = new FileParser("src/test/java/seng302/invalid.json");
|
||||
|
||||
assertEquals(null, fileParser.getRaceName());
|
||||
assertEquals(null, fileParser.getTeams());
|
||||
//assertEquals(-1, fileParser.getTimeScale());
|
||||
assertEquals(null, fileParser.getTeams());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package seng302;
|
||||
|
||||
import org.junit.Test;
|
||||
import seng302.models.Leg;
|
||||
import seng302.models.mark.SingleMark;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@@ -15,25 +17,25 @@ public class LegTest {
|
||||
*/
|
||||
@Test
|
||||
public void testLegCreationUsingMarkerLabel() {
|
||||
Leg leg = new Leg(010, 100, "Marker");
|
||||
Leg leg = new Leg(010, 100, "SingleMark");
|
||||
|
||||
assertEquals(leg.getHeading(), 010);
|
||||
assertEquals(leg.getDistance(), 100);
|
||||
assertEquals(leg.getMarkerLabel(), "Marker");
|
||||
assertEquals(leg.getMarkerLabel(), "SingleMark");
|
||||
assertEquals(leg.getIsFinishingLeg(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creation of the leg by providing a
|
||||
* Marker object
|
||||
* SingleMark object
|
||||
*/
|
||||
@Test
|
||||
public void testLegCreation() {
|
||||
Leg leg = new Leg(010, 100, new Marker("Marker"));
|
||||
Leg leg = new Leg(010, 100, new SingleMark("SingleMark"));
|
||||
|
||||
assertEquals(leg.getHeading(), 010);
|
||||
assertEquals(leg.getDistance(), 100);
|
||||
assertEquals(leg.getMarkerLabel(), "Marker");
|
||||
assertEquals(leg.getMarkerLabel(), "SingleMark");
|
||||
assertEquals(leg.getIsFinishingLeg(), false);
|
||||
}
|
||||
|
||||
@@ -43,7 +45,7 @@ public class LegTest {
|
||||
*/
|
||||
@Test
|
||||
public void testSetFinishLeg() {
|
||||
Leg leg = new Leg(010, 100, "Marker");
|
||||
Leg leg = new Leg(010, 100, "SingleMark");
|
||||
|
||||
leg.setFinishingLeg(true);
|
||||
assertEquals(leg.getIsFinishingLeg(), true);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package seng302;
|
||||
|
||||
import org.junit.Test;
|
||||
import seng302.models.Boat;
|
||||
import seng302.models.Race;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package seng302;
|
||||
|
||||
import org.junit.Test;
|
||||
import seng302.controllers.RaceViewController;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
public class TestRaceTimer {
|
||||
@Test
|
||||
public void testPositiveTimeString(){
|
||||
RaceViewController controller = new RaceViewController();
|
||||
String result = controller.convertTimeToMinutesSeconds(61);
|
||||
|
||||
assertTrue(result.equals("01:01"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeTimeString(){
|
||||
RaceViewController controller = new RaceViewController();
|
||||
String result = controller.convertTimeToMinutesSeconds(-61);
|
||||
|
||||
assertTrue(result.equals("-01:01"));
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"time-scale": "abc",
|
||||
"race-name": 123,
|
||||
"teams-with-wrong-name": [
|
||||
"team1",
|
||||
"team2",
|
||||
"team3"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package seng302.models.mark;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Created by Haoming on 17/3/17.
|
||||
*/
|
||||
public class MarkTest {
|
||||
|
||||
private SingleMark singleMark1;
|
||||
private SingleMark singleMark2;
|
||||
private GateMark gateMark;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.singleMark1 = new SingleMark("testMark_SM1", 12.23234, -34.342);
|
||||
this.singleMark2 = new SingleMark("testMark_SM2", 12.23239, -34.352);
|
||||
this.gateMark = new GateMark("testMark_GM", singleMark1, singleMark2, singleMark1.getLatitude(), singleMark2.getLongitude());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getName() throws Exception {
|
||||
assertEquals("testMark_SM1", this.singleMark1.getName());
|
||||
assertEquals("testMark_GM", this.gateMark.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMarkType() throws Exception {
|
||||
assertTrue(this.singleMark2.getMarkType() == MarkType.SINGLE_MARK);
|
||||
assertTrue(this.gateMark.getMarkType() == MarkType.GATE_MARK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMarkContent() throws Exception {
|
||||
assertEquals(12.23234, this.singleMark1.getLatitude(), 1e-10);
|
||||
assertEquals(-34.342, this.singleMark1.getLongitude(), 1e-10);
|
||||
assertEquals("testMark_SM1", this.gateMark.getSingleMark1().getName());
|
||||
assertEquals(-34.352, this.gateMark.getSingleMark2().getLongitude(), 1e-10);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package seng302.models.parsers;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Created by Haoming on 23/03/17.
|
||||
*/
|
||||
public class ConfigParserTest {
|
||||
|
||||
private ConfigParser cp;
|
||||
|
||||
@Before
|
||||
public void initializeParser() throws Exception {
|
||||
cp = new ConfigParser("/config/config.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWindDirection() throws Exception {
|
||||
assertEquals(135, cp.getWindDirection(), 1e-10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTimeScale() throws Exception {
|
||||
assertEquals(10.0, cp.getTimeScale(), 1e-10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDoubleByTagName() throws Exception {
|
||||
assertEquals(6, cp.getDoubleByTagName("race-size", 0), 1e-10);
|
||||
assertEquals(100, cp.getDoubleByTagName("noTag", 100), 1e-10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStringByTagName() throws Exception {
|
||||
assertEquals("AC35", cp.getStringByTagName("race-name", "11"));
|
||||
assertEquals("oops", cp.getStringByTagName("noTag", "oops"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package seng302.models.parsers;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import seng302.models.mark.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* To test if course parser works as expected.
|
||||
* Created by Haoming on 17/03/17.
|
||||
*/
|
||||
public class CourseParserTest {
|
||||
|
||||
private CourseParser cp;
|
||||
|
||||
@Before
|
||||
public void initializeParser() throws Exception {
|
||||
cp = new CourseParser("/config/course.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getGates() throws Exception {
|
||||
ArrayList<Mark> course = cp.getCourse();
|
||||
|
||||
assertTrue(MarkType.GATE_MARK == course.get(0).getMarkType());
|
||||
|
||||
GateMark gateMark1 = (GateMark) course.get(0);
|
||||
assertEquals(32.293771, gateMark1.getSingleMark2().getLatitude(), 0.00000001);
|
||||
assertEquals(-64.855242, gateMark1.getSingleMark2().getLongitude(), 0.00000001);
|
||||
|
||||
GateMark gateMark2 = (GateMark) course.get(5);
|
||||
|
||||
assertEquals("Finish1", gateMark2.getSingleMark1().getName());
|
||||
assertEquals("Finish2", gateMark2.getSingleMark2().getName());
|
||||
assertEquals(32.317257, gateMark2.getSingleMark2().getLatitude(), 0.00000001);
|
||||
assertEquals(-64.83626, gateMark2.getSingleMark2().getLongitude(), 0.00000001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMarks() throws Exception {
|
||||
ArrayList<Mark> course = cp.getCourse();
|
||||
assertEquals("Mid Mark", course.get(1).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getOrder() {
|
||||
ArrayList<Mark> course = cp.getCourse();
|
||||
assertEquals(6, course.size());
|
||||
assertEquals("Start", course.get(0).getName());
|
||||
assertEquals("Mid Mark", course.get(1).getName());
|
||||
assertEquals("Leeward Gate", course.get(2).getName());
|
||||
assertEquals("Windward Gate", course.get(3).getName());
|
||||
assertEquals("Leeward Gate", course.get(4).getName());
|
||||
assertEquals("Finish", course.get(5).getName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package seng302.models.parsers;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import seng302.models.Boat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Created by Haoming on 18/03/17.
|
||||
*/
|
||||
public class TeamsParserTest {
|
||||
|
||||
private TeamsParser tp;
|
||||
@Before
|
||||
public void readFile() {
|
||||
tp = new TeamsParser("/config/teams.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBoats() throws Exception {
|
||||
ArrayList<Boat> boats = tp.getBoats();
|
||||
|
||||
assertEquals(6, boats.size(), 1e-10);
|
||||
|
||||
assertEquals("Oracle Team USA", boats.get(0).getTeamName());
|
||||
//assertEquals(30.9, boats.get(0).getVelocity(), 1e-10);
|
||||
|
||||
assertEquals("Groupama Team France", boats.get(5).getTeamName());
|
||||
//assertEquals(45.6, boats.get(5).getVelocity(), 1e-10);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"race-name": "AC35",
|
||||
"time-scale": 1,
|
||||
"race-size": 2,
|
||||
"teams": [
|
||||
{
|
||||
"team-name": "Oracle Team USA",
|
||||
"velocity": 20.9
|
||||
},
|
||||
{
|
||||
"team-name": "Artemis Racing",
|
||||
"velocity": 18.3
|
||||
},
|
||||
{
|
||||
"team-name": "Emirates Team New Zealand",
|
||||
"velocity": 21.5
|
||||
},
|
||||
{
|
||||
"team-name": "Groupama Team France",
|
||||
"velocity": 19.9
|
||||
},
|
||||
{
|
||||
"team-name": "Land Rover BAR",
|
||||
"velocity": 17.6
|
||||
},
|
||||
{
|
||||
"team-name": "SoftBank Team Japan",
|
||||
"velocity": 16.6
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user