diff --git a/mock/src/main/java/seng302/DataInput/BoatXMLReader.java b/mock/src/main/java/seng302/DataInput/BoatXMLReader.java index 20ef2086..ab87967c 100644 --- a/mock/src/main/java/seng302/DataInput/BoatXMLReader.java +++ b/mock/src/main/java/seng302/DataInput/BoatXMLReader.java @@ -31,6 +31,7 @@ public class BoatXMLReader extends XMLReader implements BoatDataSource { * Constructor for Boat XML * * @param filePath Name/path of file to read. Read as a resource. + * @param boatPolars polars used by the boats * @throws IOException error * @throws SAXException error * @throws ParserConfigurationException error diff --git a/mock/src/main/java/seng302/DataInput/RaceXMLReader.java b/mock/src/main/java/seng302/DataInput/RaceXMLReader.java index 08fa73a7..1488988c 100644 --- a/mock/src/main/java/seng302/DataInput/RaceXMLReader.java +++ b/mock/src/main/java/seng302/DataInput/RaceXMLReader.java @@ -38,6 +38,7 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource { /** * Constructor for Streamed Race XML * @param filePath path of the file + * @param boatData data for boats in race * @throws IOException error * @throws SAXException error * @throws ParserConfigurationException error @@ -53,6 +54,7 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource { /** * Constructor for Streamed Race XML * @param filePath file path to read + * @param boatData data of the boats in race * @param read whether or not to read and store the files straight away. * @throws IOException error * @throws SAXException error diff --git a/mock/src/main/java/seng302/Model/Angle.java b/mock/src/main/java/seng302/Model/Angle.java index 8fa767dd..0fa6db0c 100644 --- a/mock/src/main/java/seng302/Model/Angle.java +++ b/mock/src/main/java/seng302/Model/Angle.java @@ -54,6 +54,9 @@ public class Angle implements Comparable { return this.degrees; } + public void setDegrees(double degrees) { + this.degrees = degrees; + } /** * Returns the value of this Angle object, in radians. diff --git a/mock/src/main/java/seng302/Model/Race.java b/mock/src/main/java/seng302/Model/Race.java index a5de88c9..f0810eaf 100644 --- a/mock/src/main/java/seng302/Model/Race.java +++ b/mock/src/main/java/seng302/Model/Race.java @@ -3,7 +3,6 @@ package seng302.Model; import javafx.animation.AnimationTimer; import javafx.collections.FXCollections; import javafx.collections.ObservableList; - import seng302.Constants; import seng302.DataInput.RaceDataSource; import seng302.MockOutput; @@ -14,8 +13,6 @@ import seng302.Networking.Messages.Enums.RaceStatusEnum; import seng302.Networking.Messages.Enums.RaceTypeEnum; import seng302.Networking.Messages.RaceStatus; - -import java.io.*; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -592,9 +589,21 @@ public class Race implements Runnable { VMG newVMG = this.calculateVMG(boat, bearingBounds); + + + Azimuth azimuth = Azimuth.fromBearing(boat.getBearing()); + GPSCoordinate testBounds = GPSCoordinate.calculateNewPosition(boat.getCurrentPosition(), + (100.0 / Constants.NMToMetersConversion), azimuth); //If the new vmg improves velocity, use it. if (improvesVelocity(boat, newVMG)) { boat.setVMG(newVMG); + }else if (!GPSCoordinate.isInsideBoundary(testBounds, boundary)){ + //checks to see if the new vmg sends the boat out of bounds and if so mirrors its direction in the wind + double currDegrees = newVMG.getBearing().degrees(); + double windDirectionDegrees = this.windDirection.degrees(); + double tempHeading = (currDegrees - windDirectionDegrees +90)%360; + newVMG.getBearing().setDegrees(tempHeading); + boat.setVMG(newVMG); } diff --git a/mock/src/test/java/seng302/Model/PolarsTest.java b/mock/src/test/java/seng302/Model/PolarsTest.java index fbdcae2d..f7835662 100644 --- a/mock/src/test/java/seng302/Model/PolarsTest.java +++ b/mock/src/test/java/seng302/Model/PolarsTest.java @@ -33,6 +33,8 @@ public class PolarsTest { } + + /** * Tests if we can calculate VMG for a variety of values. */ diff --git a/visualiser/src/main/java/seng302/Controllers/RaceController.java b/visualiser/src/main/java/seng302/Controllers/RaceController.java index f52e5054..df6318f6 100644 --- a/visualiser/src/main/java/seng302/Controllers/RaceController.java +++ b/visualiser/src/main/java/seng302/Controllers/RaceController.java @@ -7,23 +7,17 @@ import javafx.fxml.FXML; import javafx.scene.chart.LineChart; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; -import javafx.geometry.Pos; import javafx.scene.control.*; import javafx.scene.layout.GridPane; -import javafx.scene.paint.Color; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; -import javafx.scene.paint.Paint; import seng302.Mock.StreamedRace; import seng302.Model.*; import seng302.VisualiserInput; import java.net.URL; import java.util.*; -import java.util.ArrayList; -import java.util.Locale; -import java.util.ResourceBundle; /** * Created by fwy13 on 15/03/2017. @@ -40,6 +34,7 @@ public class RaceController extends Controller { private ArrayList colours; private Map boatColours = new HashMap<>(); private int legNum; + private RaceClock raceClock; @FXML Pane arrow; @FXML SplitPane race; @FXML StackPane arrowPane; @@ -52,6 +47,7 @@ public class RaceController extends Controller { @FXML CheckBox showName; @FXML CheckBox showAbbrev; @FXML CheckBox showSpeed; + @FXML CheckBox showTime; @FXML Button saveAnno; @FXML Button showSetAnno; @FXML TableView boatInfoTable; @@ -138,8 +134,8 @@ public class RaceController extends Controller { xAxis.setTickMarkVisible(false); xAxis.setTickLabelsVisible(false); xAxis.setMinorTickVisible(false); - xAxis.setUpperBound(startBoats.size()*legNum); - xAxis.setTickUnit(startBoats.size()*legNum); + xAxis.setUpperBound((startBoats.size()+1)*legNum); + xAxis.setTickUnit((startBoats.size()+1)*legNum); // set y axis details @@ -173,7 +169,7 @@ public class RaceController extends Controller { * @param raceClock The RaceClock to use for the race's countdown/elapsed duration + timezone. */ public void startRace(VisualiserInput visualiserInput, RaceClock raceClock) { - StreamedRace newRace = new StreamedRace(visualiserInput, this); + //newRace.initialiseBoats(); legNum = visualiserInput.getCourse().getLegs().size()-1; @@ -211,6 +207,10 @@ public class RaceController extends Controller { }); }); + this.raceClock = raceClock; + raceMap.setRaceClock(raceClock); + + StreamedRace newRace = new StreamedRace(visualiserInput, this); initializeFPS(); initializeAnnotations(); @@ -357,6 +357,10 @@ public class RaceController extends Controller { raceMap.toggleAnnoSpeed(); raceMap.update(); }); + showTime.selectedProperty().addListener((ov, old_val, new_val) -> { + raceMap.toggleAnnoTime(); + raceMap.update(); + }); //listener to save currently selected annotation saveAnno.setOnAction(event -> { presetAnno.clear(); @@ -364,6 +368,7 @@ public class RaceController extends Controller { presetAnno.add(showAbbrev.isSelected()); presetAnno.add(showSpeed.isSelected()); presetAnno.add(showBoatPath.isSelected()); + presetAnno.add(showTime.isSelected()); }); //listener to show saved annotation showSetAnno.setOnAction(event -> { @@ -372,6 +377,7 @@ public class RaceController extends Controller { showAbbrev.setSelected(presetAnno.get(1)); showSpeed.setSelected(presetAnno.get(2)); showBoatPath.setSelected(presetAnno.get(3)); + showTime.setSelected(presetAnno.get(4)); raceMap.update(); } }); @@ -387,4 +393,8 @@ public class RaceController extends Controller { private void makeArrow() { arrowPane.getChildren().add(arrow); } + + public RaceClock getRaceClock() { + return raceClock; + } } diff --git a/visualiser/src/main/java/seng302/Mock/StreamedRace.java b/visualiser/src/main/java/seng302/Mock/StreamedRace.java index 7e0d7d3e..3b4d5f60 100644 --- a/visualiser/src/main/java/seng302/Mock/StreamedRace.java +++ b/visualiser/src/main/java/seng302/Mock/StreamedRace.java @@ -55,7 +55,8 @@ public class StreamedRace implements Runnable { if (boat != null) { Leg startLeg = new Leg(name, 0); startLeg.setEndMarker(endCompoundMark); - boat.setCurrentLeg(startLeg); + boat.setCurrentLeg(startLeg, controller.getRaceClock()); + boat.setTimeSinceLastMark(controller.getRaceClock().getTime()); } } } @@ -87,7 +88,7 @@ public class StreamedRace implements Runnable { if (legNumber >= 1 && legNumber < legs.size()) { if (boat.getCurrentLeg() != legs.get(legNumber)){ - boat.setCurrentLeg(legs.get(legNumber)); + boat.setCurrentLeg(legs.get(legNumber), controller.getRaceClock()); legChanged = true; } } diff --git a/visualiser/src/main/java/seng302/Model/Boat.java b/visualiser/src/main/java/seng302/Model/Boat.java index 6e553318..a99ed148 100644 --- a/visualiser/src/main/java/seng302/Model/Boat.java +++ b/visualiser/src/main/java/seng302/Model/Boat.java @@ -6,6 +6,7 @@ import org.geotools.referencing.GeodeticCalculator; import seng302.GPSCoordinate; import java.awt.geom.Point2D; +import java.time.ZonedDateTime; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; @@ -31,6 +32,8 @@ public class Boat { private final Queue track = new ConcurrentLinkedQueue<>(); private long nextValidTime = 0; + private ZonedDateTime timeSinceLastMark; + /** * Boat initializer which keeps all of the information of the boat. * @@ -181,9 +184,10 @@ public class Boat { return currentLeg; } - public void setCurrentLeg(Leg currentLeg) { + public void setCurrentLeg(Leg currentLeg, RaceClock raceClock) { this.currentLeg = currentLeg; this.currentLegName.setValue(currentLeg.getName()); + this.setTimeSinceLastMark(raceClock.getTime()); } public boolean isFinished() { @@ -236,4 +240,12 @@ public class Boat { public void setDnf(boolean dnf) { this.dnf = dnf; } + + public ZonedDateTime getTimeSinceLastMark() { + return timeSinceLastMark; + } + + public void setTimeSinceLastMark(ZonedDateTime timeSinceLastMark) { + this.timeSinceLastMark = timeSinceLastMark; + } } diff --git a/visualiser/src/main/java/seng302/Model/ResizableRaceCanvas.java b/visualiser/src/main/java/seng302/Model/ResizableRaceCanvas.java index 7a47b06c..f3ba5262 100644 --- a/visualiser/src/main/java/seng302/Model/ResizableRaceCanvas.java +++ b/visualiser/src/main/java/seng302/Model/ResizableRaceCanvas.java @@ -12,6 +12,10 @@ import seng302.Mock.StreamedCourse; import seng302.RaceDataSource; import seng302.RaceMap; +import java.time.Duration; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; + import java.util.*; /** @@ -28,12 +32,15 @@ public class ResizableRaceCanvas extends ResizableCanvas { private boolean annoAbbrev = true; private boolean annoSpeed = true; private boolean annoPath = true; + private boolean annoTimeSinceLastMark = true; private List colours; private final List markers; private final RaceDataSource raceData; private Map boatColours = new HashMap<>(); private Node arrow; + private RaceClock raceClock; + public ResizableRaceCanvas(RaceDataSource raceData) { super(); @@ -169,8 +176,9 @@ public class ResizableRaceCanvas extends ResizableCanvas { * @param abbrev abbreviation of the boat name * @param speed speed of the boat * @param coordinate coordinate the text appears + * @param timeSinceLastMark time since the last mark was passed */ - private void displayText(String name, String abbrev, double speed, GraphCoordinate coordinate) { + private void displayText(String name, String abbrev, double speed, GraphCoordinate coordinate, ZonedDateTime timeSinceLastMark) { String text = ""; //Check name toggle value if (annoName){ @@ -182,7 +190,12 @@ public class ResizableRaceCanvas extends ResizableCanvas { } //Check speed toggle value if (annoSpeed){ - text += String.format("%.2fkn", speed); + text += String.format("%.2fkn ", speed); + } + //Check time since last mark toggle value + if(annoTimeSinceLastMark){ + Duration timeSince = Duration.between(timeSinceLastMark, raceClock.getTime()); + text += String.format("%d", timeSince.getSeconds()); } //String text = String.format("%s, %2$.2fkn", name, speed); long xCoord = coordinate.getX() + 20; @@ -264,10 +277,18 @@ public class ResizableRaceCanvas extends ResizableCanvas { annoName = !annoName; } + /** + * Toggle boat path display in annotation + */ public void toggleBoatPath() { annoPath = !annoPath; } + /** + * Toggle boat time display in annotation + */ + public void toggleAnnoTime() { annoTimeSinceLastMark = !annoTimeSinceLastMark;} + /** * Toggle abbreviation display in annotation */ @@ -303,10 +324,14 @@ public class ResizableRaceCanvas extends ResizableCanvas { displayBoat(boat, 0, boatColours.get(sourceID)); } - if (raceAnno) - displayText(boat.toString(), boat.getAbbrev(), boat.getVelocity(), this.map.convertGPS(boat.getCurrentPosition())); - //TODO this needs to be fixed. - drawTrack(boat, boatColours.get(sourceID)); + if (raceAnno) { + if (Duration.between(boat.getTimeSinceLastMark(), raceClock.getTime()).getSeconds() < 0) { + boat.setTimeSinceLastMark(raceClock.getTime()); + } + displayText(boat.toString(), boat.getAbbrev(), boat.getVelocity(), this.map.convertGPS(boat.getCurrentPosition()), boat.getTimeSinceLastMark()); + //TODO this needs to be fixed. + drawTrack(boat, boatColours.get(sourceID)); + } } } } @@ -345,6 +370,10 @@ public class ResizableRaceCanvas extends ResizableCanvas { this.arrow = arrow; } + public void setRaceClock(RaceClock raceClock) { + this.raceClock = raceClock; + } + private void mapBoatColours() { int currentColour = 0; for (Boat boat : boats) { diff --git a/visualiser/src/main/resources/scenes/race.fxml b/visualiser/src/main/resources/scenes/race.fxml index 70f46745..e21436f4 100644 --- a/visualiser/src/main/resources/scenes/race.fxml +++ b/visualiser/src/main/resources/scenes/race.fxml @@ -33,8 +33,9 @@ -