package seng302.Model; import javafx.animation.AnimationTimer; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import org.geotools.referencing.GeodeticCalculator; import seng302.Controllers.RaceController; import seng302.GPSCoordinate; import seng302.RaceDataSource; import seng302.VisualiserInput; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; /** * Parent class for races * Created by fwy13 on 3/03/17. */ public abstract class Race implements Runnable { protected ObservableList startingBoats; protected List legs; protected RaceController controller; protected int boatsFinished = 0; protected long totalTimeElapsed; protected int scaleFactor; private int lastFPS = 20; /** * Initailiser for Race * * @param boats Takes in an array of boats that are participating in the race. * @param legs Number of marks in order that the boats pass in order to complete the race. * @param controller race controller * @param scaleFactor for race */ public Race(List boats, List legs, RaceController controller, int scaleFactor) { this.startingBoats = FXCollections.observableArrayList(boats); this.legs = legs; this.legs.add(new Leg("Finish", this.legs.size())); this.controller = controller; this.scaleFactor = scaleFactor; if (startingBoats != null && startingBoats.size() > 0) { initialiseBoats(); } } public Race(RaceDataSource raceData, RaceController controller, int scaleFactor) { this(raceData.getBoats(), raceData.getLegs(), controller, scaleFactor); } /** * @deprecated * @param startingBoats boats starting the race * @param legs legs in race * @param controller controller for the race * @param scaleFactor factor to scale by */ public Race(Boat[] startingBoats, List legs, RaceController controller, int scaleFactor) { this(Arrays.asList(startingBoats), legs, controller, scaleFactor); } public void setController(RaceController controller) { this.controller = controller; } public abstract void initialiseBoats(); /** * Checks if the boat cannot finish the race * @return True if boat cannot finish the race */ protected abstract boolean doNotFinish(); /** * Checks the position of the boat, this updates the boats current position. * * @param boat Boat that the postion is to be updated for. * @param timeElapsed Time that has elapse since the start of the the race. * @see Boat */ protected abstract void checkPosition(Boat boat, long timeElapsed); /** * Updates the boat's gps coordinates * * @param boat to be updated * @param millisecondsElapsed time since last update */ protected abstract void updatePosition(Boat boat, int millisecondsElapsed); /** * Runnable for the thread. */ public void run() { setControllerListeners(); initialiseBoats(); simulateRace(); } /** * Update the calculated fps to the fps label * * @param fps The new calculated fps value */ private void updateFPS(int fps) { Platform.runLater(() -> { controller.setFrames("FPS: " + fps); }); } /** * Starts the Race Simulation, playing the race start to finish with the timescale. * This prints the boats participating, the order that the events occur in time order, and the respective information of the events. */ protected final void simulateRace() { System.setProperty("javafx.animation.fullspeed", "true"); for (Boat boat : startingBoats) { boat.setStarted(true); } new AnimationTimer() { long timeRaceStarted = System.currentTimeMillis(); //start time of loop int fps = 0; //init fps value long timeCurrent = System.currentTimeMillis(); //current time @Override public void handle(long arg0) { if (boatsFinished < startingBoats.size()) { totalTimeElapsed = System.currentTimeMillis() - timeRaceStarted; for (Boat boat : startingBoats) { if (boat != null && !boat.isFinished()) { updatePosition(boat, Math.round(1000 / lastFPS) > 20 ? 15 : Math.round(1000 / lastFPS)); checkPosition(boat, totalTimeElapsed); } } } controller.updateMap(startingBoats); fps++; if ((System.currentTimeMillis() - timeCurrent) > 1000) { updateFPS(fps); lastFPS = fps; fps = 0; timeCurrent = System.currentTimeMillis(); } } }.start(); } /** * Update position of boats in race, no position if on starting leg or DNF. */ protected void updatePositions() { FXCollections.sort(startingBoats, (a, b) -> b.getCurrentLeg().getLegNumber() - a.getCurrentLeg().getLegNumber()); for(Boat boat: startingBoats) { if(boat != null) { boat.setPosition(Integer.toString(startingBoats.indexOf(boat) + 1)); if (boat.isDnf() || !boat.isStarted() || boat.getCurrentLeg().getLegNumber() < 0) boat.setPosition("-"); } } } /** * Update call for the controller. */ protected void setControllerListeners() { if (controller != null) controller.setInfoTable(this); } /** * Returns the boats that have started the race. * * @return ObservableList of Boat class that participated in the race. * @see ObservableList * @see Boat */ public ObservableList getStartingBoats() { return startingBoats; } }