diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 2e00b8f1..c589c635 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -1,9 +1,6 @@ package seng302.controllers; -import javafx.animation.AnimationTimer; -import javafx.animation.KeyFrame; -import javafx.animation.KeyValue; -import javafx.animation.Timeline; +import javafx.animation.*; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.fxml.FXML; @@ -24,7 +21,6 @@ import seng302.models.mark.Mark; import seng302.models.mark.MarkType; import seng302.models.mark.SingleMark; import seng302.models.parsers.ConfigParser; -import seng302.models.parsers.CourseParser; import java.io.IOException; import java.util.ArrayList; @@ -53,6 +49,13 @@ public class CanvasController { @FXML private Text windArrowText, windDirectionText; + @FXML Pane raceTimer; + + private Animation.Status raceStatus = Animation.Status.PAUSED; + + /** + * Display the list of boats in the order they finished the race + */ private void loadRaceResultView() { FXMLLoader loader = new FXMLLoader(getClass().getResource("/FinishView.fxml")); loader.setController(new RaceResultController(race)); @@ -69,6 +72,57 @@ public class CanvasController { } } + /** + * Load the race timer + */ + private void loadTimerView(){ + FXMLLoader loader = new FXMLLoader(getClass().getResource("/raceTimer.fxml")); + loader.setController(new RaceTimerController(race)); + + try{ + raceTimer.getChildren().clear(); + raceTimer.getChildren().removeAll(); + raceTimer.getChildren().addAll((Pane) loader.load()); + } + catch(javafx.fxml.LoadException e){ + System.out.println(e); + } + catch(IOException e){ + System.out.println(e); + } + } + + /** + * Play each boats timeline + */ + private void playTimelines(){ + for (TimelineInfo timelineInfo : timelineInfos.values()){ + Timeline timeline = timelineInfo.getTimeline(); + + if (timeline.getStatus() == Animation.Status.PAUSED){ + timeline.play(); + } + } + raceStatus = Animation.Status.RUNNING; + } + + /** + * Pause each boats timeline + */ + private void pauseTimelines(){ + for (TimelineInfo timelineInfo : timelineInfos.values()){ + Timeline timeline = timelineInfo.getTimeline(); + + if (timeline.getStatus() == Animation.Status.RUNNING){ + timeline.pause(); + } + } + raceStatus = Animation.Status.PAUSED; + } + + /** + * Initialize the controller + */ public void initialize() { gc = canvas.getGraphicsContext2D(); gc.scale(15, 15); @@ -84,14 +138,26 @@ public class CanvasController { gc.clearRect(0, 0, 760, 360); drawCourse(); drawBoats(); + + // If race has started, draw the boats and play the timeline + if (race.getRaceTime() > 1){ + playTimelines(); + + } + // Race has not started, pause the timelines + else if (race.getRaceTime() < 1 || raceStatus == Animation.Status.RUNNING){ + pauseTimelines(); + } + } }; generateTimeline(); - // starts the timer and reads events from each boat's time line timer.start(); + loadTimerView(); + Double maxDuration = 0.0; Timeline maxTimeline = null; @@ -105,10 +171,16 @@ public class CanvasController { maxTimeline = timeline; } + // Timelines are paused by default timeline.play(); + timeline.pause(); + raceStatus = Animation.Status.RUNNING; } - maxTimeline.setOnFinished(event -> loadRaceResultView()); + maxTimeline.setOnFinished(event -> { + race.setRaceFinished(); + loadRaceResultView(); + }); //set wind direction!!!!!!! can't find another place to put my code --haoming double windDirection = new ConfigParser("doc/examples/config.xml").getWindDirection(); diff --git a/src/main/java/seng302/controllers/RaceTimerController.java b/src/main/java/seng302/controllers/RaceTimerController.java new file mode 100644 index 00000000..9c1d1130 --- /dev/null +++ b/src/main/java/seng302/controllers/RaceTimerController.java @@ -0,0 +1,78 @@ +package seng302.controllers; + +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.text.Text; +import javafx.util.Duration; +import seng302.models.Race; + +import java.net.URL; +import java.util.ResourceBundle; + +public class RaceTimerController implements Initializable{ + private Timeline timeline; + private Race race; + + @FXML + private Text timerLabel; + + /** + * 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); + } + + /** + * Controller to control the race timer + * @param race the race the timer is timing + */ + public RaceTimerController(Race race){ + this.race = race; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + timeline = new Timeline(); + timeline.setCycleCount(Timeline.INDEFINITE); + + // Run timer update every second + timeline.getKeyFrames().add( + new KeyFrame(Duration.seconds(1), + event -> { + // Stop timer if race is finished + if (this.race.isRaceFinished()){ + this.timeline.stop(); + } + else{ + timerLabel.setText(convertTimeToMinutesSeconds(race.getRaceTime())); + this.race.incrementRaceTime(); + } + }) + ); + + // Start the timer + timeline.playFromStart(); + } + + /** + * Stop the race timer + */ + public void stop(){ + timeline.stop(); + } + + /** + * Start the race timer + */ + public void start(){ + timeline.play(); + } +} diff --git a/src/main/java/seng302/models/Race.java b/src/main/java/seng302/models/Race.java index 7361ebec..2ad111e8 100644 --- a/src/main/java/seng302/models/Race.java +++ b/src/main/java/seng302/models/Race.java @@ -15,6 +15,8 @@ public class Race { private List course; // Marks in the race private long startTime = 0; private double timeScale = 1; + private boolean raceFinished = false; // Race is finished + private int raceTime = -10; // Current time in the race /** * Race class containing the boats and legs in the race @@ -106,7 +108,6 @@ public class Race { else{ Event event = new Event(time, boat, course.get(i)); events.get(boat).add(event); - } } } @@ -122,20 +123,74 @@ public class Race { generateEvents(); } + /** + * Set the race course + * @param course a list of marks in the course + */ public void addCourse(List course) { this.course = course; } + /** + * Get a list of marks in the course + * @return + */ public List getCourse() { return course; } + /** + * Get a map of the events in the race + * @return + */ public HashMap getEvents() { return events; } + /** + * Set a boat as finished + * @param boat The boat that has finished the race + */ public void setBoatFinished(Boat boat){ System.out.println(boat.getTeamName() + " finished"); 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 ++; + } } \ No newline at end of file diff --git a/src/main/resources/RaceView.fxml b/src/main/resources/RaceView.fxml index 4a726873..29165d5d 100644 --- a/src/main/resources/RaceView.fxml +++ b/src/main/resources/RaceView.fxml @@ -46,6 +46,7 @@ + diff --git a/src/main/resources/raceTimer.fxml b/src/main/resources/raceTimer.fxml new file mode 100644 index 00000000..afa99263 --- /dev/null +++ b/src/main/resources/raceTimer.fxml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/test/java/seng302/TestRaceTimer.java b/src/test/java/seng302/TestRaceTimer.java new file mode 100644 index 00000000..cd51db3b --- /dev/null +++ b/src/test/java/seng302/TestRaceTimer.java @@ -0,0 +1,26 @@ +package seng302; + +import org.junit.Test; +import seng302.controllers.RaceTimerController; +import seng302.models.Race; + +import static org.junit.Assert.assertTrue; + + +public class TestRaceTimer { + @Test + public void testPositiveTimeString(){ + RaceTimerController controller = new RaceTimerController(new Race()); + String result = controller.convertTimeToMinutesSeconds(61); + + assertTrue(result.equals("01:01")); + } + + @Test + public void testNegativeTimeString(){ + RaceTimerController controller = new RaceTimerController(new Race()); + String result = controller.convertTimeToMinutesSeconds(-61); + + assertTrue(result.equals("-01:01")); + } +}