diff --git a/src/main/java/seng302/Constants.java b/src/main/java/seng302/Constants.java index 5b2dc0a7..466f760b 100644 --- a/src/main/java/seng302/Constants.java +++ b/src/main/java/seng302/Constants.java @@ -21,6 +21,8 @@ public class Constants { public static final GPSCoordinate finishLineMarker1 = new GPSCoordinate(32.317379, -64.839291); public static final GPSCoordinate finishLineMarker2 = new GPSCoordinate(32.317257, -64.836260); + public static final double wakeScale = 10; + public static final BoatInRace[] OFFICIAL_AC35_COMPETITORS = new BoatInRace[] {new BoatInRace("Oracle Team USA", 30.0, Color.BLUEVIOLET, "Oracle"), new BoatInRace("Land Rover BAR", 23.0, Color.BLACK, "BGR"), diff --git a/src/main/java/seng302/Controllers/RaceController.java b/src/main/java/seng302/Controllers/RaceController.java index 88c448a1..dfdddaf3 100644 --- a/src/main/java/seng302/Controllers/RaceController.java +++ b/src/main/java/seng302/Controllers/RaceController.java @@ -73,7 +73,7 @@ public class RaceController extends Controller{ public void updateMap(ObservableList boats) { BoatInRace[] boatInRaces = new BoatInRace[boats.size()]; raceMap.setBoats(boats.toArray(boatInRaces)); - raceMap.drawRaceMap(); + raceMap.update(); } /** @@ -146,7 +146,6 @@ public class RaceController extends Controller{ });*/ } - /** * Initializes and runs the race, based on the user's chosen scale factor * Currently uses an example racecourse @@ -173,6 +172,7 @@ public class RaceController extends Controller{ raceMap.widthProperty().bind(canvasBase.widthProperty()); raceMap.heightProperty().bind(canvasBase.heightProperty()); raceMap.setBoats(boats); + raceMap.drawBoats(); raceMap.drawRaceMap(); raceMap.setVisible(true); @@ -192,7 +192,7 @@ public class RaceController extends Controller{ public void changed(ObservableValue ov, Boolean old_val, Boolean new_val) { raceMap.toggleAnno(); - raceMap.drawRaceMap(); + raceMap.update(); } }); } @@ -200,6 +200,7 @@ public class RaceController extends Controller{ /** * Function for the Bermuda Race. + * * @return legs in the Bermuda Race. */ private ArrayList generateBermudaCourseLegs() { @@ -249,5 +250,4 @@ public class RaceController extends Controller{ */ public void setFrames(String fps) { FPS.setText((fps)); } - } diff --git a/src/main/java/seng302/Model/Boat.java b/src/main/java/seng302/Model/Boat.java index ec298847..866de7dc 100644 --- a/src/main/java/seng302/Model/Boat.java +++ b/src/main/java/seng302/Model/Boat.java @@ -48,6 +48,15 @@ public class Boat { return velocity; } + /** + * Sets the speed of the boat in knots. + * @param velocity speed in knots + */ + public void setVelocity(double velocity) { + this.velocity = velocity; + this.velocityProp.setValue(velocity); + } + /** * Print method prints the name of the boat * @return Name of the boat. diff --git a/src/main/java/seng302/Model/BoatInRace.java b/src/main/java/seng302/Model/BoatInRace.java index b6fc7c60..72906939 100644 --- a/src/main/java/seng302/Model/BoatInRace.java +++ b/src/main/java/seng302/Model/BoatInRace.java @@ -4,8 +4,10 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.scene.paint.Color; import org.geotools.referencing.GeodeticCalculator; +import seng302.Constants; import seng302.GPSCoordinate; +import java.awt.geom.Point2D; /** * Boat in the Race extends Boat. @@ -73,6 +75,25 @@ public class BoatInRace extends Boat { return calculateHeading(azimuth); } + /** + * Returns the position of the end of the boat's wake, which is 180 degrees + * from the boat's heading, and whose length is proportional to the boat's + * speed. + * @return GPSCoordinate of wake endpoint. + */ + public GPSCoordinate getWake() { + double reverseHeading = calculateHeading() - 180; + double distance = Constants.wakeScale * getVelocity(); + + GeodeticCalculator calc = new GeodeticCalculator(); + calc.setStartingGeographicPoint( + new Point2D.Double(getCurrentPosition().getLongitude(), getCurrentPosition().getLatitude()) + ); + calc.setDirection(reverseHeading, distance); + Point2D endpoint = calc.getDestinationGeographicPoint(); + return new GPSCoordinate(endpoint.getY(), endpoint.getX()); + } + /** * @return Scaled velocity of the boat */ diff --git a/src/main/java/seng302/Model/Race.java b/src/main/java/seng302/Model/Race.java index 1f572755..8c8d9c99 100644 --- a/src/main/java/seng302/Model/Race.java +++ b/src/main/java/seng302/Model/Race.java @@ -118,15 +118,18 @@ public abstract class Race implements Runnable { long timeLoopEnded; while (currentTime <= startTime) { - //if (controller != null) controller.updateMap(startingBoats); timeLeft = startTime - currentTime; - currentTimeInSeconds = timeLeft / 1000; - minutes = currentTimeInSeconds / 60; - remainingSeconds = currentTimeInSeconds % 60; - hours = minutes / 60; - minutes = minutes % 60; - if (controller != null) { - updateTime(String.format("Time until race starts: %02d:%02d:%02d", hours, minutes, remainingSeconds)); + if (timeLeft == 0 && controller != null) { + updateTime("Race is starting..."); + } else { + currentTimeInSeconds = timeLeft / 1000; + minutes = currentTimeInSeconds / 60; + remainingSeconds = currentTimeInSeconds % 60; + hours = minutes / 60; + minutes = minutes % 60; + if (controller != null) { + updateTime(String.format("Race clock: -%02d:%02d:%02d", hours, minutes, remainingSeconds)); + } } try { timeLoopEnded = System.currentTimeMillis(); @@ -162,6 +165,16 @@ public abstract class Race implements Runnable { */ protected void updateTime(String time){ Platform.runLater(() -> {controller.setTimer(time);}); +======= + * Updates the GUI race clock + * @param time + */ + protected void updateTime(String time) { + + Platform.runLater(() -> { + controller.setTimer(time); + }); +>>>>>>> Temporary merge branch 2 } /** @@ -183,6 +196,8 @@ public abstract class Race implements Runnable { */ private void simulateRace() { + System.setProperty("javafx.animation.fullspeed", "true"); + new AnimationTimer() { long timeRaceStarted = System.currentTimeMillis(); //start time of loop @@ -242,7 +257,6 @@ public abstract class Race implements Runnable { */ protected void checkPosition(BoatInRace boat, long timeElapsed) { if (boat.getDistanceTravelledInLeg() > boat.getCurrentLeg().getDistance()){ -// updateController(); //boat has passed onto new leg if (boat.getCurrentLeg().getName().equals("Finish")) { //boat has finished @@ -254,13 +268,17 @@ public abstract class Race implements Runnable { boat.setFinished(true); boat.setCurrentLeg(new Leg("DNF",-1)); } else { + //Calculate how much the boat overshot the marker by boat.setDistanceTravelledInLeg(boat.getDistanceTravelledInLeg() - boat.getCurrentLeg().getDistance()); + //Move boat on to next leg Leg nextLeg = legs.get(boat.getCurrentLeg().getLegNumber() + 1); boat.setCurrentLeg(nextLeg); + //Add overshoot distance into the distance travelled for the next leg boat.setDistanceTravelledInLeg(boat.getDistanceTravelledInLeg()); } - FXCollections.sort(startingBoats, (a,b) -> b.getCurrentLeg().getLegNumber() - a.getCurrentLeg().getLegNumber()); + //Update the boat display table in the GUI to reflect the leg change + FXCollections.sort(startingBoats, (a, b) -> b.getCurrentLeg().getLegNumber() - a.getCurrentLeg().getLegNumber()); } } @@ -282,9 +300,9 @@ public abstract class Race implements Runnable { } /** - * This function is a function that generates the Race and populates the events list. - * Is automatically called by the initialiser function, so that simulateRace() does not return an empty race. - * @see Race#simulateRace() + * Updates the boat's gps coordinates depending on time elapsed + * @param boat + * @param millisecondsElapsed */ protected abstract void updatePosition(BoatInRace boat, int millisecondsElapsed); diff --git a/src/main/java/seng302/Model/ResizableRaceCanvas.java b/src/main/java/seng302/Model/ResizableRaceCanvas.java index 6a552b52..c2d0cad4 100644 --- a/src/main/java/seng302/Model/ResizableRaceCanvas.java +++ b/src/main/java/seng302/Model/ResizableRaceCanvas.java @@ -40,7 +40,6 @@ public class ResizableRaceCanvas extends Canvas { this.boats = boats; } - public ResizableRaceCanvas(RaceMap map) { this.map = map; gc = this.getGraphicsContext2D(); @@ -73,9 +72,24 @@ public class ResizableRaceCanvas extends Canvas { * @see Color * @see Paint */ - private void displayMark(GraphCoordinate graphCoordinate, Paint paint) { + public void displayMark(GraphCoordinate graphCoordinate, Paint paint){ + double d = 25; gc.setFill(paint); - gc.fillOval(graphCoordinate.getX(), graphCoordinate.getY(), 25, 25); + gc.fillOval(graphCoordinate.getX() - (d/2), graphCoordinate.getY() - (d/2), d, d); + } + + public void displayBoat(BoatInRace boat, double angle) { + GraphCoordinate pos = this.map.convertGPS(boat.getCurrentPosition()); + Paint paint = boat.getColour(); + + double[] x = {pos.getX() - 6, pos.getX(), pos.getX() + 6}; + double[] y = {pos.getY() + 12, pos.getY() - 12, pos.getY() + 12}; + gc.setFill(paint); + + gc.save(); + rotate(angle, pos.getX(), pos.getY()); + gc.fillPolygon(x, y, 3); + gc.restore(); } /** @@ -117,10 +131,10 @@ public class ResizableRaceCanvas extends Canvas { */ private void displayArrow(GraphCoordinate coordinate, int angle) { gc.save(); - rotate(angle, coordinate.getX(),coordinate.getY()); + rotate(angle, coordinate.getX(), coordinate.getY()); gc.setFill(Color.BLACK); - gc.fillPolygon(new double[]{coordinate.getX()-12, coordinate.getX()-6, coordinate.getX(), coordinate.getX()-4, coordinate.getX()-4, coordinate.getX()-8, coordinate.getX()-8}, - new double[]{coordinate.getY()-5, coordinate.getY()-20, coordinate.getY()-5, coordinate.getY()-5, coordinate.getY()+20, coordinate.getY()+20, coordinate.getY()-5}, + gc.fillPolygon(new double[]{coordinate.getX() - 12, coordinate.getX() - 6, coordinate.getX(), coordinate.getX() - 4, coordinate.getX() - 4, coordinate.getX() - 8, coordinate.getX() - 8}, + new double[]{coordinate.getY() - 5, coordinate.getY() - 20, coordinate.getY() - 5, coordinate.getY() - 5, coordinate.getY() + 20, coordinate.getY() + 20, coordinate.getY() - 5}, 7); gc.restore(); } @@ -156,6 +170,13 @@ public class ResizableRaceCanvas extends Canvas { gc.fillText(text, xCoord, yCoord); } + /** + * Draws race map with up to date data. + */ + public void update() { + this.drawRaceMap(); + this.updateBoats(); + } /** * Draws the Race Map @@ -168,9 +189,6 @@ public class ResizableRaceCanvas extends Canvas { gc.clearRect(0, 0, width, height); //System.out.println("Race Map Canvas Width: "+ width + ", Height:" + height); this.map = new RaceMap(32.278, -64.863, 32.320989, -64.821, (int) width, (int) height); - if (map == null) { - return; - } //finish line gc.setLineWidth(2); @@ -208,18 +226,39 @@ public class ResizableRaceCanvas extends Canvas { displayArrow(new GraphCoordinate((int)getWidth()-40, 40), 150); } - /** * Toggle the raceAnno value */ - public void toggleAnno(){ - if (raceAnno){ + public void toggleAnno() { + if (raceAnno) { raceAnno = false; } else { raceAnno = true; } } + /** + * Draws boats while race in progress, when leg heading is set. + */ + public void updateBoats() { + if (boats != null) { + for (BoatInRace boat : boats) { + boolean finished = boat.getCurrentLeg().getName().equals("Finish") || boat.getCurrentLeg().getName().equals("DNF"); + if (!finished) { + displayBoat(boat, boat.calculateHeading()); + GraphCoordinate wakeFrom = this.map.convertGPS(boat.getCurrentPosition()); + GraphCoordinate wakeTo = this.map.convertGPS(boat.getWake()); + displayLine(wakeFrom, wakeTo, boat.getColour()); + } + else { + displayBoat(boat, 0); + } + + if (raceAnno) displayText(boat.getAbbrev(), boat.getVelocity(), this.map.convertGPS(boat.getCurrentPosition())); + } + } + } + /** * Set the Canvas to resizable. * @@ -252,4 +291,17 @@ public class ResizableRaceCanvas extends Canvas { return getHeight(); } + /** + * Draws boats during race setup, when leg heading is not set. + */ + public void drawBoats() { + if (boats != null) { + for (BoatInRace boat : boats) { + if (boat != null) { + displayBoat(boat, 0); + if (raceAnno) displayText(boat.getAbbrev(), boat.getVelocity(), this.map.convertGPS(boat.getCurrentPosition())); + } + } + } + } } diff --git a/src/main/resources/scenes/racepane.fxml b/src/main/resources/scenes/racepane.fxml index bff11199..9d409ab4 100644 --- a/src/main/resources/scenes/racepane.fxml +++ b/src/main/resources/scenes/racepane.fxml @@ -6,8 +6,8 @@ - - + @@ -27,7 +27,7 @@ - +