diff --git a/src/main/java/seng302/Controllers/RaceController.java b/src/main/java/seng302/Controllers/RaceController.java index 7f9b6421..88556461 100644 --- a/src/main/java/seng302/Controllers/RaceController.java +++ b/src/main/java/seng302/Controllers/RaceController.java @@ -35,6 +35,9 @@ public class RaceController extends Controller { SplitPane race; @FXML CheckBox showFPS; + + @FXML + CheckBox showBoatPath; @FXML CheckBox showAnnotations; @FXML @@ -235,6 +238,15 @@ public class RaceController extends Controller { raceMap.update(); } }); + + //listener for show abbreviation for annotation + showBoatPath.selectedProperty().addListener(new ChangeListener() { + public void changed(ObservableValue ov, + Boolean old_val, Boolean new_val) { + raceMap.toggleBoatPath(); + raceMap.update(); + } + }); //listener to show speed for annotation showSpeed.selectedProperty().addListener(new ChangeListener() { public void changed(ObservableValue ov, @@ -251,6 +263,7 @@ public class RaceController extends Controller { presetAnno.add(showName.isSelected()); presetAnno.add(showAbbrev.isSelected()); presetAnno.add(showSpeed.isSelected()); + presetAnno.add(showBoatPath.isSelected()); } }); //listener to show saved annotation @@ -261,9 +274,10 @@ public class RaceController extends Controller { showName.setSelected(presetAnno.get(0)); showAbbrev.setSelected(presetAnno.get(1)); showSpeed.setSelected(presetAnno.get(2)); + showBoatPath.setSelected(presetAnno.get(3)); raceMap.update(); } } }); } -} +} \ No newline at end of file diff --git a/src/main/java/seng302/Model/BoatInRace.java b/src/main/java/seng302/Model/BoatInRace.java index d306eb4c..e1039630 100644 --- a/src/main/java/seng302/Model/BoatInRace.java +++ b/src/main/java/seng302/Model/BoatInRace.java @@ -1,6 +1,5 @@ package seng302.Model; -import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.scene.paint.Color; @@ -9,6 +8,8 @@ import seng302.Constants; import seng302.GPSCoordinate; import java.awt.geom.Point2D; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; /** * Boat in the Race extends Boat. @@ -27,6 +28,14 @@ public class BoatInRace extends Boat { private boolean started = false; private StringProperty position; + private Queue track = new ConcurrentLinkedQueue<>(); + private long nextValidTime = 0; + + private static final float BASE_TRACK_POINT_TIME_INTERVAL = 5000; + private static float trackPointTimeInterval = 5000; // every 1 seconds + private final int TRACK_POINT_LIMIT = 10; + private boolean trackVisible = true; + /** * Constructor method. * @@ -251,4 +260,53 @@ public class BoatInRace extends Boat { public void setPosition(String position) { this.position.set(position); } + + /** + * Adds a new point to boat's track. + * @param coordinate of point on track + * @return whether add is successful + * @see seng302.Model.TrackPoint + */ + public boolean addTrackPoint(GPSCoordinate coordinate) { + Boolean added = System.currentTimeMillis() >= nextValidTime; + long currentTime = System.currentTimeMillis(); + if (added && this.started) { + nextValidTime = currentTime + (long) trackPointTimeInterval; + track.add(new TrackPoint(coordinate, currentTime, TRACK_POINT_LIMIT * (long) trackPointTimeInterval)); + } + return added; + } + + /** + * Returns the boat's sampled track between start of race and current time. + * @return queue of track points + * @see seng302.Model.TrackPoint + */ + public Queue getTrack() { + return track; + } + + /** + * Returns whether track is visible + * @return true if visible + */ + public boolean isTrackVisible() { + return trackVisible; + } + + /** + * Sets track visibility. + * @param trackVisible visible if true. + */ + public void setTrackVisible(boolean trackVisible) { + this.trackVisible = trackVisible; + } + + public static float getBaseTrackPointTimeInterval() { + return BASE_TRACK_POINT_TIME_INTERVAL; + } + + public static void setTrackPointTimeInterval(float value) { + trackPointTimeInterval = value; + } } diff --git a/src/main/java/seng302/Model/Race.java b/src/main/java/seng302/Model/Race.java index 825b7145..54e14245 100644 --- a/src/main/java/seng302/Model/Race.java +++ b/src/main/java/seng302/Model/Race.java @@ -72,6 +72,7 @@ public abstract class Race implements Runnable { String name = officialStart.getName(); Marker endMarker = officialStart.getEndMarker(); + BoatInRace.setTrackPointTimeInterval(BoatInRace.getBaseTrackPointTimeInterval() / scaleFactor); ArrayList startMarkers = getSpreadStartingPositions(); for (int i = 0; i < startingBoats.size(); i++) { @@ -214,20 +215,23 @@ public abstract class Race implements Runnable { for (BoatInRace boat : startingBoats) { if (boat != null && !boat.isFinished()) { + boat.addTrackPoint(boat.getCurrentPosition()); updatePosition(boat, Math.round(1000 / lastFPS) > 20 ? 15 : Math.round(1000 / lastFPS)); checkPosition(boat, totalTimeElapsed); } } - if (controller != null) controller.updateMap(startingBoats); +// if (controller != null) controller.updateMap(startingBoats); if (timerEnabled) updateTime(calcTimer()); - } else { - //Exit animation timer - updateTime(calcTimer()); - updateFPS(0); //race ended so fps = 0 - stop(); //exit animation timer } + controller.updateMap(startingBoats); +// } else { +// //Exit animation timer +// updateTime(calcTimer()); +// updateFPS(0); //race ended so fps = 0 +// stop(); //exit animation timer +// } fps++; if ((System.currentTimeMillis() - timeCurrent) > 1000) { updateFPS(fps); diff --git a/src/main/java/seng302/Model/ResizableRaceCanvas.java b/src/main/java/seng302/Model/ResizableRaceCanvas.java index befa173c..bd056749 100644 --- a/src/main/java/seng302/Model/ResizableRaceCanvas.java +++ b/src/main/java/seng302/Model/ResizableRaceCanvas.java @@ -29,6 +29,7 @@ public class ResizableRaceCanvas extends Canvas { private boolean annoName = true; private boolean annoAbbrev = true; private boolean annoSpeed = true; + private boolean annoPath = true; private ArrayList raceBoundaries; double[] xpoints = {}, ypoints = {}; @@ -39,6 +40,9 @@ public class ResizableRaceCanvas extends Canvas { */ public void setBoats(BoatInRace[] boats) { this.boats = boats; + for (BoatInRace boat : boats) { + System.out.println(); + } } public ResizableRaceCanvas(RaceMap map) { @@ -295,6 +299,14 @@ public class ResizableRaceCanvas extends Canvas { } } + public void toggleBoatPath() { + if (annoPath) { + annoPath = false; + } else { + annoPath = true; + } + } + /** * Toggle abbreviation display in annotation */ @@ -338,6 +350,24 @@ public class ResizableRaceCanvas extends Canvas { if (raceAnno) displayText(boat.toString(), boat.getAbbrev(), boat.getVelocity(), this.map.convertGPS(boat.getCurrentPosition())); + + if(boat.isTrackVisible()) drawTrack(boat); + } + } + } + + /** + * Draws all track points for a given boat. Colour is set by boat, opacity by track point. + * @param boat whose track is displayed + * @see seng302.Model.TrackPoint + */ + private void drawTrack(BoatInRace boat) { + if (annoPath) { + for (TrackPoint point : boat.getTrack()) { + GraphCoordinate scaledCoordinate = this.map.convertGPS(point.getCoordinate()); + Color boatColour = boat.getColour(); + gc.setFill(new Color(boatColour.getRed(), boatColour.getGreen(), boatColour.getBlue(), point.getAlpha())); + gc.fillOval(scaledCoordinate.getX(), scaledCoordinate.getY(), 5, 5); } } } diff --git a/src/main/java/seng302/Model/TrackPoint.java b/src/main/java/seng302/Model/TrackPoint.java new file mode 100644 index 00000000..a2c30aa0 --- /dev/null +++ b/src/main/java/seng302/Model/TrackPoint.java @@ -0,0 +1,50 @@ +package seng302.Model; + +import seng302.GPSCoordinate; + +/** + * Created by cbt24 on 7/04/17. + */ +public class TrackPoint { + private GPSCoordinate coordinate; + private long timeAdded; + private long expiry; + private double minAlpha; + + /** + * Creates a new track point with fixed GPS coordinates and time, to reach minimum opacity on expiry. + * @param coordinate position of point on physical race map + * @param timeAdded system clock at time of addition + * @param expiry time to minimum opacity after added + */ + public TrackPoint(GPSCoordinate coordinate, long timeAdded, long expiry) { + this.coordinate = coordinate; + this.timeAdded = timeAdded; + this.expiry = expiry; + this.minAlpha = 0.1; + } + + /** + * Gets the position of the point on physical race map. + * @return GPS coordinate of point + */ + public GPSCoordinate getCoordinate() { + return coordinate; + } + + /** + * Gets opacity of point scaled by age in proportion to expiry, between 1 and minimum opacity inclusive. + * @return greater of minimum opacity and scaled opacity + */ + public double getAlpha() { + return Double.max(minAlpha,1.0 - (double)(System.currentTimeMillis() - timeAdded) / expiry); + } + + /** + * Gets time point was added to track. + * @return system clock at time of addition + */ + public long getTimeAdded() { + return timeAdded; + } +} diff --git a/src/main/resources/scenes/race.fxml b/src/main/resources/scenes/race.fxml index 00e815a9..56aed929 100644 --- a/src/main/resources/scenes/race.fxml +++ b/src/main/resources/scenes/race.fxml @@ -23,14 +23,15 @@ - + + -