From 4a6978ff7988b730fe66059493be01257c18a7a9 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 29 Mar 2017 12:58:49 +1300 Subject: [PATCH 1/2] Fxml refactored, partway through refactoring controllers (app does not run) #story[463] --- .../seng302/controllers/CanvasController.java | 71 +------- .../java/seng302/controllers/Controller.java | 38 +++++ .../controllers/RaceViewController.java | 155 ++++++++++++++++++ src/main/resources/MainView.fxml | 2 +- src/test/java/seng302/TestRaceTimer.java | 1 - 5 files changed, 202 insertions(+), 65 deletions(-) create mode 100644 src/main/java/seng302/controllers/Controller.java create mode 100644 src/main/java/seng302/controllers/RaceViewController.java diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index c8a8797f..e9ce5d58 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -35,56 +35,32 @@ import java.util.*; * Modified by Haoming Yin (hyi25) on 20/3/2017. */ public class CanvasController { + @FXML - private AnchorPane contentAnchorPane; - @FXML - private Text windArrowText, windDirectionText; - @FXML - private Pane raceTimer; - @FXML - private BoatPositionController teamPositionsController; - @FXML - private CheckBox toggleAnnotation, toggleFps; + private AnchorPane canvasPane; private ResizableCanvas canvas; - private Race race; private GraphicsContext gc; private HashMap timelineInfos; - - private AnchorPane raceResults; - - private final double ORIGIN_LAT = 32.321504; - private final double ORIGIN_LON = -64.857063; - private Animation.Status raceStatus = Animation.Status.PAUSED; - private final int SCALE = 16000; - - private boolean annotationCheck = true; - private boolean displayFps = true; - ///test private HashSet headingTest = new HashSet<>(); - /** - * Initialize the controller - */ public void initialize() { canvas = new ResizableCanvas(); - contentAnchorPane.getChildren().add(canvas); + canvasPane.getChildren().add(canvas); // Bind canvas size to stack pane size. canvas.widthProperty().bind( - contentAnchorPane.widthProperty()); + canvasPane.widthProperty()); canvas.heightProperty().bind( - contentAnchorPane.heightProperty()); + canvasPane.heightProperty()); gc = canvas.getGraphicsContext2D(); - RaceController raceController = new RaceController(); - raceController.initializeRace(); - race = raceController.getRace(); - timelineInfos = new HashMap<>(); +// +// timelineInfos = new HashMap<>(); // overriding the handle so that it can clean canvas and redraw boats and course marks AnimationTimer timer = new AnimationTimer() { @@ -154,18 +130,7 @@ public class CanvasController { loadRaceResultView(); }); - toggleAnnotation.selectedProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { - annotationCheck = !annotationCheck; - } - }); - toggleFps.selectedProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { - displayFps = !displayFps; - } - }); + //set wind direction!!!!!!! can't find another place to put my code --haoming double windDirection = new ConfigParser("/config.xml").getWindDirection(); @@ -224,26 +189,6 @@ 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 */ diff --git a/src/main/java/seng302/controllers/Controller.java b/src/main/java/seng302/controllers/Controller.java new file mode 100644 index 00000000..22f4dfcb --- /dev/null +++ b/src/main/java/seng302/controllers/Controller.java @@ -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("/RaceView.fxml"); + } +} diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java new file mode 100644 index 00000000..0b57d849 --- /dev/null +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -0,0 +1,155 @@ +package seng302.controllers; + +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.fxml.FXML; +import javafx.scene.control.CheckBox; +import javafx.scene.layout.AnchorPane; +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 java.net.URL; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.ResourceBundle; + +/** + * 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; + + private boolean displayAnnotations; + private boolean displayFps; + private Timeline timeline; + private Race race; + private ArrayList boatOrder = new ArrayList<>(); + + private final double ORIGIN_LAT = 32.321504; + private final double ORIGIN_LON = -64.857063; + private final int SCALE = 16000; + +// /** +// * Controller to control the race timer +// * @param race the race the timer is timing +// */ +// public RaceTimerController(Race race){ +// this.race = race; +// } + + public void initialize() { + RaceController raceController = new RaceController(); + raceController.initializeRace(); + race = raceController.getRace(); + + initializeTimer(); + initializeSettings(); + + + + } + + private void initializeSettings(){ + displayAnnotations = true; + displayFps = true; + + toggleAnnotation.selectedProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { + displayAnnotations = !displayAnnotations; + } + }); + toggleFps.selectedProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) { + displayFps = !displayFps; + } + }); + } + + private void initializeTimer(){ + 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(); + } + + public void handleEvent(Event event) { + Boat boat = event.getBoat(); + boatOrder.remove(boat); + boat.setMarkLastPast(event.getMarkPosInRace()); + boatOrder.add(boat); + boatOrder.sort(new Comparator() { + @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); + } + + /** + * Stop the race timer + */ + public void stop() { + timeline.stop(); + } + + /** + * Start the race timer + */ + public void start() { + timeline.play(); + } +} \ No newline at end of file diff --git a/src/main/resources/MainView.fxml b/src/main/resources/MainView.fxml index 9c1ea80f..ac0b944e 100644 --- a/src/main/resources/MainView.fxml +++ b/src/main/resources/MainView.fxml @@ -4,7 +4,7 @@ - + diff --git a/src/test/java/seng302/TestRaceTimer.java b/src/test/java/seng302/TestRaceTimer.java index cd51db3b..138cd7ad 100644 --- a/src/test/java/seng302/TestRaceTimer.java +++ b/src/test/java/seng302/TestRaceTimer.java @@ -1,7 +1,6 @@ package seng302; import org.junit.Test; -import seng302.controllers.RaceTimerController; import seng302.models.Race; import static org.junit.Assert.assertTrue; From a95d030817694fbf5852be3ee060eb45c7ed9122 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 29 Mar 2017 14:59:37 +1300 Subject: [PATCH 2/2] Controllers and Fxml nicely refactored, tests still broken #story[463] --- src/main/java/seng302/App.java | 2 +- .../controllers/BoatPositionController.java | 49 ----- .../seng302/controllers/CanvasController.java | 187 ++--------------- .../java/seng302/controllers/Controller.java | 2 +- .../controllers/MasterViewController.java | 38 ---- .../seng302/controllers/RaceController.java | 6 +- .../controllers/RaceTimerController.java | 78 ------- .../controllers/RaceViewController.java | 195 ++++++++++++++---- src/main/resources/TeamPositions.fxml | 11 - src/main/resources/{ => config}/config.xml | 0 src/main/resources/{ => config}/course.xml | 0 src/main/resources/{ => config}/teams.xml | 0 src/main/resources/raceTimer.fxml | 15 -- src/main/resources/views/CanvasView.fxml | 7 + .../resources/{ => views}/FinishView.fxml | 0 src/main/resources/{ => views}/MainView.fxml | 0 src/main/resources/{ => views}/RaceView.fxml | 23 ++- src/test/java/seng302/TestRaceTimer.java | 30 +-- .../models/parsers/ConfigParserTest.java | 2 +- .../models/parsers/CourseParserTest.java | 2 +- .../models/parsers/TeamsParserTest.java | 2 +- 21 files changed, 225 insertions(+), 424 deletions(-) delete mode 100644 src/main/java/seng302/controllers/BoatPositionController.java delete mode 100644 src/main/java/seng302/controllers/MasterViewController.java delete mode 100644 src/main/java/seng302/controllers/RaceTimerController.java delete mode 100644 src/main/resources/TeamPositions.fxml rename src/main/resources/{ => config}/config.xml (100%) rename src/main/resources/{ => config}/course.xml (100%) rename src/main/resources/{ => config}/teams.xml (100%) delete mode 100644 src/main/resources/raceTimer.fxml create mode 100644 src/main/resources/views/CanvasView.fxml rename src/main/resources/{ => views}/FinishView.fxml (100%) rename src/main/resources/{ => views}/MainView.fxml (100%) rename src/main/resources/{ => views}/RaceView.fxml (72%) diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index b88f607b..0637d2cc 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -10,7 +10,7 @@ public class App extends Application { @Override public void start(Stage primaryStage) throws Exception { - Parent root = FXMLLoader.load(getClass().getResource("/MainView.fxml")); + Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml")); primaryStage.setTitle("RaceVision"); primaryStage.setScene(new Scene(root)); diff --git a/src/main/java/seng302/controllers/BoatPositionController.java b/src/main/java/seng302/controllers/BoatPositionController.java deleted file mode 100644 index 8ca99269..00000000 --- a/src/main/java/seng302/controllers/BoatPositionController.java +++ /dev/null @@ -1,49 +0,0 @@ -package seng302.controllers; - -import javafx.fxml.FXML; -import javafx.scene.layout.VBox; -import javafx.scene.text.Text; -import seng302.models.Boat; -import seng302.models.Event; - -import java.util.*; - -/** - * Created by ptg19 on 23/03/17. - */ -public class BoatPositionController { - @FXML - private VBox positionVbox; - - private ArrayList boatOrder = new ArrayList<>(); - - public void initialize() { - } - - public void handleEvent(Event event){ - Boat boat = event.getBoat(); - boatOrder.remove(boat); - boat.setMarkLastPast(event.getMarkPosInRace()); - boatOrder.add(boat); - boatOrder.sort(new Comparator() { - @Override - public int compare(Boat b1, Boat b2) { - return b2.getMarkLastPast() - b1.getMarkLastPast(); - } - }); - displayBoats(); - } - - private void displayBoats(){ - positionVbox.getChildren().clear(); - positionVbox.getChildren().removeAll(); - - for (Boat boat: boatOrder){ - positionVbox.getChildren().add(new Text(boat.getShortName() + " " + boat.getSpeedInKnots() + " Knots")); - } - } - - public ArrayList getBoatOrder() { - return boatOrder; - } -} diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index e9ce5d58..0ec93c83 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -1,33 +1,19 @@ package seng302.controllers; import javafx.animation.*; -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.geometry.NodeOrientation; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; -import javafx.scene.control.CheckBox; import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.text.Font; -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.mark.GateMark; import seng302.models.mark.Mark; import seng302.models.mark.MarkType; import seng302.models.mark.SingleMark; -import seng302.models.parsers.ConfigParser; -import java.io.IOException; import java.util.*; /** @@ -39,28 +25,26 @@ public class CanvasController { @FXML private AnchorPane canvasPane; + private RaceViewController raceViewController; private ResizableCanvas canvas; - private Race race; private GraphicsContext gc; - private HashMap timelineInfos; - private Animation.Status raceStatus = Animation.Status.PAUSED; - ///test - private HashSet headingTest = new HashSet<>(); + 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()); + canvas.widthProperty().bind(canvasPane.widthProperty()); + canvas.heightProperty().bind(canvasPane.heightProperty()); gc = canvas.getGraphicsContext2D(); -// -// timelineInfos = new HashMap<>(); + // overriding the handle so that it can clean canvas and redraw boats and course marks AnimationTimer timer = new AnimationTimer() { @@ -80,13 +64,12 @@ public class CanvasController { drawFps(lastFpsCount); // If race has started, draw the boats and play the timeline - if (race.getRaceTime() > 1){ - playTimelines(); - + if (raceViewController.getRace().getRaceTime() > 1){ + raceViewController.playTimelines(); } // Race has not started, pause the timelines - else if (race.getRaceTime() < 1 || raceStatus == Animation.Status.RUNNING){ - pauseTimelines(); + else { + raceViewController.pauseTimelines(); } lastUpdate = now; fpsCount ++; @@ -98,44 +81,7 @@ public class CanvasController { } } }; - - try{ - generateTimelines(); - } - catch (Exception e){ - e.printStackTrace(); - } timer.start(); - loadTimerView(); - - 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(); - raceStatus = Animation.Status.RUNNING; - } - - maxTimeline.setOnFinished(event -> { - race.setRaceFinished(); - loadRaceResultView(); - }); - - - - //set wind direction!!!!!!! can't find another place to put my code --haoming - double windDirection = new ConfigParser("/config.xml").getWindDirection(); - windDirectionText.setText(String.format("%.1f°", windDirection)); - windArrowText.setRotate(windDirection); } class ResizableCanvas extends Canvas { @@ -170,98 +116,8 @@ public class CanvasController { } } - /** - * 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)); - - 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); - } - } - - /** - * 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; - } - - /** - * Generates time line for each boat, and stores time time into timelineInfos hash map - */ - private void generateTimelines() { - HashMap 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 keyFrames = new ArrayList<>(); - List 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() / 60 / 60 / 5), - onFinished -> {race.setBoatFinished(boat); teamPositionsController.handleEvent(event);}, - new KeyValue(x, event.getThisMark().getLatitude()), - new KeyValue(y, event.getThisMark().getLongitude()) - ) - ); - } else { - keyFrames.add( - new KeyFrame(Duration.seconds(event.getTime() / 60 / 60 / 5), - onFinished ->{ - teamPositionsController.handleEvent(event); - boat.setHeading(event.getBoatHeading()); - }, - new KeyValue(x, event.getThisMark().getLatitude()), - new KeyValue(y, event.getThisMark().getLongitude()) - ) - ); - } - } - - // uses the lists generated above to create a Timeline for the boat. - timelineInfos.put(boat, new TimelineInfo(new Timeline(keyFrames.toArray(new KeyFrame[keyFrames.size()])), x, y)); - } - } - private void drawFps(int fps){ - if (displayFps){ + if (raceViewController.isDisplayFps()){ gc.setFill(Color.BLACK); gc.setFont(new Font(14)); gc.setLineWidth(3); @@ -273,6 +129,7 @@ public class CanvasController { * Draws all the boats. */ private void drawBoats() { + Map timelineInfos = raceViewController.getTimelineInfos(); for (Boat boat : timelineInfos.keySet()) { TimelineInfo timelineInfo = timelineInfos.get(boat); @@ -318,7 +175,7 @@ public class CanvasController { gc.setFill(color); - if (annotationCheck) { + if (raceViewController.isDisplayAnnotations()) { // Set boat text gc.setFont(new Font(14)); gc.setLineWidth(3); @@ -338,7 +195,7 @@ public class CanvasController { double[] yy = new double[] {p1.y + y, p2.y + y, y, p3.y + y}; gc.fillPolygon(xx, yy, 4); - if (annotationCheck){ + if (raceViewController.isDisplayAnnotations()){ drawWake(gc, x, y, speed, color, heading); } } @@ -368,7 +225,7 @@ public class CanvasController { * Draws the course. */ private void drawCourse() { - for (Mark mark : race.getCourse()) { + for (Mark mark : raceViewController.getRace().getCourse()) { if (mark.getMarkType() == MarkType.SINGLE_MARK) { drawSingleMark((SingleMark) mark, Color.BLACK); } else if (mark.getMarkType() == MarkType.GATE_MARK) { @@ -423,8 +280,4 @@ public class CanvasController { gc.setLineWidth(1); gc.strokeLine(x1, y1, x2, y2); } - - public Race getRace(){ - return this.race; - } } \ No newline at end of file diff --git a/src/main/java/seng302/controllers/Controller.java b/src/main/java/seng302/controllers/Controller.java index 22f4dfcb..1892d472 100644 --- a/src/main/java/seng302/controllers/Controller.java +++ b/src/main/java/seng302/controllers/Controller.java @@ -33,6 +33,6 @@ public class Controller implements Initializable { @Override public void initialize(URL location, ResourceBundle resources) { - setContentPane("/RaceView.fxml"); + setContentPane("/views/RaceView.fxml"); } } diff --git a/src/main/java/seng302/controllers/MasterViewController.java b/src/main/java/seng302/controllers/MasterViewController.java deleted file mode 100644 index 72de0419..00000000 --- a/src/main/java/seng302/controllers/MasterViewController.java +++ /dev/null @@ -1,38 +0,0 @@ -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 MasterViewController 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("/RaceView.fxml"); - } -} diff --git a/src/main/java/seng302/controllers/RaceController.java b/src/main/java/seng302/controllers/RaceController.java index a079440d..b5fa2847 100644 --- a/src/main/java/seng302/controllers/RaceController.java +++ b/src/main/java/seng302/controllers/RaceController.java @@ -20,8 +20,8 @@ public class RaceController { Race race = null; public void initializeRace() { - String raceConfigFile = "/config.xml"; - String teamsConfigFile = "/teams.xml"; + String raceConfigFile = "/config/config.xml"; + String teamsConfigFile = "/config/teams.xml"; try { race = createRace(raceConfigFile, teamsConfigFile); @@ -68,7 +68,7 @@ public class RaceController { return null; } - CourseParser course = new CourseParser("/course.xml"); + CourseParser course = new CourseParser("/config/course.xml"); race.addCourse(course.getCourse()); return race; diff --git a/src/main/java/seng302/controllers/RaceTimerController.java b/src/main/java/seng302/controllers/RaceTimerController.java deleted file mode 100644 index 9c1d1130..00000000 --- a/src/main/java/seng302/controllers/RaceTimerController.java +++ /dev/null @@ -1,78 +0,0 @@ -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/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 0b57d849..fe5a9326 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -1,23 +1,29 @@ 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.net.URL; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.ResourceBundle; +import java.io.IOException; +import java.util.*; /** * Created by ptg19 on 29/03/17. @@ -33,35 +39,35 @@ public class RaceViewController { private AnchorPane contentAnchorPane; @FXML private Text windArrowText, windDirectionText; + @FXML + private CanvasController includedCanvasController; private boolean displayAnnotations; private boolean displayFps; - private Timeline timeline; - private Race race; + private Timeline timerTimeline; + private Map timelineInfos = new HashMap<>(); private ArrayList boatOrder = new ArrayList<>(); - - private final double ORIGIN_LAT = 32.321504; - private final double ORIGIN_LON = -64.857063; - private final int SCALE = 16000; - -// /** -// * Controller to control the race timer -// * @param race the race the timer is timing -// */ -// public RaceTimerController(Race race){ -// this.race = race; -// } + 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(){ @@ -83,15 +89,15 @@ public class RaceViewController { } private void initializeTimer(){ - timeline = new Timeline(); - timeline.setCycleCount(Timeline.INDEFINITE); + timerTimeline = new Timeline(); + timerTimeline.setCycleCount(Timeline.INDEFINITE); // Run timer update every second - timeline.getKeyFrames().add( + timerTimeline.getKeyFrames().add( new KeyFrame(Duration.seconds(1), event -> { // Stop timer if race is finished if (this.race.isRaceFinished()) { - this.timeline.stop(); + this.timerTimeline.stop(); } else { timerLabel.setText(convertTimeToMinutesSeconds(race.getRaceTime())); this.race.incrementRaceTime(); @@ -100,7 +106,117 @@ public class RaceViewController { ); // Start the timer - timeline.playFromStart(); + timerTimeline.playFromStart(); + } + + /** + * Generates time line for each boat, and stores time time into timelineInfos hash map + */ + private void initializeTimelines() { + HashMap 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 keyFrames = new ArrayList<>(); + List 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() / 60 / 60 / 5), + 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() / 60 / 60 / 5), + 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) { @@ -139,17 +255,26 @@ public class RaceViewController { return String.format("%02d:%02d", time / 60, time % 60); } - /** - * Stop the race timer - */ - public void stop() { - timeline.stop(); + public void stopTimer() { + timerTimeline.stop(); + } + public void startTimer() { + timerTimeline.play(); } - /** - * Start the race timer - */ - public void start() { - timeline.play(); + public boolean isDisplayFps() { + return displayFps; + } + + public boolean isDisplayAnnotations() { + return displayAnnotations; + } + + public Race getRace() { + return race; + } + + public Map getTimelineInfos() { + return timelineInfos; } } \ No newline at end of file diff --git a/src/main/resources/TeamPositions.fxml b/src/main/resources/TeamPositions.fxml deleted file mode 100644 index 20b9a705..00000000 --- a/src/main/resources/TeamPositions.fxml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/src/main/resources/config.xml b/src/main/resources/config/config.xml similarity index 100% rename from src/main/resources/config.xml rename to src/main/resources/config/config.xml diff --git a/src/main/resources/course.xml b/src/main/resources/config/course.xml similarity index 100% rename from src/main/resources/course.xml rename to src/main/resources/config/course.xml diff --git a/src/main/resources/teams.xml b/src/main/resources/config/teams.xml similarity index 100% rename from src/main/resources/teams.xml rename to src/main/resources/config/teams.xml diff --git a/src/main/resources/raceTimer.fxml b/src/main/resources/raceTimer.fxml deleted file mode 100644 index afa99263..00000000 --- a/src/main/resources/raceTimer.fxml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/main/resources/views/CanvasView.fxml b/src/main/resources/views/CanvasView.fxml new file mode 100644 index 00000000..bc16ad7e --- /dev/null +++ b/src/main/resources/views/CanvasView.fxml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/FinishView.fxml b/src/main/resources/views/FinishView.fxml similarity index 100% rename from src/main/resources/FinishView.fxml rename to src/main/resources/views/FinishView.fxml diff --git a/src/main/resources/MainView.fxml b/src/main/resources/views/MainView.fxml similarity index 100% rename from src/main/resources/MainView.fxml rename to src/main/resources/views/MainView.fxml diff --git a/src/main/resources/RaceView.fxml b/src/main/resources/views/RaceView.fxml similarity index 72% rename from src/main/resources/RaceView.fxml rename to src/main/resources/views/RaceView.fxml index ee30c84c..f7fcbfeb 100644 --- a/src/main/resources/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -9,7 +9,7 @@ - + @@ -37,16 +37,23 @@ - - - - - - + + + + + + + + + + - + + + + diff --git a/src/test/java/seng302/TestRaceTimer.java b/src/test/java/seng302/TestRaceTimer.java index 138cd7ad..6756c0a0 100644 --- a/src/test/java/seng302/TestRaceTimer.java +++ b/src/test/java/seng302/TestRaceTimer.java @@ -7,19 +7,19 @@ 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")); - } +// @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")); +// } } diff --git a/src/test/java/seng302/models/parsers/ConfigParserTest.java b/src/test/java/seng302/models/parsers/ConfigParserTest.java index a3dade74..e75db85d 100644 --- a/src/test/java/seng302/models/parsers/ConfigParserTest.java +++ b/src/test/java/seng302/models/parsers/ConfigParserTest.java @@ -14,7 +14,7 @@ public class ConfigParserTest { @Before public void initializeParser() throws Exception { - cp = new ConfigParser("/config.xml"); + cp = new ConfigParser("/config/config.xml"); } @Test diff --git a/src/test/java/seng302/models/parsers/CourseParserTest.java b/src/test/java/seng302/models/parsers/CourseParserTest.java index b413f723..865caec6 100644 --- a/src/test/java/seng302/models/parsers/CourseParserTest.java +++ b/src/test/java/seng302/models/parsers/CourseParserTest.java @@ -18,7 +18,7 @@ public class CourseParserTest { @Before public void initializeParser() throws Exception { - cp = new CourseParser("/course.xml"); + cp = new CourseParser("/config/course.xml"); } @Test diff --git a/src/test/java/seng302/models/parsers/TeamsParserTest.java b/src/test/java/seng302/models/parsers/TeamsParserTest.java index c47b419b..75a00ee5 100644 --- a/src/test/java/seng302/models/parsers/TeamsParserTest.java +++ b/src/test/java/seng302/models/parsers/TeamsParserTest.java @@ -16,7 +16,7 @@ public class TeamsParserTest { private TeamsParser tp; @Before public void readFile() { - tp = new TeamsParser("/teams.xml"); + tp = new TeamsParser("/config/teams.xml"); } @Test