Compare commits

..

111 Commits

Author SHA1 Message Date
Haoming Yin 971a3920a3 Fixed race timer to display real race time, and made race time scalable.
#story[445]
2017-03-30 16:12:01 +13:00
Michael Rausch b252797e9b Removed test case that failed when the config file was changed 2017-03-30 14:48:47 +13:00
Michael Rausch c758afe3e3 Merge branch 'master' of https://eng-git.canterbury.ac.nz/seng302-2017/team-13 2017-03-30 14:46:22 +13:00
Peter f8d3f53158 changed boat speed to a lower value 2017-03-30 14:45:33 +13:00
Michael Rausch 4a8672a20b Updated race timer test to use new controller
Tags:story[455]
2017-03-30 14:44:57 +13:00
Peter 32109e8565 Merge branch 'controller-refacto' into origin/master 2017-03-29 15:03:38 +13:00
Peter 5d6060c690 Merge remote-tracking branch 'origin/master' into controller-refacto 2017-03-29 15:01:38 +13:00
Peter a95d030817 Controllers and Fxml nicely refactored, tests still broken #story[463] 2017-03-29 14:59:37 +13:00
Michael Rausch ffa84c6e87 Removed testNextColor for now as the test seems to be broken.. 2017-03-29 13:28:49 +13:00
Peter 4a6978ff79 Fxml refactored, partway through refactoring controllers (app does not run) #story[463] 2017-03-29 12:58:49 +13:00
Haoming Yin 1497858cc0 Deleted the print exception stack statement to make terminal more tidy.
- the exception is aim to happen, so no need to print it out.

#story[377]
2017-03-28 19:38:57 +13:00
Michael Rausch 2c125d4ce0 Fixed test case for the boat heading #story[466] 2017-03-28 19:25:04 +13:00
Zhi You Tan 8bf5455f42 Updated fxml and canvas controller to display a resizable canvas.
#story[377]
2017-03-27 17:05:49 +13:00
Zhi You Tan 48f7e41905 Merge branch 'master' of /home/cosc/student/zyt10/Documents/[SENG302]/team-13 with conflicts. 2017-03-27 16:52:31 +13:00
Peter b50ac62a4b Merge branch 'add-fps-counter' 2017-03-27 16:47:34 +13:00
Peter 6fc55bb82c Added toggle for fps #story[463] 2017-03-27 16:45:02 +13:00
Peter 65ac864bf2 added count for fps inside handle loop and displayed fps on the canvas #story[463] 2017-03-27 16:34:54 +13:00
Haoming Yin cf6bbdd1f1 Fixed boat heading calculation method to get a correct direction
#story[480] #pair[hyi25, ptg19]
2017-03-27 14:29:03 +13:00
Haoming Yin bbe7cbee8f Use canvas polygon to draw a triangle
#story[480]
2017-03-27 14:26:38 +13:00
Michael Rausch 55ba343426 Merge branch 'change-to-short-names' into 'master'
Changed the team names to their abbreviated versions

- Also halved the size of the wake lines
- Updated tests to support the shorter team names
- Wake lines are now hidden with the other annotations

Tags: #story[23,21]

See merge request !25
2017-03-24 21:01:55 +13:00
Michael Rausch e6ace5fb2f Changed the team names to their abbreviated versions
- Also halved the size of the wake lines
- Updated tests to support the shorter team names
- Wake lines are now hidden with the other annotations

Tags: #story[23,21]
2017-03-24 20:56:52 +13:00
Michael Rausch 550ab59231 Merge branch 'master' of https://eng-git.canterbury.ac.nz/seng302-2017/team-13 2017-03-24 20:45:49 +13:00
Michael Rausch 213303d674 Merge branch 'create-wake-line' 2017-03-24 20:39:21 +13:00
Michael Rausch 74c81eb7b3 Removed a character that was accidentally inserted 2017-03-24 20:37:32 +13:00
Michael Rausch c33586e7f5 Changed test to work with the new getHeading method, and removed an unused import that was causing tests to fail
- There was a com.sun import that we were no longer using that was causing issues

Tags: story[466]
2017-03-24 20:33:53 +13:00
Michael Rausch 5dd5e50738 Implemented wake lines
- Changed heading calculation in event class
- The boats now go to the marker, rather than the center of a gate

Tags: #story[466]
2017-03-24 20:27:17 +13:00
Haoming Yin 304f30ece6 Reformatted and refactored the fileparser to get xml from resource folder
#fix #refactor #story[377] #pair[xyi25, zyt10]
2017-03-24 12:55:11 +13:00
zyt10 e8b1720fee Created a toggle checkbox to show and hide all annotation
#story[477]
2017-03-24 12:15:39 +13:00
Peter e0cea098c1 Merge remote-tracking branch 'origin/master' 2017-03-24 11:52:17 +13:00
Peter 50ab481b18 added knots beside boat name #story[447] 2017-03-24 11:51:07 +13:00
zyt10 d39aacba83 RaceController now uses new parsers & deleted OldApp and OldFileParser
#story[377]
2017-03-24 11:34:34 +13:00
Michael Rausch 798fe4da0e Changed FPS to 30 2017-03-24 11:15:46 +13:00
Peter e19f110f19 Merge branch 'origin/addBoatPositionsInSideBar' 2017-03-24 11:06:03 +13:00
Peter 59a4a74a97 changed boatPositionController to also add velocity to the boat ordering shown in the side bar #story[447] 2017-03-23 22:50:04 +13:00
Peter 590ef557d3 fixed bugs from last commit and updated some tests #story[426] 2017-03-23 22:24:42 +13:00
Peter 7bc5c8f8a4 Merge remote-tracking branch 'refs/remotes/origin/master'
Conflicts:
	src/main/java/seng302/controllers/CanvasController.java
	src/main/java/seng302/models/Boat.java
2017-03-23 22:17:45 +13:00
Peter bf8244ce49 tried to test BoatPositionController but as it's connected to the fxml I am unsure if it is possible to test (test code is commented out) #story[426] 2017-03-23 22:04:25 +13:00
Peter a860cc0ec1 connected up all the javafx components and got team positions displaying on the sidebar #story[426] 2017-03-23 21:21:54 +13:00
Peter bb8c681270 added a markpos value to event for use in displaying the team positions #story[426] 2017-03-23 21:19:39 +13:00
Michael Rausch 9872095e50 Merge branch 'boat-labels' into 'master'
Display team name and speed beside boat

- Also slowed down the AnimationTimer
- Removed the need to scale the canvas

Tags: #implement #story[18] #story[19]

See merge request !23
2017-03-23 18:06:31 +13:00
Michael Rausch 403aaa76ae Display team name and speed beside boat
- Also slowed down the AnimationTimer
- Removed the need to scale the canvas

Tags: #implement #story[18] #story[19]
2017-03-23 18:04:00 +13:00
Peter 8578bc4a5b changed the "sea" color of the canvas to be actually drawn on the canvas rather than the pane behind #story[377] 2017-03-23 16:18:42 +13:00
Peter ed004d1423 Merge branch 'master'
Conflicts:
	src/main/java/seng302/controllers/CanvasController.java
	src/main/resources/RaceView.fxml
2017-03-23 15:16:11 +13:00
Michael Rausch a91b2b4f7e Merge branch 'create-race-timer' into 'master'
Create race timer

Created race timer controller

#implement #test #story[16]

See merge request !22
2017-03-23 14:20:25 +13:00
Michael Rausch 24f9607e5a Added tests for the RaceTimerController #test #story[16] 2017-03-23 14:18:27 +13:00
Michael Rausch 2384013139 Created race timer
- Race starts when timer reaches 1 second
- Race waits 10 seconds before it starts

Tags: #implement #story[16]
2017-03-23 14:11:12 +13:00
Haoming Yin 42ffd1b1f8 Add rotated wind direction arrow to race view.
#story[422]
2017-03-23 01:10:07 +13:00
Haoming Yin a2d06909c9 Finished config parser to read race info from external xml file
- created config parser unit test.
- modified config.xml file
- write unit test for config parser

#story[422]
2017-03-23 00:21:18 +13:00
Michael Rausch c8b7b95df8 Merge branch 'finish-race-results' into 'master'
Added the race results to the RaceResultController. Also fixed some bugs

- Fixed a bug where the race results would be out of order.
- Changed the colour of the start and finish gates
- Added the race results to the RaceResultController and updated view

Tags: #fix #implement #story[13, 10, 11]

See merge request !21
2017-03-22 22:42:05 +13:00
Michael Rausch 9e22eac4d8 Added the race results to the RaceResultController. Also fixed some bugs
- Fixed a bug where the race results would be out of order.
- Changed the colour of the start and finish gates
- Added the race results to the RaceResultController and updated view

Tags: #fix #implement #story[13, 10, 11]
2017-03-22 22:30:49 +13:00
Michael Rausch a41f2e4bde Changed marks from circles to squares #fix 2017-03-22 15:10:57 +13:00
Michael Rausch 039e61def6 Fixed broken JavaFX file #fix 2017-03-22 14:50:28 +13:00
Peter 54d329c5cf changed raceView javafx structure and added an example of how the handle function works with keyframes #story[426] 2017-03-22 14:44:40 +13:00
Michael Rausch f46a98fad9 Merge branch 'master' of https://eng-git.canterbury.ac.nz/seng302-2017/team-13 2017-03-22 14:39:37 +13:00
Michael Rausch 6a6816dda1 Merge branch 'create-finish-display' 2017-03-22 14:31:06 +13:00
Michael Rausch f6ea2953e9 Changed lat/long for gates to midpoint and added colours for the marks
Tags: #fix #implement #story[10,11]
2017-03-22 14:28:51 +13:00
Michael Rausch 7591a79323 Fixed course rotation #fix 2017-03-22 14:05:55 +13:00
Michael Rausch c12760b70a Inserted missing } #fix 2017-03-22 12:53:42 +13:00
Michael Rausch a526b0d65a Rotated map by 180 degrees #fix 2017-03-22 12:52:43 +13:00
Michael Rausch ef098e63d7 Added finishing events #implement and rotated the map by 180 degrees#fix 2017-03-22 12:51:03 +13:00
zyt10 29a0b23670 Reformatted double value lat and lon to final double VIEW_CORNER_LAT and VIEW_CORNER_LON 2017-03-22 12:03:46 +13:00
zyt10 44b5b0b771 Added side pane to GUI and resize to 1280x960
#story[426]
2017-03-22 11:46:39 +13:00
Michael Rausch 00f9cc4698 Merge branch 'fix-boat-postions-and-keyframes' into 'master'
Fixed x and y coordinates #fix #story[9]



See merge request !19
2017-03-21 17:42:13 +13:00
Michael Rausch 3d4d5a3dab Fixed x and y coordinates #fix #story[9] 2017-03-20 21:13:30 +13:00
Peter ed577fad6a adding files missed in previous commit 2017-03-20 17:58:40 +13:00
Peter ae61260665 created a javafx view for the race finish and created a rough wireframe for the controller to change the view from race to finish #story[414] 2017-03-20 17:56:14 +13:00
zyt10 0e4bb0f942 Changed getMark to getThisMark. Added start point to KeyFrame. Race now starts from start point and ends at finish point
#story[377]
2017-03-20 17:34:24 +13:00
Haoming Yin ee34e5028f Reformatted and refactored the canvas controller
#fix #refactor #story[377]
2017-03-20 17:23:33 +13:00
Peter 3b8dd11758 Added drawBoats for use in animation timer, also fixed gate marks only drawing one mark #story[378] 2017-03-20 16:11:04 +13:00
zyt10 cc04e2dd6d Fixed boat colour, was in the wrong constructor
#story[377]
2017-03-20 16:03:07 +13:00
Haoming Yin b88cf6a101 Merge branch 'refactor-file-parser' 2017-03-18 21:38:38 +13:00
Haoming Yin d10985f890 Finished team parser to read team info from external xml file
- created team parser unit test
- refactored team parser functions

#fix #refactor #implement
2017-03-18 21:32:12 +13:00
zyt10 c08504293b Created canvas and race controllers to display boats on canvas and modified marks and parsers to support them.
#story[377] #pair[zyt10, ptg19]
2017-03-17 18:21:11 +13:00
zyt10 4bc49da10d Merge remote-tracking branch 'origin/master' 2017-03-17 16:18:24 +13:00
zyt10 683f4ba94e Mostly got boats going to marks on the canvas (code currently broken) #story[377] #pair[zyt10, ptg19] 2017-03-17 16:18:01 +13:00
Haoming Yin 8fd06c84ac Merged the refactored mark related class with course parser.
#fix #refactor #story[9] #story[10] #story[11]
2017-03-17 15:56:37 +13:00
Haoming Yin 23b163e6c1 Merge branch 'master' into refactor-file-parser
# Conflicts:
#	src/main/java/seng302/models/GateMark.java
2017-03-17 15:44:16 +13:00
Haoming Yin 6383b9a6f8 Deleted the old Mark and GateMark files 2017-03-17 15:26:38 +13:00
Haoming Yin 0b3ebf229f Refactor mark related classes.
- Mark is an abstract class which containing its name and type
- Single Mark is a sub class of Mark which containing only one GPS location
- Gate Mark is a sub class of Mark which containing two Single Marks

#refactor #fix #story[10] #story[11] #story[12]
2017-03-17 15:21:04 +13:00
Michael Rausch d6fe155d4d Changed distance calculation to use latitude and longitude
Tags: #fix
2017-03-17 11:08:35 +13:00
Michael Rausch 8829728f19 Merge branch 'master' of https://eng-git.canterbury.ac.nz/seng302-2017/team-13 2017-03-17 10:27:04 +13:00
Michael Rausch a6c9156ae5 Changed Main-Class in pom.xml to our new main class
Tags: #fix
2017-03-17 10:26:09 +13:00
Haoming Yin 121f996a43 created config, teams xml file, and teamsParser class
#implement
2017-03-17 01:04:10 +13:00
Ryan Tan 44d4f25413 Implemented Color Enum & boat will call function from enum to get next color. Using static colour cycling for now.
#story[377]
2017-03-17 00:57:50 +13:00
Haoming Yin 16abfcffda Created course parser as a subclass of file parser
- refactored file parser as an abstract class
- created course parser to parse course xml file

#implement #fix #refactor #story[9] #story[10]
2017-03-17 00:54:43 +13:00
Haoming Yin e7ba9d962d Deleted and modified previous sprint parser to cater the sprint2’s new requirement
- also added new method for GateMark

#story[9] #story[10] #fix
2017-03-17 00:53:08 +13:00
Haoming Yin b7631c0b46 Create unit test for course parser, and modified course xml file
#implement #fix #story[10] #story[9]
2017-03-17 00:51:16 +13:00
Michael Rausch 0039703f03 Merge branch 'master' of https://eng-git.canterbury.ac.nz/seng302-2017/team-13 2017-03-16 20:50:41 +13:00
Michael Rausch d439c9673f removed intellij files #chore 2017-03-16 20:50:27 +13:00
Haoming Yin f255b0ea6d Add intellij file type to .gitignore. 2017-03-16 20:47:55 +13:00
Michael Rausch 9d01ed8eb3 Merge branch 'refactor-events-to-update-boat-positions' into 'master'
Removed Legs from the race, using coordinates instead

Tags: #implement #refactor #test #story[9]

See merge request !17
2017-03-16 20:30:48 +13:00
Michael Rausch 94e4e853c3 Removed Legs from the race, using coordinates instead
Tags: #implement #refactor #test #story[9]
2017-03-16 20:29:17 +13:00
Ryan Tan ffa39e6a9c Changed size of canvas to 720 x 360
#story[377]
2017-03-16 17:40:31 +13:00
Ryan Tan 9e4ae60885 Added a function to draw boat on canvas
#story[377]
2017-03-16 17:25:17 +13:00
Peter 2cd4366d07 added course xml file #story[378] 2017-03-16 17:15:45 +13:00
Peter 11c5e1e9ba Added gate mark and refactored marker to mark #story[378] 2017-03-16 16:11:48 +13:00
Peter 550812d8e1 Currently displaying basic javafx window with canvas. Also changed the file structure a bit.
At this point the javafx is not tied to the old code in any way #story[377]
2017-03-15 18:16:43 +13:00
Haoming Yin 9ca5f5e7fd Updated design decision file in doc. 2017-03-09 16:22:40 +13:00
Haoming Yin abc5df7837 Added unit for boat velocity
#fix #story[6]
2017-03-09 15:00:48 +13:00
Haoming Yin debe2c0cca Fixed documentation for FileParserTest and LegTest
#document #fix
2017-03-09 14:52:13 +13:00
Michael Rausch cfa851b968 Added user manual
Tags: #docs
2017-03-09 12:54:37 +13:00
Michael Rausch 37f4b55b04 Merge branch 'read_config_from_args' into 'master'
Added ability to pass the config file as a command line argument

Tags: #implement

See merge request !16
2017-03-09 12:42:53 +13:00
Michael Rausch c1aa38c1b0 Added ability to pass the config file as a command line argument
Tags: #implement
2017-03-09 12:41:44 +13:00
Michael Rausch 260bf06219 Merge branch 'make-tests' into 'master'
Added tests

Tags: #test

See merge request !15
2017-03-09 12:23:23 +13:00
Michael Rausch 8d85557e10 Added tests
Tags: #test
2017-03-09 12:22:38 +13:00
Michael Rausch d33a88d313 Added docstrings to classes
Tags: #docs
2017-03-08 23:02:45 +13:00
Michael Rausch d3b71c21e5 Merge branch 'format-and-doc' 2017-03-08 22:57:59 +13:00
Michael Rausch d10c6a54f5 Added and fixed docstrings
Tags: #docs
2017-03-08 22:53:22 +13:00
Michael Rausch 0a86dde7e4 Fixed docstrings
Tags: #docs
2017-03-08 22:31:05 +13:00
Michael Rausch ae80b434f6 Added and fixed docstrings
Tags #docs
2017-03-08 22:25:52 +13:00
Haoming Yin b0cd7c8c08 Reformatted doctring and import statements 2017-03-08 14:45:06 +13:00
52 changed files with 2477 additions and 944 deletions
+3
View File
@@ -84,6 +84,9 @@ nbactions.xml
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff: # User-specific stuff:
# get rid of these annoying files
.idea/
*.iml
.idea/workspace.xml .idea/workspace.xml
.idea/tasks.xml .idea/tasks.xml
.idea/dictionaries .idea/dictionaries
+14
View File
@@ -1,2 +1,16 @@
# Design Decisions # 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.
-13
View File
@@ -1,13 +0,0 @@
{
"race-name": "AC35",
"time-scale": 1.0,
"race-size": 4,
"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}
]
}
+14
View File
@@ -1 +1,15 @@
# User Manual # User Manual
## Running the application
When you execute the application, it will try to load a configuration file called config.json located in doc/examples/.
You can specify a config file using the using the -f flag, for example 'java -jar app.jar -f doc/examples/config1.json'
## The config file
The teams/boats are specified in the config file under 'teams', each team requires a team name, and a velocity (in meters per second).
The 'time-scale' option lets you change how long the race takes to complete. A time-scale of 1.0 is normal speed, 2.0 is 2x etc.
The 'race-size' option lets you specify how many boats will be selected to compete in each race. There must be at least this many teams defined.
+9 -2
View File
@@ -1,4 +1,4 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>seng302</groupId> <groupId>seng302</groupId>
@@ -25,6 +25,12 @@
<artifactId>json-simple</artifactId> <artifactId>json-simple</artifactId>
<version>1.1.1</version> <version>1.1.1</version>
</dependency> </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.7.13</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
@@ -40,7 +46,8 @@
<version>2.4.3</version> <version>2.4.3</version>
<configuration> <configuration>
<transformers> <transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries> <manifestEntries>
<Main-Class>seng302.App</Main-Class> <Main-Class>seng302.App</Main-Class>
<X-Compile-Source-JDK>${maven.compiler.source}</X-Compile-Source-JDK> <X-Compile-Source-JDK>${maven.compiler.source}</X-Compile-Source-JDK>
+17 -79
View File
@@ -1,87 +1,25 @@
package seng302; package seng302;
import java.util.*; import javafx.application.Application;
import java.lang.reflect.Array; 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
* Builds a race object for the AC35 course public void start(Stage primaryStage) throws Exception {
*/ Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml"));
public static Race createRace() throws Exception{ primaryStage.setTitle("RaceVision");
Race race = new Race(); primaryStage.setScene(new Scene(root));
// Read team names from file primaryStage.show();
FileParser fp = new FileParser("doc/examples/config.json"); }
ArrayList<String> boatNames = new ArrayList<>();
ArrayList<Map<String, Object>> teams = fp.getTeams();
//get race size public static void main(String[] args) {
int numberOfBoats = (int) fp.getRaceSize(); launch(args);
//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;
}
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;
}
public static void main( String[] args )
{
Race race = null;
try{
race = createRace();
}
catch (Exception e){
System.out.println(e);
}
// 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.");
}
} }
} }
-56
View File
@@ -1,56 +0,0 @@
package seng302;
/**
* 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 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
}
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;
}
/**
* Sets velocity of the boat
* @param velocity The velocity of boat
*/
public void setVelocity(float velocity) {
this.velocity = velocity;
}
/**
* Gets velocity of the boat
* @return a float number of the boat velocity
*/
public double getVelocity() {
return this.velocity;
}
}
-119
View File
@@ -1,119 +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.
*
* @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 class Event {
private long time;
private Boat boat;
private Leg leg;
private boolean isFinishingEvent = false;
public Event(long eventTime, Boat eventBoat, Leg eventLeg) {
this.time = eventTime;
this.boat = eventBoat;
this.leg = eventLeg;
}
public Event(long eventTime, Boat eventBoat, Leg eventLeg, boolean isFinishingEvent) {
this.time = eventTime;
this.boat = eventBoat;
this.leg = eventLeg;
this.isFinishingEvent = isFinishingEvent;
}
/**
* 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 for the event
* @return the time for event in millisecond
*/
public long getTime() {
return this.time;
}
/**
* 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));
}
/**
* Sets the involved boat
* @param eventBoat the involved boat
*/
public void setBoat(Boat eventBoat) {
this.boat = eventBoat;
}
/**
* Gets the involved boat
* @return the boat involved in the event
*/
public Boat getBoat() {
return this.boat;
}
/**
* Sets the involved location/leg
* @param eventLeg the involved leg
*/
public void setLeg(Leg eventLeg) {
this.leg = eventLeg;
}
/**
* Gets the involved location/leg
* @return the leg involved in the event
*/
public Leg getLeg() {
return this.leg;
}
/**
* Call when the boat reaches the marker, this will tell the marker the order
* in which boats pass it
*/
public void addBoatToMarker(){
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 contains the timestamp and course information for this event
*/
public String getEventString(){
String currentHeading = Integer.toString(this.getLeg().getHeading());
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 + "°");
}
}
-125
View File
@@ -1,125 +0,0 @@
package seng302;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.io.FileReader;
import java.io.IOException;
import java.io.FileNotFoundException;
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;
}
}
}
-108
View File
@@ -1,108 +0,0 @@
package seng302;
public class Leg {
private int heading;
private int distance;
private boolean isFinishingLeg;
private Marker startingMarker;
/*
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
*/
public Leg(int heading, int distance, Marker marker){
this.heading = heading;
this.distance = distance;
this.startingMarker = marker;
this.isFinishingLeg = false;
}
/*
Create a new leg
@param heading, the magnetic heading of this leg
@param distance, the total distance of this leg in meters
@param markerName, the name of the marker this leg starts on
*/
public Leg(int heading, int distance, String markerName){
this.heading = heading;
this.distance = distance;
this.startingMarker = new Marker(markerName);
this.isFinishingLeg = false;
}
/*
Set the heading for this leg
*/
public void setHeading(int heading){
this.heading = heading;
}
/*
Get the heading of this leg
*/
public int getHeading(){
return this.heading;
}
/*
Set the distance of this leg in meters
*/
public void setDistance(int distance){
this.distance = distance;
}
/*
Get the total distance of this leg in meters
*/
public int getDistance(){
return this.distance;
}
/*
Set the marker this leg starts on
*/
public void setMarker(Marker marker){
this.startingMarker = marker;
}
/*
Returns the marker this leg started on
*/
public Marker getMarker(){
return this.startingMarker;
}
/*
Returns the name of the marker this leg started on
*/
public String getMarkerLabel(){
return this.startingMarker.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
@param isFinishingLeg whether or not the race finishes on this leg
*/
public void setFinishingLeg(boolean isFinishingLeg){
this.isFinishingLeg = isFinishingLeg;
}
/*
@returns true if this the race finishes after this leg
*/
public boolean getIsFinishingLeg(){
return this.isFinishingLeg;
}
}
-54
View File
@@ -1,54 +0,0 @@
package seng302;
import java.util.ArrayList;
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()]);
}
}
-246
View File
@@ -1,246 +0,0 @@
package seng302;
import java.util.*;
import java.lang.reflect.Array;
import java.util.concurrent.TimeUnit;
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;
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
@returns 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
*/
public void displayFinishingOrder() {
int numberOfBoats = this.getNumberOfBoats();
Boat[] boats = this.getFinishedBoats();
System.out.println("\n\n");
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.");
}
}
/*
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;
}
/**
* Temporary method used to generated all the events.
*/
private void generateEvents() {
//calculate the time for 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);
}
}
}
}
/**
* Note: this function is useless so far
* Calculates how far a boat has travelled in meter
*
* @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()) {
// pull out the event
Event nextEvent = events.poll();
// I just simply print it out for testing
System.out.println(nextEvent.getEventString());
nextEvent.addBoatToMarker();
if (nextEvent.getIsFinishingEvent()){
this.finishingOrder.add(nextEvent.getBoat());
}
}
// Wait for 100ms to slow down 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;
}
}
+130
View File
@@ -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;
}
}
+20
View File
@@ -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());
}
}
+148
View File
@@ -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;
}
}
+109
View File
@@ -0,0 +1,109 @@
package seng302.models;
import seng302.models.mark.SingleMark;
/**
* Represents the leg of a race.
*/
public class Leg {
private int heading;
private int distance;
private boolean isFinishingLeg;
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 singleMark, the singleMark this leg starts on
*/
public Leg(int heading, int distance, SingleMark singleMark) {
this.heading = heading;
this.distance = distance;
this.startingSingleMark = singleMark;
this.isFinishingLeg = false;
}
/**
* Create a new leg
*
* @param heading, the magnetic heading of this leg
* @param distance, the total distance of this leg in meters
* @param markerName, the name of the marker this leg starts on
*/
public Leg(int heading, int distance, String markerName) {
this.heading = heading;
this.distance = distance;
this.startingSingleMark = new SingleMark(markerName);
this.isFinishingLeg = false;
}
/**
* Get the heading of this leg
*/
public int getHeading() {
return this.heading;
}
/**
* Set the heading for this leg
*/
public void setHeading(int heading) {
this.heading = heading;
}
/**
* Get the total distance of this leg in meters
*/
public int getDistance() {
return this.distance;
}
/**
* Set the distance of this leg in meters
*/
public void setDistance(int distance) {
this.distance = distance;
}
/**
* Returns the marker this leg started on
*/
public SingleMark getMarker() {
return this.startingSingleMark;
}
/**
* Set the singleMark this leg starts on
*/
public void setMarker(SingleMark singleMark) {
this.startingSingleMark = singleMark;
}
/**
* Returns the name of the marker this leg started on
*/
public String getMarkerLabel() {
return this.startingSingleMark.getName();
}
/**
* Specify whether or not the race finishes on this leg
*
* @param isFinishingLeg whether or not the race finishes on this leg
*/
public void setFinishingLeg(boolean isFinishingLeg) {
this.isFinishingLeg = isFinishingLeg;
}
/**
* Returns whether or not the race finishes after this leg
* @return true if this the race finishes after this leg
*/
public boolean getIsFinishingLeg() {
return this.isFinishingLeg;
}
}
+197
View File
@@ -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;
}
}
}
+9
View File
@@ -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>
+71
View File
@@ -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>
+34
View File
@@ -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>
+7
View File
@@ -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" />
+55
View File
@@ -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>
+11
View File
@@ -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>
+59
View File
@@ -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>
-16
View File
@@ -1,16 +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 );
}
}
+18 -10
View File
@@ -1,27 +1,35 @@
package seng302; package seng302;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertTrue; import seng302.models.Boat;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
/** /**
* Unit test for the Team class. * Unit test for the Team class.
*/ */
public class BoatTest public class BoatTest {
{
@Test @Test
public void testBoatCreation() public void testBoatCreation() {
{
Boat boat1 = new Boat("Team 1"); Boat boat1 = new Boat("Team 1");
assertEquals(boat1.getTeamName(), "Team 1"); assertEquals(boat1.getTeamName(), "Team 1");
assertEquals(boat1.getVelocity(), (double) 10.0, 1e-15);
} }
@Test @Test
public void testChangeTeamName() public void testChangeTeamName() {
{ Boat boat1 = new Boat("Team 1");
Boat boat1 = new Boat("Team 1"); boat1.setTeamName("Team 2");
boat1.setTeamName("Team 2"); assertEquals(boat1.getTeamName(), "Team 2");
assertEquals(boat1.getTeamName(), "Team 2"); }
@Test
public void testSetVelocity() {
Boat boat1 = new Boat("Team 1", 29.0, "");
assertEquals(boat1.getVelocity(), (double) 29.0, 1e-15);
boat1.setVelocity(12.0);
assertEquals(boat1.getVelocity(), (double)12.0, 1e-15);
} }
} }
+45
View File
@@ -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);
}
}
+26 -9
View File
@@ -1,9 +1,11 @@
package seng302; package seng302;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import seng302.models.Boat;
import seng302.models.Event;
import seng302.models.mark.SingleMark;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
/** /**
* Test for Event class * Test for Event class
@@ -11,11 +13,26 @@ import static org.junit.Assert.*;
*/ */
public class EventTest { public class EventTest {
@Test @Test
public void getTimeString() throws Exception { public void getTimeString() throws Exception {
Leg leg = new Leg(035, 100, "Start"); Boat boat = new Boat("testBoat");
Boat boat = new Boat("testBoat"); Event event = new Event(1231242.2, boat, new SingleMark("mark1"), new SingleMark("mark2"), 0);
Event event = new Event(1231242, boat, leg); assertEquals("20:31:242", event.getTimeString());
assertEquals("20:31:242", event.getTimeString()); }
}
@Test
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);
assertEquals(event.getBoatHeading(), 228.0266137055349, 1e-15);
}
@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);
assertEquals(event.getDistanceBetweenMarks(), 339059.653830461, 1e-15);
}
} }
-52
View File
@@ -1,52 +0,0 @@
package seng302;
import org.junit.Test;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import static org.junit.Assert.*;
/** 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());
}
}
+25 -26
View File
@@ -1,52 +1,51 @@
package seng302; package seng302;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertTrue; import seng302.models.Leg;
import seng302.models.mark.SingleMark;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
/** /**
* Unit test for the Leg class. * Unit test for the Leg class.
*/ */
public class LegTest{ public class LegTest {
/* /**
Test creation of the leg by specifying a string * Test creation of the leg by specifying a string
for the marker label * for the marker label
*/ */
@Test @Test
public void testLegCreationUsingMarkerLabel() public void testLegCreationUsingMarkerLabel() {
{ Leg leg = new Leg(010, 100, "SingleMark");
Leg leg = new Leg(010, 100, "Marker");
assertEquals(leg.getHeading(), 010); assertEquals(leg.getHeading(), 010);
assertEquals(leg.getDistance(), 100); assertEquals(leg.getDistance(), 100);
assertEquals(leg.getMarkerLabel(), "Marker"); assertEquals(leg.getMarkerLabel(), "SingleMark");
assertEquals(leg.getIsFinishingLeg(), false); assertEquals(leg.getIsFinishingLeg(), false);
} }
/* /**
Test creation of the leg by providing a * Test creation of the leg by providing a
Marker object * SingleMark object
*/ */
@Test @Test
public void testLegCreation() public void testLegCreation() {
{ Leg leg = new Leg(010, 100, new SingleMark("SingleMark"));
Leg leg = new Leg(010, 100, new Marker("Marker"));
assertEquals(leg.getHeading(), 010); assertEquals(leg.getHeading(), 010);
assertEquals(leg.getDistance(), 100); assertEquals(leg.getDistance(), 100);
assertEquals(leg.getMarkerLabel(), "Marker"); assertEquals(leg.getMarkerLabel(), "SingleMark");
assertEquals(leg.getIsFinishingLeg(), false); assertEquals(leg.getIsFinishingLeg(), false);
} }
/* /**
Test changing whether or not a * Test changing whether or not a
leg is the finishing leg * leg is the finishing leg
*/ */
@Test @Test
public void testSetFinishLeg() public void testSetFinishLeg() {
{ Leg leg = new Leg(010, 100, "SingleMark");
Leg leg = new Leg(010, 100, "Marker");
leg.setFinishingLeg(true); leg.setFinishingLeg(true);
assertEquals(leg.getIsFinishingLeg(), true); assertEquals(leg.getIsFinishingLeg(), true);
+22 -8
View File
@@ -1,20 +1,22 @@
package seng302; package seng302;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertTrue; import seng302.models.Boat;
import static org.junit.Assert.assertEquals; import seng302.models.Race;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import static org.junit.Assert.assertEquals;
/** /**
* Unit test for the Race class. * Unit test for the Race class.
*/ */
public class RaceTest public class RaceTest {
{ /**
/* * Test that all boats were added to the race
Test that all boats were added to the race */
*/
@Test @Test
public void testAddingBoatsToRace(){ public void testAddingBoatsToRace() {
Boat boat1 = new Boat("Team 1"); Boat boat1 = new Boat("Team 1");
Boat boat2 = new Boat("Team 2"); Boat boat2 = new Boat("Team 2");
@@ -24,4 +26,16 @@ public class RaceTest
assertEquals(Array.getLength(race.getBoats()), 2); assertEquals(Array.getLength(race.getBoats()), 2);
} }
@Test
public void testGetShuffledBoats(){
Boat boat1 = new Boat("Team 1");
Boat boat2 = new Boat("Team 2");
Race race = new Race();
race.addBoat(boat1);
race.addBoat(boat2);
assertEquals(Array.getLength(race.getShuffledBoats()), 2);
}
} }
+25
View File
@@ -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"));
}
}
-5
View File
@@ -1,5 +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(1.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);
}
}
-13
View File
@@ -1,13 +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}
]
}