diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index 3625db47..5c595ccf 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -5,8 +5,8 @@ import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; -import seng302.models.parsers.StreamParser; -import seng302.models.parsers.StreamReceiver; +import seng302.models.stream.StreamParser; +import seng302.models.stream.StreamReceiver; import seng302.server.ServerThread; public class App extends Application { @@ -18,6 +18,7 @@ public class App extends Application { primaryStage.setScene(new Scene(root)); primaryStage.setMaximized(true); + primaryStage.show(); primaryStage.setOnCloseRequest(e -> { StreamParser.appClose(); @@ -64,6 +65,7 @@ public class App extends Application { else{ // sr = new StreamReceiver("localhost", 4949, "RaceStream"); sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream"); +// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream"); } sr.start(); diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 9a2a9797..06dc88f0 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -1,6 +1,6 @@ package seng302.controllers; -import javafx.animation.AnimationTimer; +import javafx.animation.*; import javafx.beans.property.SimpleDoubleProperty; import javafx.fxml.FXML; import javafx.geometry.Point2D; @@ -10,22 +10,14 @@ import javafx.scene.canvas.GraphicsContext; import javafx.scene.layout.AnchorPane; import javafx.scene.paint.Color; import javafx.scene.text.Font; -import seng302.models.BoatGroup; -import seng302.models.Colors; -import seng302.models.RaceObject; -import seng302.models.Yacht; +import seng302.models.*; import seng302.models.mark.*; -import seng302.models.parsers.StreamParser; -import seng302.models.parsers.XMLParser; -import seng302.models.parsers.XMLParser.RaceXMLObject.CompoundMark; -import seng302.models.parsers.XMLParser.RaceXMLObject.Limit; -import seng302.models.parsers.packets.BoatPositionPacket; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; +import seng302.models.stream.StreamParser; +import seng302.models.stream.packets.BoatPositionPacket; +import seng302.models.stream.XMLParser; +import seng302.models.stream.XMLParser.RaceXMLObject.Limit; +import seng302.models.mark.Mark; +import java.util.*; import java.util.concurrent.PriorityBlockingQueue; /** @@ -50,6 +42,7 @@ public class CanvasController { private final int RHS_BUFFER = BUFFER_SIZE + MARK_SIZE / 2; private final int TOP_BUFFER = BUFFER_SIZE; private final int BOT_BUFFER = TOP_BUFFER + MARK_SIZE / 2; + private boolean horizontalInversion = false; private double distanceScaleFactor; private ScaleDirection scaleDirection; @@ -59,16 +52,15 @@ public class CanvasController { private Mark maxLonPoint; private double referencePointX; private double referencePointY; - private double metersToPixels; - private List raceObjects = new ArrayList<>(); - private List raceMarks = new ArrayList<>(); + + private List markGroups = new ArrayList<>(); + private List boatGroups = new ArrayList<>(); //FRAME RATE - private static final double UPDATE_TIME = 0.016666; // 1 / 60 ie 60fps + private Double frameRate = 60.0; private final long[] frameTimes = new long[30]; private int frameTimeIndex = 0; private boolean arrayFilled = false; - private DecimalFormat decimalFormat2dp = new DecimalFormat("0.00"); public AnimationTimer timer; @@ -91,8 +83,6 @@ public class CanvasController { // Bind canvas size to stack pane size. canvas.widthProperty().bind(new SimpleDoubleProperty(CANVAS_WIDTH)); canvas.heightProperty().bind(new SimpleDoubleProperty(CANVAS_HEIGHT)); - //group.minWidth(CANVAS_WIDTH); - //group.minHeight(CANVAS_HEIGHT); } public void initializeCanvas (){ @@ -106,7 +96,7 @@ public class CanvasController { // TODO: 1/05/17 wmu16 - Change this call to now draw the marks as from the xml - drawBoats(); + initializeBoats(); timer = new AnimationTimer() { @Override @@ -123,13 +113,12 @@ public class CanvasController { if (arrayFilled) { elapsedNanos = now - oldFrameTime ; long elapsedNanosPerFrame = elapsedNanos / frameTimes.length ; - Double frameRate = 1_000_000_000.0 / elapsedNanosPerFrame ; + frameRate = 1_000_000_000.0 / elapsedNanosPerFrame ; drawFps(frameRate.intValue()); } // TODO: 1/05/17 cir27 - Make the RaceObjects update on the actual delay. - elapsedNanos = 1000 / 60; - updateRaceObjects(); + updateGroups(); if (StreamParser.isRaceFinished()) { this.stop(); } @@ -160,7 +149,7 @@ public class CanvasController { Point2D borderPoint1 = findScaledXY(thisMark1); Point2D borderPoint2 = findScaledXY(thisMark2); gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(), - borderPoint2.getX(), borderPoint2.getY()); + borderPoint2.getX(), borderPoint2.getY()); xBoundaryPoints[i] = borderPoint1.getX(); yBoundaryPoints[i] = borderPoint1.getY(); } @@ -171,72 +160,28 @@ public class CanvasController { Point2D borderPoint1 = findScaledXY(thisMark1); Point2D borderPoint2 = findScaledXY(thisMark2); gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(), - borderPoint2.getX(), borderPoint2.getY()); + borderPoint2.getX(), borderPoint2.getY()); xBoundaryPoints[courseLimits.size()-1] = borderPoint1.getX(); yBoundaryPoints[courseLimits.size()-1] = borderPoint1.getY(); gc.setFill(Color.LIGHTBLUE); gc.fillPolygon(xBoundaryPoints,yBoundaryPoints,yBoundaryPoints.length); } - - /** - * Adds the course marks to the canvas, taken from the XMl file - * - * NOTE: This is quite confusing as objects are grabbed from the XMLParser such as Mark and CompoundMark which are - * named the same as those in the model package but are, however not the same, so they do not have things such as - * a type and must be derived from the number of marks in a compound mark etc.. - */ - private void addCourseMarks() { - XMLParser.RaceXMLObject raceXMLObject = StreamParser.getXmlObject().getRaceXML(); - ArrayList compoundMarks = raceXMLObject.getCompoundMarks(); - RaceObject markGroup; - - for (CompoundMark compoundMark : compoundMarks) { - - //If the compound mark has 2 marks then its a gate mark - if (compoundMark.getMarks().size() == 2) { - CompoundMark.Mark mark1 = compoundMark.getMarks().get(0); - CompoundMark.Mark mark2 = compoundMark.getMarks().get(1); - SingleMark singleMark1 = new SingleMark(mark1.getMarkName(), mark1.getTargetLat(), mark1.getTargetLng(), mark1.getSourceID()); - SingleMark singleMark2 = new SingleMark(mark1.getMarkName(), mark2.getTargetLat(), mark2.getTargetLng(), mark2.getSourceID()); - GateMark thisGateMark = new GateMark(compoundMark.getcMarkName(), - (compoundMark.getMarkID().equals(1)) ? MarkType.OPEN_GATE : MarkType.CLOSED_GATE, - singleMark1, - singleMark2, - singleMark1.getLatitude(), - singleMark1.getLongitude()); - - markGroup = new MarkGroup(thisGateMark, - findScaledXY(thisGateMark.getSingleMark1()), - findScaledXY(thisGateMark.getSingleMark2())); - - raceObjects.add(markGroup); - raceMarks.add(thisGateMark); - - //Otherwise its a single mark - } else { - CompoundMark.Mark singleMark = compoundMark.getMarks().get(0); - Mark thisSingleMark = new SingleMark(singleMark.getMarkName(), - singleMark.getTargetLat(), - singleMark.getTargetLng(), - singleMark.getSourceID()); - - markGroup = new MarkGroup(thisSingleMark, findScaledXY(thisSingleMark)); - raceObjects.add(markGroup); - raceMarks.add(thisSingleMark); - + private void updateGroups(){ + for (BoatGroup boatGroup : boatGroups) { + // some raceObjects will have multiple ID's (for instance gate marks) + //checking if the current "ID" has any updates associated with it + if (StreamParser.boatPositions.containsKey(boatGroup.getRaceId())) { + if (boatGroup.isStopped()) { + updateBoatGroup(boatGroup); + } } + boatGroup.move(); } - } - - private void updateRaceObjects(){ - for (RaceObject raceObject : raceObjects) { - raceObject.updatePosition(1000 / 60); - // some raceObjects will have multiply ID's (for instance gate marks) - for (long id : raceObject.getRaceIds()) { - //checking if the current "ID" has any updates associated with it + for (MarkGroup markGroup : markGroups) { + for (int id : markGroup.getRaceIds()) { if (StreamParser.boatPositions.containsKey(id)) { - move(id, raceObject); + updateMarkGroup(id, markGroup); } } } @@ -253,33 +198,52 @@ public class CanvasController { } } - private void move(long id, RaceObject raceObject){ - PriorityBlockingQueue movementQueue = StreamParser.boatPositions.get(id); + private void updateBoatGroup(BoatGroup boatGroup) { + PriorityBlockingQueue movementQueue = StreamParser.boatPositions.get(boatGroup.getRaceId()); + // giving the movementQueue a 5 packet buffer to account for slightly out of order packets if (movementQueue.size() > 0){ - BoatPositionPacket positionPacket = movementQueue.peek(); - - //this code adds a delay to reading from the movementQueue - //in case things being put into the movement queue are slightly - //out of order - int delayTime = 1000; - int loopTime = delayTime * 10; - long timeDiff = (System.currentTimeMillis()%loopTime - positionPacket.getTimeValid()%loopTime); - if (timeDiff < 0){ - timeDiff = loopTime + timeDiff; + try { + BoatPositionPacket positionPacket = movementQueue.take(); + Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon()); + double heading = 360.0 / 0xffff * positionPacket.getHeading(); + boatGroup.setDestination(p2d.getX(), p2d.getY(), heading, positionPacket.getGroundSpeed(), positionPacket.getTimeValid(), frameRate, boatGroup.getRaceId()); + } catch (InterruptedException e){ + e.printStackTrace(); } - if (timeDiff > delayTime) { - try { - positionPacket = movementQueue.take(); - Point2D p2d = latLonToXY(positionPacket.getLat(), positionPacket.getLon()); - double heading = 360.0 / 0xffff * positionPacket.getHeading(); - raceObject.setDestination(p2d.getX(), p2d.getY(), heading, positionPacket.getGroundSpeed(), (int) id); - } catch (InterruptedException e){ - e.printStackTrace(); - } +// } + } + } + + void updateMarkGroup (int raceId, MarkGroup markGroup) { + PriorityBlockingQueue movementQueue = StreamParser.boatPositions.get(raceId); + if (movementQueue.size() > 0){ + try { + BoatPositionPacket positionPacket = movementQueue.take(); + Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon()); + markGroup.moveMarkTo(p2d.getX(), p2d.getY(), raceId); + } catch (InterruptedException e){ + e.printStackTrace(); } } } + /** + * Draws all the boats. + */ + private void initializeBoats() { + Map boats = StreamParser.getBoats(); + Group boatAnnotations = new Group(); + + for (Yacht boat : boats.values()) { + boat.setColour(Colors.getColor()); + BoatGroup boatGroup = new BoatGroup(boat, boat.getColour()); + boatGroups.add(boatGroup); + boatAnnotations.getChildren().add(boatGroup.getLowPriorityAnnotations()); + } + group.getChildren().add(boatAnnotations); + group.getChildren().addAll(boatGroups); + } + class ResizableCanvas extends Canvas { ResizableCanvas() { @@ -305,11 +269,11 @@ public class CanvasController { public double prefWidth(double height) { return getWidth(); } - @Override public double prefHeight(double width) { return getHeight(); } + } private void drawFps(int fps){ @@ -328,30 +292,6 @@ public class CanvasController { } } - /** - * Draws all the boats. - */ - private void drawBoats() { -// Map timelineInfos = raceViewController.getTimelineInfos(); -// List boats = raceViewController.getStartingBoats(); - Map boats = StreamParser.getBoats(); - Double startingX = raceObjects.get(0).getLayoutX(); - Double startingY = raceObjects.get(0).getLayoutY(); - Group boatAnnotations = new Group(); - - for (Yacht boat : boats.values()) { -// for (Boat boat : boats) { - boat.setColour(Colors.getColor()); - BoatGroup boatGroup = new BoatGroup(boat, boat.getColour()); - boatGroup.moveTo(startingX, startingY, 0d); - //boatGroup.setStage(raceViewController.getStage()); - raceObjects.add(boatGroup); - boatAnnotations.getChildren().add(boatGroup.getLowPriorityAnnotations()); - } - group.getChildren().add(boatAnnotations); - group.getChildren().addAll(raceObjects); - } - /** * Calculates x and y location for every marker that fits it to the canvas the race will be drawn on. */ @@ -361,9 +301,8 @@ public class CanvasController { findMinMaxPoint(); double minLonToMaxLon = scaleRaceExtremities(); calculateReferencePointLocation(minLonToMaxLon); - givePointsXY(); + //givePointsXY(); addRaceBorder(); - findMetersToPixels(); } @@ -386,16 +325,11 @@ public class CanvasController { //If the course is on a point on the earth where longitudes wrap around. Limit minLonMark = sortedPoints.get(0); Limit maxLonMark = sortedPoints.get(sortedPoints.size()-1); - SingleMark thisMinLon = new SingleMark(minLonMark.toString(), minLonMark.getLat(), minLonMark.getLng(), minLonMark.getSeqID()); - SingleMark thisMaxLon = new SingleMark(maxLonMark.toString(), maxLonMark.getLat(), maxLonMark.getLng(), maxLonMark.getSeqID()); - // TODO: 30/03/17 cir27 - Correctly account for longitude wrapping around. - if (thisMaxLon.getLongitude() - thisMinLon.getLongitude() > 180) { - SingleMark temp = thisMinLon; - thisMinLon = thisMaxLon; - thisMaxLon = temp; + minLonPoint = new SingleMark(minLonMark.toString(), minLonMark.getLat(), minLonMark.getLng(), minLonMark.getSeqID()); + maxLonPoint = new SingleMark(maxLonMark.toString(), maxLonMark.getLat(), maxLonMark.getLng(), maxLonMark.getSeqID()); + if (maxLonPoint.getLongitude() - minLonPoint.getLongitude() > 180) { + horizontalInversion = true; } - minLonPoint = thisMinLon; - maxLonPoint = thisMaxLon; } /** @@ -426,8 +360,12 @@ public class CanvasController { referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * Mark.calculateDistance(referencePoint, minLonPoint); referencePointX += ((CANVAS_WIDTH - (LHS_BUFFER + RHS_BUFFER)) - (minLonToMaxLon * distanceScaleFactor)) / 2; } + if(horizontalInversion) { + referencePointX = CANVAS_WIDTH - RHS_BUFFER - (referencePointX - LHS_BUFFER); + } } + /** * Finds the scale factor necessary to fit all race markers within the onscreen map and assigns it to distanceScaleFactor * Returns the max horizontal distance of the map. @@ -456,43 +394,18 @@ public class CanvasController { return horiDistance; } - /** - * Give all markers in the course an x,y location relative to a given reference with a known x,y location. Distances - * are scaled according to the distanceScaleFactor variable. - */ - private void givePointsXY() { - List allPoints = new ArrayList<>(raceViewController.getRace().getCourse()); - List processed = new ArrayList<>(); - RaceObject markGroup; - - for (Mark mark : allPoints) { - if (!processed.contains(mark)) { - if (mark.getMarkType() != MarkType.SINGLE_MARK) { - GateMark gateMark = (GateMark) mark; - markGroup = new MarkGroup(mark, findScaledXY(gateMark.getSingleMark1()), findScaledXY(gateMark.getSingleMark2())); - raceObjects.add(markGroup); - } else { - markGroup = new MarkGroup(mark, findScaledXY(mark)); - raceObjects.add(markGroup); - } - processed.add(mark); - } - } - } - private Point2D findScaledXY (Mark unscaled) { - return findScaledXY (minLatPoint.getLatitude(), minLatPoint.getLongitude(), - unscaled.getLatitude(), unscaled.getLongitude()); + return findScaledXY (unscaled.getLatitude(), unscaled.getLongitude()); } - private Point2D findScaledXY (double latA, double lonA, double latB, double lonB) { + private Point2D findScaledXY (double unscaledLat, double unscaledLon) { double distanceFromReference; double angleFromReference; int xAxisLocation = (int) referencePointX; int yAxisLocation = (int) referencePointY; - angleFromReference = Mark.calculateHeadingRad(latA, lonA, latB, lonB); - distanceFromReference = Mark.calculateDistance(latA, lonA, latB, lonB); + angleFromReference = Mark.calculateHeadingRad(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat, unscaledLon); + distanceFromReference = Mark.calculateDistance(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat, unscaledLon); if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) { xAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); yAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); @@ -509,44 +422,13 @@ public class CanvasController { xAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); yAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); } + if(horizontalInversion) { + xAxisLocation = CANVAS_WIDTH - RHS_BUFFER - (xAxisLocation - LHS_BUFFER); + } return new Point2D(xAxisLocation, yAxisLocation); } - - - /** - * Find the number of meters per pixel. - */ - private void findMetersToPixels () { - Double angularDistance; - Double angle; - Double straightLineDistance; - if (scaleDirection == ScaleDirection.HORIZONTAL) { - angularDistance = Mark.calculateDistance(minLonPoint, maxLonPoint); - angle = Mark.calculateHeadingRad(minLonPoint, maxLonPoint); - if (angle > Math.PI / 2) { - straightLineDistance = Math.cos(angle - Math.PI) * angularDistance; - } else { - straightLineDistance = Math.cos(angle) * angularDistance; - } - metersToPixels = (CANVAS_WIDTH - RHS_BUFFER - LHS_BUFFER) / straightLineDistance; - } else { - angularDistance = Mark.calculateDistance(minLatPoint, maxLatPoint); - angle = Mark.calculateHeadingRad(minLatPoint, maxLatPoint); - if (angle < Math.PI / 2) { - straightLineDistance = Math.cos(angle) * angularDistance; - } else { - straightLineDistance = Math.cos(-angle + Math.PI * 2) * angularDistance; - } - metersToPixels = (CANVAS_HEIGHT - TOP_BUFFER - BOT_BUFFER) / straightLineDistance; - } - } - - private Point2D latLonToXY (double latitude, double longitude) { - return findScaledXY(minLatPoint.getLatitude(), minLatPoint.getLongitude(), latitude, longitude); - } - - List getRaceObjects() { - return raceObjects; + List getBoatGroups() { + return boatGroups; } } \ 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 a1488a84..a222cfc3 100644 --- a/src/main/java/seng302/controllers/Controller.java +++ b/src/main/java/seng302/controllers/Controller.java @@ -9,6 +9,7 @@ import javafx.scene.layout.Pane; import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; +import seng302.models.stream.StreamParser; public class Controller implements Initializable { @@ -33,5 +34,6 @@ public class Controller implements Initializable { public void initialize(URL location, ResourceBundle resources) { contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString()); setContentPane("/views/StartScreenView.fxml"); + StreamParser.boatPositions.clear(); } } diff --git a/src/main/java/seng302/controllers/RaceController.java b/src/main/java/seng302/controllers/RaceController.java deleted file mode 100644 index e5e95d59..00000000 --- a/src/main/java/seng302/controllers/RaceController.java +++ /dev/null @@ -1,82 +0,0 @@ -package seng302.controllers; - -import seng302.models.Race; -import seng302.models.Yacht; -import seng302.models.parsers.ConfigParser; -import seng302.models.parsers.CourseParser; -import seng302.models.parsers.StreamParser; - -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Map; -import java.util.Random; - -/** - * Created by zyt10 on 17/03/17. - * run before CanvasController to initialize race events - * the CanvasController then uses the event data to make the animations - */ -public class RaceController { - Race race = null; - - public void initializeRace() { - String raceConfigFile = "/config/config.xml"; - String teamsConfigFile = "/config/teams.xml"; - - try { - race = createRace(raceConfigFile, teamsConfigFile); //These config files arent actually used - } catch (Exception e) { - System.out.println("There was an error creating the race."); - } - - if (race != null) { - race.startRace(); - } else { - System.out.println("There was an error creating the race. Exiting."); - } - } - - public Race createRace(String configFile, String teamsConfigFile) throws Exception { - Race race = new Race(); -// StreamParser.xmlObject - // Read team names from file -// TeamsParser tp = new TeamsParser(teamsConfigFile); - - // Read course from file -// ConfigParser config = new ConfigParser(configFile); - - ArrayList boatNames = new ArrayList<>(); -// ArrayList teams = tp.getBoats(); - Map teams = StreamParser.getBoatsPos(); - - //get race size - int numberOfBoats = teams.size(); - - //get time scale -// double timeScale = config.getTimeScale(); -// race.setTimeScale(timeScale); - - for (Yacht boat : teams.values()) { - boatNames.add(boat.getBoatName()); - race.addBoat(boat); - } - - // Shuffle team names - long seed = System.nanoTime(); - Collections.shuffle(boatNames, new Random(seed)); - - if (numberOfBoats > Array.getLength(boatNames.toArray())) { - return null; - } - - CourseParser course = new CourseParser("/config/course.xml"); - race.addCourse(course.getCourse()); - - return race; - } - - public Race getRace() { - return race; - } -} diff --git a/src/main/java/seng302/controllers/RaceResultController.java b/src/main/java/seng302/controllers/RaceResultController.java deleted file mode 100644 index bff697ed..00000000 --- a/src/main/java/seng302/controllers/RaceResultController.java +++ /dev/null @@ -1,37 +0,0 @@ -package seng302.controllers; - -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.VBox; -import javafx.scene.text.Text; -import seng302.models.Race; - -import java.net.URL; -import java.util.ResourceBundle; - -/** - * Created by ptg19 on 20/03/17. - */ -public class RaceResultController implements Initializable{ - @FXML private AnchorPane window; - @FXML private VBox resultsVBox; - private Race race; - - RaceResultController(Race race){ - this.race = race; - } - - @Override - public void initialize(URL location, ResourceBundle resources) { - int boatPosition = this.race.getFinishedBoats().length; - - for (int i = this.race.getFinishedBoats().length - 1; i >= 0; i--){ - resultsVBox.getChildren().add(0, new Text(boatPosition + ": " + this.race.getFinishedBoats()[i].getBoatName())); - boatPosition--; - } - - - - } -} diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 22c96f13..43c04a9b 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -26,7 +26,7 @@ import seng302.controllers.annotations.ImportantAnnotationController; import seng302.controllers.annotations.ImportantAnnotationDelegate; import seng302.controllers.annotations.ImportantAnnotationsState; import seng302.models.*; -import seng302.models.parsers.StreamParser; +import seng302.models.stream.StreamParser; import java.io.IOException; import java.util.*; @@ -58,7 +58,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel private ArrayList startingBoats = new ArrayList<>(); private boolean displayFps; private Timeline timerTimeline; - private Race race; private Stage stage; private ImportantAnnotationsState importantAnnotations; @@ -68,13 +67,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel // Load a default important annotation state importantAnnotations = new ImportantAnnotationsState(); - //Initialise race controller - RaceController raceController = new RaceController(); - raceController.initializeRace(); - race = raceController.getRace(); - - startingBoats = new ArrayList<>(Arrays.asList(race.getBoats())); - includedCanvasController.setup(this); includedCanvasController.initializeCanvas(); initializeUpdateTimer(); @@ -113,7 +105,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel // Load FXML and set CSS fxmlLoader .setLocation(getClass().getResource("/views/importantAnnotationSelectView.fxml")); - Scene scene = new Scene(fxmlLoader.load(), 469, 248); + Scene scene = new Scene(fxmlLoader.load(), 469, 298); scene.getStylesheets().add(getClass().getResource("/css/master.css").toString()); stage.initStyle(StageStyle.UNDECORATED); @@ -282,7 +274,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel */ private void loadRaceResultView() { FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/FinishView.fxml")); - loader.setController(new RaceResultController(race)); try { contentAnchorPane.getChildren().removeAll(); @@ -335,11 +326,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel return displayFps; } - public Race getRace() { - return race; - } - - /** * Display the important annotations for a specific BoatGroup * @param bg The boat group to set the annotations for @@ -368,7 +354,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } else { bg.setWakeVisible(false); } - + //TODO fix boat annotations with new boatgroup if (importantAnnotations.getAnnotationState(Annotation.ESTTIMETONEXTMARK)) { bg.setEstTimeToNextMarkObjectVisible(true); } else { @@ -386,39 +372,30 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel switch (annotationLevel) { // No Annotations case 0: - for (RaceObject ro : includedCanvasController.getRaceObjects()) { - if (ro instanceof BoatGroup) { - BoatGroup bg = (BoatGroup) ro; - bg.setTeamNameObjectVisible(false); - bg.setVelocityObjectVisible(false); - bg.setEstTimeToNextMarkObjectVisible(false); - bg.setLegTimeObjectVisible(false); - bg.setLineGroupVisible(false); - bg.setWakeVisible(false); - } + for (BoatGroup bg : includedCanvasController.getBoatGroups()) { + bg.setTeamNameObjectVisible(false); + bg.setVelocityObjectVisible(false); + bg.setEstTimeToNextMarkObjectVisible(false); + bg.setLegTimeObjectVisible(false); + bg.setLineGroupVisible(false); + bg.setWakeVisible(false); } break; // Important Annotations case 1: - for (RaceObject ro : includedCanvasController.getRaceObjects()) { - if (ro instanceof BoatGroup) { - BoatGroup bg = (BoatGroup) ro; - setBoatGroupImportantAnnotations(bg); - } + for (BoatGroup bg : includedCanvasController.getBoatGroups()) { + setBoatGroupImportantAnnotations(bg); } break; // All Annotations case 2: - for (RaceObject ro : includedCanvasController.getRaceObjects()) { - if (ro instanceof BoatGroup) { - BoatGroup bg = (BoatGroup) ro; - bg.setTeamNameObjectVisible(true); - bg.setVelocityObjectVisible(true); - bg.setEstTimeToNextMarkObjectVisible(true); - bg.setLegTimeObjectVisible(true); - bg.setLineGroupVisible(true); - bg.setWakeVisible(true); - } + for (BoatGroup bg : includedCanvasController.getBoatGroups()) { + bg.setTeamNameObjectVisible(true); + bg.setVelocityObjectVisible(true); + bg.setEstTimeToNextMarkObjectVisible(true); + bg.setLegTimeObjectVisible(true); + bg.setLineGroupVisible(true); + bg.setWakeVisible(true); } break; } @@ -431,17 +408,14 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel * @param yacht The yacht for which we want to view all annotations */ private void setSelectedBoat(Yacht yacht) { - for (RaceObject ro : includedCanvasController.getRaceObjects()) { - if (ro instanceof BoatGroup) { - BoatGroup bg = (BoatGroup) ro; - //We need to iterate over all race groups to get the matching boat group belonging to this boat if we - //are to toggle its annotations, there is no other backwards knowledge of a yacht to its boatgroup. - if (bg.getBoat().getHullID().equals(yacht.getHullID())) { - bg.setIsSelected(true); - selectedBoat = yacht; - } else { - bg.setIsSelected(false); - } + for (BoatGroup bg : includedCanvasController.getBoatGroups()) { + //We need to iterate over all race groups to get the matching boat group belonging to this boat if we + //are to toggle its annotations, there is no other backwards knowledge of a yacht to its boatgroup. + if (bg.getBoat().getHullID().equals(yacht.getHullID())) { + bg.setIsSelected(true); + selectedBoat = yacht; + } else { + bg.setIsSelected(false); } } } diff --git a/src/main/java/seng302/controllers/StartScreenController.java b/src/main/java/seng302/controllers/StartScreenController.java index 8a4d563b..debeb371 100644 --- a/src/main/java/seng302/controllers/StartScreenController.java +++ b/src/main/java/seng302/controllers/StartScreenController.java @@ -22,7 +22,7 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import seng302.models.Yacht; -import seng302.models.parsers.StreamParser; +import seng302.models.stream.StreamParser; public class StartScreenController implements Initializable { @FXML @@ -58,10 +58,10 @@ public class StartScreenController implements Initializable { contentPane.getChildren().addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl))); } catch(javafx.fxml.LoadException e){ - System.err.println(e.getCause()); + e.printStackTrace(); } catch(IOException e){ - System.err.println(e); + e.printStackTrace(); } } @@ -132,6 +132,7 @@ public class StartScreenController implements Initializable { } public void switchToRaceView() { + StreamParser.boatPositions.clear(); switchedToRaceView = true; setContentPane("/views/RaceView.fxml"); } diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index bea2f04b..9ce9db91 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -1,29 +1,30 @@ package seng302.models; +import javafx.event.EventHandler; import javafx.geometry.Point2D; +import javafx.scene.CacheHint; import javafx.scene.Group; +import javafx.scene.input.MouseDragEvent; +import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; import javafx.scene.shape.Line; import javafx.scene.shape.Polygon; import javafx.scene.text.Text; import javafx.scene.transform.Rotate; -import javafx.stage.Stage; -import seng302.models.parsers.StreamParser; +import seng302.models.stream.StreamParser; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.List; /** - * BoatGroup is a javafx group that by default contains a graphical objects for representing a 2 - * dimensional boat. It contains a single polygon for the boat, a group of lines to show it's path, - * a wake object and two text labels to annotate the boat teams name and the boats velocity. The - * boat will update it's position onscreen everytime UpdatePosition is called unless the window is - * minimized in which case it attempts to store animations and apply them when the window is - * maximised. + * BoatGroup is a javafx group that by default contains a graphical objects for representing a 2 dimensional boat. + * It contains a single polygon for the boat, a group of lines to show it's path, a wake object and two text labels to + * annotate the boat teams name and the boats velocity. The boat will update it's position onscreen everytime + * UpdatePosition is called unless the window is minimized in which case it attempts to store animations and apply them + * when the window is maximised. */ -public class BoatGroup extends RaceObject { +public class BoatGroup extends Group{ //Constants for drawing private static final double TEAMNAME_X_OFFSET = 10d; @@ -37,9 +38,12 @@ public class BoatGroup extends RaceObject { private static final double BOAT_HEIGHT = 15d; private static final double BOAT_WIDTH = 10d; //Variables for boat logic. - private Point2D lastPoint; - private int wakeGenerationDelay = 10; - private double distanceTravelled; + private boolean isStopped = true; + private double xIncrement; + private double yIncrement; + private long lastTimeValid = 0; + private Double lastRotation = 0.0; + private long framesToMove; //Graphical objects private Yacht boat; private Group lineGroup = new Group(); @@ -49,70 +53,73 @@ public class BoatGroup extends RaceObject { private Text estTimeToNextMarkObject; private Text legTimeObject; private Wake wake; - private boolean isSelected = true; //Boats annotations are visible by default at the start - //Handles boat moving when connecting to a stream - private boolean setToInitialLocation = false; + private Double distanceTravelled = 0.0; + private Point2D lastPoint; private boolean destinationSet; - //Variables for handling minimization - private Stage stage; - private boolean isMaximized = true; - private List lineStorage = new ArrayList<>(); - private int setCallCount = 5; + private Color textColor = Color.RED; + + private Boolean isSelected = true; //All boats are initalised as selected /** * Creates a BoatGroup with the default triangular boat polygon. - * - * @param boat The boat that the BoatGroup will represent. Must contain an ID which will be used - * to tell which BoatGroup to update. + * @param boat The boat that the BoatGroup will represent. Must contain an ID which will be used to tell which + * BoatGroup to update. * @param color The colour of the boat polygon and the trailing line. */ - public BoatGroup(Yacht boat, Color color) { + public BoatGroup (Yacht boat, Color color){ this.boat = boat; initChildren(color); + this.textColor = color; } /** - * Creates a BoatGroup with the boat being the default polygon. The head of the boat should be - * at point (0,0). - * - * @param boat The boat that the BoatGroup will represent. Must contain an ID which will be used - * to tell which BoatGroup to update. - * @param color The colour of the boat polygon and the trailing line. - * @param points An array of co-ordinates x1,y1,x2,y2,x3,y3... that will make up the boat - * polygon. + * Creates a BoatGroup with the boat being the default polygon. The head of the boat should be at point (0,0). + * @param boat The boat that the BoatGroup will represent. Must contain an ID which will be used to tell which + * BoatGroup to update. + * @param color The colour of the boat polygon and the trailing line. + * @param points An array of co-ordinates x1,y1,x2,y2,x3,y3... that will make up the boat polygon. */ - public BoatGroup(Yacht boat, Color color, double... points) { + public BoatGroup (Yacht boat, Color color, double... points) + { this.boat = boat; initChildren(color, points); } /** - * Creates the javafx objects that will be the in the group by default. - * - * @param color The colour of the boat polygon and the trailing line. - * @param points An array of co-ordinates x1,y1,x2,y2,x3,y3... that will make up the boat - * polygon. + * Return a text object with caching and a color applied + * @param defaultText The default text to display + * @param fill The text fill color + * @return The text object */ - private void initChildren(Color color, double... points) { + private Text getTextObject(String defaultText, Color fill){ + Text text = new Text(defaultText); + + text.setFill(fill); + text.setCacheHint(CacheHint.SPEED); + text.setCache(true); + + return text; + } + + /** + * Creates the javafx objects that will be the in the group by default. + * @param color The colour of the boat polygon and the trailing line. + * @param points An array of co-ordinates x1,y1,x2,y2,x3,y3... that will make up the boat polygon. + */ + private void initChildren (Color color, double... points) { + textColor = color; + destinationSet = false; + boatPoly = new Polygon(points); boatPoly.setFill(color); boatPoly.setOnMouseEntered(event -> boatPoly.setFill(Color.FLORALWHITE)); boatPoly.setOnMouseExited(event -> boatPoly.setFill(color)); boatPoly.setOnMouseClicked(event -> setIsSelected(!isSelected)); + boatPoly.setCache(true); + boatPoly.setCacheHint(CacheHint.SPEED); - teamNameObject = new Text(boat.getShortName()); - velocityObject = new Text(String.valueOf(boat.getVelocity())); - DateFormat format = new SimpleDateFormat("mm:ss"); - String timeToNextMark = format - .format(boat.getEstimateTimeAtNextMark() - StreamParser.getCurrentTimeLong()); - estTimeToNextMarkObject = new Text("Next mark: " + timeToNextMark); - if (boat.getMarkRoundingTime() != null) { - String elapsedTime = format - .format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundingTime()); - legTimeObject = new Text("Last mark: " + elapsedTime); - } else { - legTimeObject = new Text("Last mark: -"); - } + teamNameObject = getTextObject(boat.getShortName(), textColor); + velocityObject = getTextObject(boat.getVelocity().toString(), textColor); teamNameObject.setX(TEAMNAME_X_OFFSET); teamNameObject.setY(TEAMNAME_Y_OFFSET); @@ -121,16 +128,23 @@ public class BoatGroup extends RaceObject { velocityObject.setX(VELOCITY_X_OFFSET); velocityObject.setY(VELOCITY_Y_OFFSET); velocityObject.relocate(velocityObject.getX(), velocityObject.getY()); - destinationSet = false; - estTimeToNextMarkObject.setX(ESTTIMETONEXTMARK_X_OFFSET); - estTimeToNextMarkObject.setY(ESTTIMETONEXTMARK_Y_OFFSET); - estTimeToNextMarkObject - .relocate(estTimeToNextMarkObject.getX(), estTimeToNextMarkObject.getY()); + updateLastMarkRoundingTime(); + updateTimeTillNextMark(); - legTimeObject.setX(LEGTIME_X_OFFSET); - legTimeObject.setY(LEGTIME_Y_OFFSET); - legTimeObject.relocate(legTimeObject.getX(), legTimeObject.getY()); + if (estTimeToNextMarkObject != null){ + estTimeToNextMarkObject.setX(ESTTIMETONEXTMARK_X_OFFSET); + estTimeToNextMarkObject.setY(ESTTIMETONEXTMARK_Y_OFFSET); + estTimeToNextMarkObject + .relocate(estTimeToNextMarkObject.getX(), estTimeToNextMarkObject.getY()); + } + + if (legTimeObject != null){ + legTimeObject.setX(LEGTIME_X_OFFSET); + legTimeObject.setY(LEGTIME_Y_OFFSET); + legTimeObject.relocate(legTimeObject.getX(), legTimeObject.getY()); + + } wake = new Wake(0, -BOAT_HEIGHT); super.getChildren() @@ -140,24 +154,21 @@ public class BoatGroup extends RaceObject { /** * Creates the javafx objects that will be the in the group by default. - * * @param color The colour of the boat polygon and the trailing line. */ - private void initChildren(Color color) { + private void initChildren (Color color) { initChildren(color, - -BOAT_WIDTH / 2, BOAT_HEIGHT / 2, - 0.0, -BOAT_HEIGHT / 2, - BOAT_WIDTH / 2, BOAT_HEIGHT / 2); + -BOAT_WIDTH / 2, BOAT_HEIGHT / 2, + 0.0, -BOAT_HEIGHT / 2, + BOAT_WIDTH / 2, BOAT_HEIGHT / 2); } /** - * Moves the boat and its children annotations from its current coordinates by specified - * amounts. - * + * Moves the boat and its children annotations from its current coordinates by specified amounts. * @param dx The amount to move the X coordinate by * @param dy The amount to move the Y coordinate by */ - public void moveGroupBy(double dx, double dy, double rotation) { + private void moveGroupBy(double dx, double dy) { boatPoly.setLayoutX(boatPoly.getLayoutX() + dx); boatPoly.setLayoutY(boatPoly.getLayoutY() + dy); teamNameObject.setLayoutX(teamNameObject.getLayoutX() + dx); @@ -170,28 +181,16 @@ public class BoatGroup extends RaceObject { legTimeObject.setLayoutY(legTimeObject.getLayoutY() + dy); wake.setLayoutX(wake.getLayoutX() + dx); wake.setLayoutY(wake.getLayoutY() + dy); - rotateTo(rotation + currentRotation); } + /** * Moves the boat and its children annotations to coordinates specified - * - * @param x The X coordinate to move the boat to - * @param y The Y coordinate to move the boat to - * @param rotation The heading in degrees from north the boat should rotate to. - */ - public void moveTo(double x, double y, double rotation) { - rotateTo(rotation); - moveTo(x, y); - } - - /** - * Moves the boat and its children annotations to coordinates specified - * * @param x The X coordinate to move the boat to * @param y The Y coordinate to move the boat to */ - public void moveTo(double x, double y) { + private void moveTo(double x, double y, double rotation) { + rotateTo(rotation); boatPoly.setLayoutX(x); boatPoly.setLayoutY(y); teamNameObject.setLayoutX(x); @@ -204,159 +203,165 @@ public class BoatGroup extends RaceObject { legTimeObject.setLayoutY(y); wake.setLayoutX(x); wake.setLayoutY(y); - wake.rotate(currentRotation); + wake.rotate(rotation); } - /** - * Updates the position of all graphics in the BoatGroup based off of the given time interval. - * - * @param timeInterval The interval, in milliseconds, the boat should update it's position based - * on. - */ - public void updatePosition(long timeInterval) { - //Calculate the movement of the boat. - if (isMaximized) { - double dx = pixelVelocityX * timeInterval; - double dy = pixelVelocityY * timeInterval; - double rotation = rotationalVelocity * timeInterval; - distanceTravelled += Math.abs(dx) + Math.abs(dy); - moveGroupBy(dx, dy, rotation); - //Draw a new section of the trail every 20 pixels of movement. - if (distanceTravelled > 20) { - distanceTravelled = 0; - if (lastPoint != null) { - Line l = new Line( - lastPoint.getX(), - lastPoint.getY(), - boatPoly.getLayoutX(), - boatPoly.getLayoutY() - ); - l.getStrokeDashArray().setAll(3d, 7d); - l.setStroke(boat.getColour()); - lineGroup.getChildren().add(l); - } - if (destinationSet) { //Only begin drawing after the first destination is set - lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); - } - } - wake.updatePosition(timeInterval); - } - } - - /** - * Sets the destination of the boat and the headng it should have once it reaches - * - * @param newXValue The X co-ordinate the boat needs to move to. - * @param newYValue The Y co-ordinate the boat needs to move to. - * @param rotation Rotation to move graphics to. - * @param raceIds RaceID of the object to move. - */ - public void setDestination(double newXValue, double newYValue, double rotation, - double groundSpeed, int... raceIds) { - if (hasRaceId(raceIds)) { - if (setToInitialLocation) { - destinationSet = true; - boat.setVelocity(groundSpeed); - if (currentRotation < 0) { - currentRotation = 360 - currentRotation; - } - double dx = newXValue - boatPoly.getLayoutX(); - double dy = newYValue - boatPoly.getLayoutY(); - //Check movement is reasonable. Assumes a 1000 * 1000 canvas - if (Math.abs(dx) > 50 || Math.abs(dy) > 50) { - dx = 0; - dy = 0; - moveTo(newXValue, newYValue); - } - - pixelVelocityX = dx / expectedUpdateInterval; - pixelVelocityY = dy / expectedUpdateInterval; - rotationalGoal = rotation; - calculateRotationalVelocity(); - - if (wakeGenerationDelay > 0) { - wake.rotate(rotationalGoal); - rotateTo(rotationalGoal); //Need to test with this removed. - rotationalVelocity = 0; - wakeGenerationDelay--; - } else { - wake.setRotationalVelocity(rotationalVelocity, rotationalGoal, - boat.getVelocity()); - } - velocityObject.setText(String.format("%.2f m/s", boat.getVelocity())); - DateFormat format = new SimpleDateFormat("mm:ss"); - // estimate time to next mark - String timeToNextMark = format - .format(boat.getEstimateTimeAtNextMark() - StreamParser.getCurrentTimeLong()); - estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark); - // elapsed time - if (boat.getMarkRoundingTime() != null) { - String elapsedTime = format - .format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundingTime()); - legTimeObject.setText("Last mark: " + elapsedTime); - } else { - legTimeObject.setText("Last mark: -"); - } - } else { - setToInitialLocation = true; - rotationalGoal = rotation; - moveTo(newXValue, newYValue, rotation); - } - } - //If minimized generate lines every 5 calls to set destination. - if (!isMaximized) { - setToInitialLocation = false; - wakeGenerationDelay = 2; - if (setCallCount-- == 0) { - setCallCount = 5; - if (lastPoint != null) { - Line l = new Line( - lastPoint.getX(), - lastPoint.getY(), - newXValue, - newYValue - ); - l.getStrokeDashArray().setAll(3d, 7d); - l.setStroke(boatPoly.getFill()); - lineStorage.add(l); - } - if (destinationSet) { //Only begin drawing after the first destination is set - lastPoint = new Point2D(newXValue, newYValue); - } - } - } - } - - public void setDestination(double newXValue, double newYValue, double groundSpeed, - int... raceIDs) { - destinationSet = true; - - if (hasRaceId(raceIDs)) { - double rotation = Math.abs( - Math.toDegrees( - Math.atan( - (newYValue - boatPoly.getLayoutY()) / (newXValue - boatPoly.getLayoutX()) - ) - ) - ); - setDestination(newXValue, newYValue, rotation, groundSpeed, raceIDs); - } - } - - public void rotateTo(double rotation) { - currentRotation = rotation; + private void rotateTo(double rotation) { boatPoly.getTransforms().setAll(new Rotate(rotation)); } - public void forceRotation() { - rotateTo(rotationalGoal); - wake.rotate(rotationalGoal); + /** + * Updates the time until next mark label, will create a label if one doesn't exist + */ + private void updateTimeTillNextMark(){ + if (estTimeToNextMarkObject == null){ + estTimeToNextMarkObject = getTextObject("", textColor); + } + if (boat.getEstimateTimeAtNextMark() != null){ + DateFormat format = new SimpleDateFormat("mm:ss"); + String timeToNextMark = format + .format(boat.getEstimateTimeAtNextMark() - StreamParser.getCurrentTimeLong()); + estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark); + } else { + estTimeToNextMarkObject.setText("Next mark: -"); + } } - public void paintBoat(Color color) { - boatPoly.setFill(color); + /** + * Updates the time since last mark rounding, will create a label if one doesn't exist + */ + private void updateLastMarkRoundingTime(){ + if (legTimeObject == null){ + legTimeObject = getTextObject("", textColor); + } + + if (boat.getMarkRoundingTime() != null){ + DateFormat format = new SimpleDateFormat("mm:ss"); + String elapsedTime = format + .format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundingTime()); + legTimeObject.setText("Last mark: " + elapsedTime); + } + else{ + legTimeObject.setText("Last mark: -"); + + } } + public void move() { + double dx = xIncrement * framesToMove; + double dy = yIncrement * framesToMove; + + distanceTravelled += Math.abs(dx) + Math.abs(dy); + moveGroupBy(xIncrement, yIncrement); + framesToMove = framesToMove - 1; + + if (framesToMove <= 0){ + isStopped = true; + } + + if (distanceTravelled > 70){ + distanceTravelled = 0d; + + if (lastPoint != null){ + Line l = new Line( + lastPoint.getX(), + lastPoint.getY(), + boatPoly.getLayoutX(), + boatPoly.getLayoutY() + ); + l.getStrokeDashArray().setAll(3d, 7d); + l.setStroke(boat.getColour()); + l.setCache(true); + l.setCacheHint(CacheHint.SPEED); + lineGroup.getChildren().add(l); + } + + if (destinationSet){ + lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); + } + } + + wake.updatePosition(1000/60); + } + + /** + * Calculates the rotational velocity required to reach the rotationalGoal from the currentRotation. + */ + protected Double calculateRotationalVelocity (Double rotationalGoal) { + Double rotationalVelocity = 0.0; + + if (Math.abs(rotationalGoal - lastRotation) > 180) { + if (rotationalGoal - lastRotation >= 0.0) { + rotationalVelocity = ((rotationalGoal - lastRotation) - 360) / 200; + } else { + rotationalVelocity = (360 + (rotationalGoal - lastRotation)) / 200; + } + } else { + rotationalVelocity = (rotationalGoal - lastRotation) / 200; + } + + //Sometimes the rotation is too large to be realistic. In that case just do it instantly. + if (Math.abs(rotationalVelocity) > 1) { + rotationalVelocity = 0.0; + } + + return rotationalVelocity; + } + + /** + * Sets the destination of the boat and the headng it should have once it reaches + * @param newXValue The X co-ordinate the boat needs to move to. + * @param newYValue The Y co-ordinate the boat needs to move to. + * @param rotation Rotation to move graphics to. + * @param timeValid the time the position values are valid for + */ + public void setDestination (double newXValue, double newYValue, double rotation, double groundSpeed, long timeValid, double frameRate, long id) { + if (lastTimeValid == 0){ + lastTimeValid = timeValid - 200; + moveTo(newXValue, newYValue, rotation); + } + framesToMove = Math.round((frameRate/(1000.0f/(timeValid-lastTimeValid)))); + double dx = newXValue - boatPoly.getLayoutX(); + double dy = newYValue - boatPoly.getLayoutY(); + + xIncrement = dx/framesToMove; + yIncrement = dy/framesToMove; + + destinationSet = true; + + Double rotationalVelocity = calculateRotationalVelocity(rotation); + + updateTimeTillNextMark(); + updateLastMarkRoundingTime(); + + if (Math.abs(rotationalVelocity) > 0.075) { + rotationalVelocity = 0.0; + wake.rotate(rotation); + } + + rotateTo(rotation); + wake.setRotationalVelocity(rotationalVelocity, groundSpeed); + + velocityObject.setText(String.format("%.2f m/s", groundSpeed)); + lastTimeValid = timeValid; + isStopped = false; + + lastRotation = rotation; + } + + + public void setIsSelected(Boolean isSelected) { + this.isSelected = isSelected; + setTeamNameObjectVisible(isSelected); + setVelocityObjectVisible(isSelected); + setLineGroupVisible(isSelected); + setWakeVisible(isSelected); + setEstTimeToNextMarkObjectVisible(isSelected); + setLegTimeObjectVisible(isSelected); + } + + + public void setTeamNameObjectVisible(Boolean visible) { teamNameObject.setVisible(visible); } @@ -385,85 +390,34 @@ public class BoatGroup extends RaceObject { return boat; } - /** - * This function sets the boats isSelected property AS WELL as actually acting upon the value of - * that selection. (Painting or not painting annotations) - * - * @param isSelected A Boolean indicating whether or not the boat is selected - */ - public void setIsSelected(Boolean isSelected) { - this.isSelected = isSelected; - setTeamNameObjectVisible(isSelected); - setVelocityObjectVisible(isSelected); - setLineGroupVisible(isSelected); - setWakeVisible(isSelected); - setEstTimeToNextMarkObjectVisible(isSelected); - setLegTimeObjectVisible(isSelected); - // TODO: 17/05/17 wmu16 - this should iterate over some list of annotations which we should make to easily make extensible -// paintBoat((isSelected) ? Color.WHITE : boat.getColour()); - } - - /** - * Returns true if this BoatGroup contains at least one of the given IDs. - * - * @param raceIds The ID's to check the BoatGroup for. - * @return True if the BoatGroup contains at east one of the given IDs, false otherwise. - */ - public boolean hasRaceId(int... raceIds) { - for (int id : raceIds) { - if (id == boat.getSourceID()) { - return true; - } - } - return false; - } - /** * Returns all raceIds associated with this group. For BoatGroups the ID's are for the boat. * * @return An array containing all ID's associated with this RaceObject. */ - public int[] getRaceIds() { - return new int[]{boat.getSourceID()}; + public long getRaceId() { + return boat.getSourceID(); } /** - * Due to javaFX limitations annotations associated with a boat that you want to appear below - * all boats in the Z-axis need to be pulled out of the BoatGroup and added to the parent group - * of the BoatGroups. This function returns these annotations as a group. + * Due to javaFX limitations annotations associated with a boat that you want to appear below all boats in the + * Z-axis need to be pulled out of the BoatGroup and added to the parent group of the BoatGroups. This function + * returns these annotations as a group. * * @return A group containing low priority annotations. */ - public Group getLowPriorityAnnotations() { + public Group getLowPriorityAnnotations () { Group group = new Group(); group.getChildren().addAll(wake, lineGroup); return group; } - /** - * Use this function to let the BoatGroup know about the stage it is in. If it knows about it's - * stage then it will listen to the iconified property of that stage and change it's behaviour - * upon minimization. Without setting the Stage there is guarantee that the BoatGroup will draw - * properly when the stage is minimized. - * - * @param stage The stage that the BoatGroup is added to. - */ - public void setStage(Stage stage) { - /* TODO: 4/05/17 cir27 - Find a way to get the stage to this point. Need to pass it through multiple controllers. - App.start() -> Controller.setContentPane -> RaceViewController -> CanvasController - */ - this.stage = stage; - this.stage.iconifiedProperty().addListener(e -> { - isMaximized = !stage.isIconified(); - if (!lineStorage.isEmpty()) { - lineGroup.getChildren().addAll(lineStorage); - lineStorage.clear(); - } - }); + public boolean isStopped() { + return isStopped; } @Override public String toString() { return boat.toString(); } -} +} \ No newline at end of file diff --git a/src/main/java/seng302/models/Colors.java b/src/main/java/seng302/models/Colors.java index 23ef8f4e..8d1b9c25 100644 --- a/src/main/java/seng302/models/Colors.java +++ b/src/main/java/seng302/models/Colors.java @@ -6,7 +6,7 @@ import javafx.scene.paint.Color; * Created by ryan_ on 16/03/2017. */ public enum Colors { - RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE; + RED, PERU, SEAGREEN, GREEN, BLUE, PURPLE; static Integer index = 0; diff --git a/src/main/java/seng302/models/Race.java b/src/main/java/seng302/models/Race.java deleted file mode 100644 index e78b602a..00000000 --- a/src/main/java/seng302/models/Race.java +++ /dev/null @@ -1,198 +0,0 @@ -package seng302.models; - -import seng302.models.mark.Mark; - -import java.util.*; - -/** - * Race class containing the boats and legs in the race - * Created by mra106 on 8/3/2017. - */ -public class Race { - - private ArrayList boats; // The boats in the race - private ArrayList finishingOrder; // The order in which the boats finish the race - private HashMap events = new HashMap<>(); // The events that occur in the race - private List course; // Marks in the race - private long startTime = 0; - private double timeScale = 1; - private boolean raceFinished = false; // Race is finished - private int raceTime = -2; // Current time in the race - - /** - * Race class containing the boats and legs in the race - */ - public Race() { - this.boats = new ArrayList<>(); - this.finishingOrder = new ArrayList<>(); - this.course = new ArrayList<>(); - } - - /** - * Add a boat to the race - * - * @param boat, the boat to add - */ - public void addBoat(Yacht boat) { - boats.add(boat); - } - - /** - * Returns a list of boats in a random order - * - * @return a list of boats - */ - public Yacht[] getShuffledBoats() { - // Shuffle the list of boats - long seed = System.nanoTime(); - Collections.shuffle(this.boats, new Random(seed)); - - return boats.toArray(new Yacht[boats.size()]); - } - - /** - * Returns a list of boats in the order that they - * finished the race (position 0 is first place) - * - * @return a list of boats - */ - public Yacht[] getFinishedBoats() { - return this.finishingOrder.toArray(new Yacht[this.finishingOrder.size()]); - } - - - /** - * Returns a list of boats in the race - * - * @return a list of the boats competing in the race - */ - public Yacht[] getBoats() { - return boats.toArray(new Yacht[boats.size()]); - } - - /** - * Sets time scale - * - * @param timeScale - */ - public void setTimeScale(double timeScale) { - this.timeScale = timeScale; - } - - /** - * Generate all events that will happen during the race. - */ - private void generateEvents() { - - for (Yacht boat : this.boats) { - double totalDistance = 0; - int numberOfMarks = this.course.size(); - - for (int i = 0; i < numberOfMarks; i++) { - Double time = (totalDistance / boat.getVelocity() / timeScale); - - // If there are singleMarks after this event - if (i < numberOfMarks - 1) { - Event event = new Event(time, boat, course.get(i), course.get(i + 1), i); - - try { - events.get(boat).add(event); - - } catch (NullPointerException e) { - events.put(boat, new ArrayList<>(Arrays.asList(event))); - } - totalDistance += event.getDistanceBetweenMarks(); - //System.out.println(totalDistance); - //System.out.println(boat.getVelocity()); - } - - // There are no more marks after this event - - else{ - Event event = new Event(time, boat, course.get(i), i); - events.get(boat).add(event); - } - } - } - } - - - /** - * Starts a race and generates all events for the race. - */ - public void startRace() { - // record start time. - this.startTime = System.currentTimeMillis(); - generateEvents(); - } - - /** - * Set the race course - * @param course a list of marks in the course - */ - public void addCourse(List course) { - this.course = course; - } - - /** - * Get a list of marks in the course - * @return - */ - public List getCourse() { - return course; - } - - /** - * Get a map of the events in the race - * @return - */ - public HashMap getEvents() { - return events; - } - - /** - * Set a boat as finished - * @param boat The boat that has finished the race/home/cosc/student/wmu16 - */ - public void setBoatFinished(Yacht boat){ - this.finishingOrder.add(boat); - } - - /** - * Set the race as finished - */ - public void setRaceFinished(){ - this.raceFinished = true; - } - - /** - * Return whether or not the race is finished - * @return true if the race is finished - */ - public boolean isRaceFinished(){ - return this.raceFinished; - } - - /** - * Set the race time - * @param raceTime the race time in seconds - */ - public void setRaceTime(int raceTime){ - this.raceTime = raceTime; - } - - /** - * Return the race time - * @return the race time in seconds - */ - public int getRaceTime(){ - return this.raceTime; - } - - /** - * Increment the race time by one second - */ - public void incrementRaceTime(){ - this.raceTime += this.timeScale; - } -} \ No newline at end of file diff --git a/src/main/java/seng302/models/RaceObject.java b/src/main/java/seng302/models/RaceObject.java deleted file mode 100644 index 91f02971..00000000 --- a/src/main/java/seng302/models/RaceObject.java +++ /dev/null @@ -1,87 +0,0 @@ -package seng302.models; - -import javafx.geometry.Point2D; -import javafx.scene.Group; - -/** - * RaceObject defines the behaviour that animated objects whose position is updated from a yacht race data stream must - * adhere to. - */ -public abstract class RaceObject extends Group { - - //Time between sections of race - protected static double expectedUpdateInterval = 200; - - protected double rotationalGoal; - protected double currentRotation; - protected double rotationalVelocity; - protected double pixelVelocityX; - protected double pixelVelocityY; - - public Point2D getPosition () { - return new Point2D(super.getLayoutX(), getLayoutY()); - } - - public static double getExpectedUpdateInterval() { - return expectedUpdateInterval; - } - - /** - * - */ - public static void setExpectedUpdateInterval(double expectedUpdateInterval) { - RaceObject.expectedUpdateInterval = expectedUpdateInterval; - } - - /** - * Calculates the rotational velocity required to reach the rotationalGoal from the currentRotation. - */ - protected void calculateRotationalVelocity () { - if (Math.abs(rotationalGoal - currentRotation) > 180) { - if (rotationalGoal - currentRotation >= 0) { - this.rotationalVelocity = ((rotationalGoal - currentRotation) - 360) / expectedUpdateInterval; - } else { - this.rotationalVelocity = (360 + (rotationalGoal - currentRotation)) / expectedUpdateInterval; - } - } else { - this.rotationalVelocity = (rotationalGoal - currentRotation) / expectedUpdateInterval; - } - //Sometimes the rotation is too large to be realistic. In that case just do it instantly. - if (Math.abs(rotationalVelocity) > 1) { - rotationalVelocity = 0; - rotateTo(rotationalGoal); - } - } - - /** - * Sets the destination of everything within the RaceObject that has an ID in the array raceIds. The destination is - * set to the co-ordinates (x, y) with the given rotation. - * @param x X co-ordinate to move the graphics to. - * @param y Y co-ordinate to move the graphics to. - * @param rotation Rotation to move graphics to. - * @param raceIds RaceID of the object to move. - */ - public abstract void setDestination (double x, double y, double rotation, double groundSpeed, int... raceIds); - /** - * Sets the destination of everything within the RaceObject that has an ID in the array raceIds. The destination is - * set to the co-ordinates (x, y). - * @param x X co-ordinate to move the graphic to. - * @param y Y co-ordinate to move the graphic to. - * @param raceIds RaceID to the object to move. - */ - public abstract void setDestination (double x, double y, double groundSpeed, int... raceIds); - - public abstract void updatePosition (long timeInterval); - - public abstract void moveTo (double x, double y, double rotation); - - public abstract void moveTo (double x, double y); - - public abstract void moveGroupBy(double x, double y, double rotation); - - public abstract void rotateTo (double rotation); - - public abstract boolean hasRaceId (int... raceIds); - - public abstract int[] getRaceIds (); -} diff --git a/src/main/java/seng302/models/Wake.java b/src/main/java/seng302/models/Wake.java index 55d4381c..886dfba8 100644 --- a/src/main/java/seng302/models/Wake.java +++ b/src/main/java/seng302/models/Wake.java @@ -1,30 +1,34 @@ package seng302.models; +import javafx.scene.CacheHint; import javafx.scene.Group; import javafx.scene.paint.Color; import javafx.scene.shape.Arc; import javafx.scene.shape.ArcType; +import javafx.scene.shape.StrokeLineCap; import javafx.scene.transform.Rotate; /** - * By default wake is a group containing 5 arcs. Each arc starts from the same point. Each arc is larger and more - * transparent than the last. On calling updatePositions() arcs rotate at velocities given by setRotationalVelocity(). - * The larger and more transparent an arc is the longer the delay before it rotates at the latest velocity. It is - * assumed that rotationalVelocities() are set regularly as wakes do not stop rotating and an array of velocities needs - * to be populated for the class to work as expected. + * A group containing objects used to represent wakes onscreen. Contains functionality for their animation. */ class Wake extends Group { - private int numWakes = 5; - private double[] velocities = new double[13]; + //The number of wakes + private int numWakes = 8; + //The total possible difference between the first wake and the last. Increasing/Decreasing this will make wakes fan out more/less. + private final double MAX_DIFF = 75; + //Increasing/decreasing this will alter the speed that wakes converge when the heading stop changing. Anything over about 1500 may cause oscillation. + private final int UNIFICATION_SPEED = 750; + + private Arc[] arcs = new Arc[numWakes]; + private double[] rotationalVelocities = new double[numWakes]; private double[] rotations = new double[numWakes]; - private int[] velocityIndices = new int[numWakes]; - private double sum = 0; - private static double max; + private double baseRad; /** * Create a wake at the given location. + * * @param startingX x location where the tip of wake arcs will be. * @param startingY y location where the tip of wake arcs will be. */ @@ -34,74 +38,77 @@ class Wake extends Group { Arc arc; for (int i = 0; i < numWakes; i++) { //Default triangle is -110 deg out of phase with a default wake and has angle of 40 deg. - arc = new Arc(0,0,0,0,-110,40); - //Opacity increases from 0.5 -> 0 evenly over the 5 wake arcs. - arc.setFill(new Color(0.18, 0.7, 1.0, 1.0 + -0.175 * i)); - arc.setType(ArcType.ROUND); + arc = new Arc(0, 0, 0, 0, -110, 40); + arc.setCache(true); + arc.setCacheHint(CacheHint.SPEED); + arc.setType(ArcType.OPEN); + arc.setStroke(new Color(0.18, 0.7, 1.0, 1.0 + (-0.99 / numWakes * i))); + arc.setStrokeWidth(3.0); + arc.setStrokeLineCap(StrokeLineCap.ROUND); + arc.setFill(new Color(0.0, 0.0, 0.0, 0.0)); + baseRad = (20 / numWakes); arcs[i] = arc; } super.getChildren().addAll(arcs); } /** - * Sets the rotationalVelocity of each arc. Each arc is 3 velocities behind the next smallest arc. The smallest uses - * the latest given velocity. + * Sets the rotationalVelocity of each arc. + * * @param rotationalVelocity The rotationalVelocity the wake should move at. - * @param rotationGoal Where the wake will rotate to if the wake is calculated to be on a straight section. This is - * used to prevent desynchronisation with the Boat polygon. - * @param velocity The real world velocity of the boat in m/s. + * @param velocity The real world velocity of the boat in m/s. */ - void setRotationalVelocity (double rotationalVelocity, double rotationGoal, double velocity) { - sum -= Math.abs(velocities[(velocityIndices[0] + 10) % 13]); - sum += Math.abs(rotationalVelocity); - max = Math.max(max, rotationalVelocity); - if (sum < (max / 3)) - rotate (rotationGoal); //In relatively straight segments the wake snaps to match the boats current position. - //This stops the wake from eventually becoming out of sync with the boat. - //This accounts for rogue rotations that are greater than what would be realistic. Value is kinda rough. - //Basically just for our internal mock. - if (Math.abs(rotationalVelocity) > 0.05) { - rotationalVelocity = 0; - rotate(rotationGoal); - } - //Update the index of the array of recent velocities that each wake uses. Each wake is 3 velocities behind the - //next smallest wake. - velocityIndices[0] = (13 + (velocityIndices[0] - 1) % 13) % 13; - velocities[velocityIndices[0]] = rotationalVelocity; - for (int i = 1; i < numWakes; i++) - velocityIndices[i] = (velocityIndices[0] + 3 * i) % 13; + void setRotationalVelocity(double rotationalVelocity, double velocity) { + rotationalVelocities[0] = rotationalVelocity; + for (int i = 1; i < numWakes; i++) { + double wakeSeparationRad = Math.toRadians(rotations[i - 1] - rotations[i]); + double shortestDistance = Math.atan2( + Math.sin(wakeSeparationRad), + Math.cos(wakeSeparationRad) + ); + double distDeg = Math.toDegrees(shortestDistance); - //Scale wakes based on velocity. - double baseRad = 20; - double rad; - for (Arc arc :arcs) { - rad = baseRad + velocity; + if (rotationalVelocities[i - 1] < 0.01 && rotationalVelocities[i - 1] > -0.01) { + rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes); + + } else { + if (distDeg < (MAX_DIFF / numWakes)) + rotationalVelocities[i] = rotationalVelocities[i - 1] * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes); + else + rotationalVelocities[i] = rotationalVelocities[i - 1]; + } + } + + double rad = baseRad + velocity; + for (Arc arc : arcs) { arc.setRadiusX(rad); arc.setRadiusY(rad); - baseRad += 5 + (velocity / 2); + rad += (20 / numWakes) + (velocity / 2); } } /** * Arcs rotate based on the distance they would have travelled over the supplied time interval. + * * @param timeInterval the time interval, in microseconds, that the wake should move. */ - void updatePosition (long timeInterval) { + void updatePosition(long timeInterval) { for (int i = 0; i < numWakes; i++) { - rotations[i] = rotations[i] + velocities[velocityIndices[i]] * timeInterval; + rotations[i] = rotations[i] + rotationalVelocities[i] * timeInterval; arcs[i].getTransforms().setAll(new Rotate(rotations[i])); } } /** * Rotate all wakes to the given rotation. + * * @param rotation the from north angle in degrees to rotate to. */ - void rotate (double rotation) { + void rotate(double rotation) { for (int i = 0; i < arcs.length; i++) { rotations[i] = rotation; + rotationalVelocities[i] = 0; arcs[i].getTransforms().setAll(new Rotate(rotation)); } } - } diff --git a/src/main/java/seng302/models/Yacht.java b/src/main/java/seng302/models/Yacht.java index 23ba616a..8ae5d76c 100644 --- a/src/main/java/seng302/models/Yacht.java +++ b/src/main/java/seng302/models/Yacht.java @@ -151,7 +151,7 @@ public class Yacht { this.colour = colour; } - public double getVelocity() { + public Double getVelocity() { return velocity; } diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java index d58f3b78..0e886abc 100644 --- a/src/main/java/seng302/models/mark/MarkGroup.java +++ b/src/main/java/seng302/models/mark/MarkGroup.java @@ -1,13 +1,11 @@ package seng302.models.mark; import javafx.geometry.Point2D; -import javafx.scene.CacheHint; -import javafx.scene.Node; +import javafx.scene.Group; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; import javafx.scene.transform.Rotate; -import seng302.models.RaceObject; import java.util.ArrayList; import java.util.List; @@ -15,7 +13,7 @@ import java.util.List; /** * Created by CJIRWIN on 26/04/2017. */ -public class MarkGroup extends RaceObject { +public class MarkGroup extends Group { private static int MARK_RADIUS = 5; private static int LINE_THICKNESS = 2; @@ -24,14 +22,8 @@ public class MarkGroup extends RaceObject { private List marks = new ArrayList<>(); private Mark mainMark; - private double[] nodePixelVelocitiesX; - private double[] nodePixelVelocitiesY; - private Point2D[] nodeDestinations; public MarkGroup (Mark mark, Point2D... points) { - nodePixelVelocitiesX = new double[points.length]; - nodePixelVelocitiesY = new double[points.length]; - nodeDestinations = new Point2D[points.length]; marks.add(mark); mainMark = mark; Color color = Color.BLACK; @@ -43,45 +35,33 @@ public class MarkGroup extends RaceObject { Circle markCircle; if (mark.getMarkType() == MarkType.SINGLE_MARK) { markCircle = new Circle( - points[0].getX(), - points[0].getY(), - MARK_RADIUS, - color + points[0].getX(), + points[0].getY(), + MARK_RADIUS, + color ); - nodeDestinations = new Point2D[]{ - new Point2D(markCircle.getCenterX(), markCircle.getCenterY() - ) - }; super.getChildren().add(markCircle); } else { - marks.add(((GateMark) mark).getSingleMark1()); - marks.add(((GateMark) mark).getSingleMark2()); - nodePixelVelocitiesX = new double[]{0d,0d}; - nodePixelVelocitiesY = new double[]{0d,0d}; - nodeDestinations = new Point2D[2]; - markCircle = new Circle( - points[0].getX(), - points[0].getY(), - MARK_RADIUS, - color + points[0].getX(), + points[0].getY(), + MARK_RADIUS, + color ); - nodeDestinations[0] = new Point2D(markCircle.getCenterX(), markCircle.getCenterY()); super.getChildren().add(markCircle); markCircle = new Circle( - points[1].getX(), - points[1].getY(), - MARK_RADIUS, - color + points[1].getX(), + points[1].getY(), + MARK_RADIUS, + color ); - nodeDestinations[1] = new Point2D(markCircle.getCenterX(), markCircle.getCenterY()); super.getChildren().add(markCircle); Line line = new Line( - points[0].getX(), - points[0].getY(), - points[1].getX(), - points[1].getY() + points[0].getX(), + points[0].getY(), + points[1].getX(), + points[1].getY() ); line.setStrokeWidth(LINE_THICKNESS); line.setStroke(color); @@ -92,136 +72,38 @@ public class MarkGroup extends RaceObject { } } - public void setDestination (double x, double y, double rotation, double groundSpeed, int... raceIds) { - setDestination(x, y, 0, raceIds); - this.rotationalGoal = rotation; - calculateRotationalVelocity(); - } - - public void setDestination (double x, double y, double groundSpeed, int... raceIds) { - for (int i = 0; i < marks.size(); i++) - for (int id : raceIds) - if (id == marks.get(i).getId()) - setDestinationChild(x, y, 0, Math.max(0, i-1)); - } - - - private void setDestinationChild (double x, double y, double speed, int childIndex) { - //double relativeX = x - super.getLayoutX(); - //double relativeY = y - super.getLayoutY(); - Circle markCircle = (Circle) super.getChildren().get(childIndex); - this.nodeDestinations[childIndex] = new Point2D(x, y); - //if (Math.abs(relativeX - markCircle.getCenterX()) > 30 && Math.abs(relativeY - markCircle.getCenterY()) > 30) { - this.nodePixelVelocitiesX[childIndex] = (x - markCircle.getCenterX()) / expectedUpdateInterval; - this.nodePixelVelocitiesY[childIndex] = (y - markCircle.getCenterY()) / expectedUpdateInterval; - //} - } - - public void rotateTo (double rotation) { - if (mainMark.getMarkType() != MarkType.SINGLE_MARK) { - Line line = (Line) super.getChildren().get(2); - double xCenter = Math.abs(line.getEndX() - line.getStartX()); - double yCenter = Math.abs(line.getEndY() - line.getStartY()); - super.getTransforms().setAll(new Rotate(rotation, xCenter, yCenter)); - } - } - - public void updatePosition (long timeInterval) { - Circle markCircle = (Circle) super.getChildren().get(0); - - if (nodePixelVelocitiesX[0] > 0 && markCircle.getCenterX() > nodeDestinations[0].getX() || - nodePixelVelocitiesX[0] < 0 && markCircle.getCenterX() < nodeDestinations[0].getY()) - nodePixelVelocitiesX[0] = 0; - else if (nodePixelVelocitiesX[0] != 0) - markCircle.setCenterX(markCircle.getCenterX() + nodePixelVelocitiesX[0] * timeInterval); - - if (nodePixelVelocitiesY[0] > 0 && markCircle.getCenterY() > nodeDestinations[0].getY() || - nodePixelVelocitiesY[0] < 0 && markCircle.getCenterY() < nodeDestinations[0].getY()) - nodePixelVelocitiesY[0] = 0; - else if (nodePixelVelocitiesY[0] != 0) - markCircle.setCenterY(markCircle.getCenterY() + nodePixelVelocitiesY[0] * timeInterval); - - if (mainMark.getMarkType() != MarkType.SINGLE_MARK) { - - Line line = (Line) super.getChildren().get(2); - line.setStartX(markCircle.getCenterX()); - line.setStartY(markCircle.getCenterY()); - - markCircle = (Circle) super.getChildren().get(1); - - if (nodePixelVelocitiesX[1] > 0 && markCircle.getCenterX() >= nodeDestinations[1].getX() || - nodePixelVelocitiesX[1] < 0 && markCircle.getCenterX() <= nodeDestinations[1].getX()) - nodePixelVelocitiesX[1] = 0; - else if (nodePixelVelocitiesX[1] != 0) - markCircle.setCenterX(markCircle.getCenterX() + nodePixelVelocitiesX[1] * timeInterval); - - if (nodePixelVelocitiesY[1] > 0 && markCircle.getCenterY() > nodeDestinations[1].getY() || - nodePixelVelocitiesY[1] < 0 && markCircle.getCenterY() < nodeDestinations[1].getY()) - nodePixelVelocitiesY[1] = 0; - else if (nodePixelVelocitiesY[1] != 0) - markCircle.setCenterY(markCircle.getCenterY() + nodePixelVelocitiesY[1] * timeInterval); - line.setEndX(markCircle.getCenterX()); - line.setEndY(markCircle.getCenterY()); - } - } - - public void moveGroupBy (double x, double y, double rotation) { - if (mainMark.getMarkType() != MarkType.SINGLE_MARK) { - Line line = (Line) super.getChildren().get(2); - for (int childIndex = 0; childIndex < 2; childIndex++){ - Circle mark = (Circle) super.getChildren().get(childIndex); - mark.setCenterY(mark.getCenterY() + y); - mark.setCenterX(mark.getCenterX() + x); - } - line.setStartX(line.getStartX() + x); - line.setStartY(line.getStartY() + y); - line.setEndX(line.getEndX() + x); - line.setEndY(line.getEndY() + y); - } else { - Circle mark = (Circle) super.getChildren().get(0); - mark.setCenterY(mark.getCenterY() + y); - mark.setCenterX(mark.getCenterX() + x); - } - rotateTo(currentRotation + rotation); - } - - public void moveTo (double x, double y, double rotation) { - moveTo(x, y); - rotateTo(rotation); - } - - public void moveTo (double x, double y) { - Circle markCircle = (Circle) super.getChildren().get(0); - markCircle.setCenterX(x); - markCircle.setCenterY(y); - if (mainMark.getMarkType() != MarkType.SINGLE_MARK) { - markCircle = (Circle) super.getChildren().get(1); + public void moveMarkTo (double x, double y, int raceId) + { + if (mainMark.getMarkType() == MarkType.SINGLE_MARK) { + Circle markCircle = (Circle) super.getChildren().get(0); markCircle.setCenterX(x); markCircle.setCenterY(y); - Line line = (Line) super.getChildren().get(2); - line.setStartX(x); - line.setStartY(y); - line.setEndX(x); - line.setEndY(y); + } else { + Circle markCircle1 = (Circle) super.getChildren().get(0); + Circle markCircle2 = (Circle) super.getChildren().get(1); + Line connectingLine = (Line) super.getChildren().get(2); + if (marks.get(1).getId() == raceId) { + markCircle1.setCenterX(x); + markCircle1.setCenterY(y); + connectingLine.setStartX(markCircle1.getCenterX()); + connectingLine.setStartY(markCircle1.getCenterY()); + } else if (marks.get(2).getId() == raceId) { + markCircle2.setCenterX(x); + markCircle2.setCenterY(y); + connectingLine.setEndX(markCircle2.getCenterX()); + connectingLine.setEndY(markCircle2.getCenterY()); + } } } public boolean hasRaceId (int... raceIds) { for (int id : raceIds) for (Mark mark : marks) - if (id == mark.getId()) - return true; + if (id == mark.getId()) + return true; return false; } - public static int getMarkRadius() { - return MARK_RADIUS; - } - - public static void setMarkRadius(int markRadius) { - MARK_RADIUS = markRadius; - } - public int[] getRaceIds () { int[] idArray = new int[marks.size()]; int i = 0; @@ -229,4 +111,4 @@ public class MarkGroup extends RaceObject { idArray[i++] = mark.getId(); return idArray; } -} +} \ No newline at end of file diff --git a/src/main/java/seng302/models/parsers/ConfigParser.java b/src/main/java/seng302/models/parsers/ConfigParser.java deleted file mode 100644 index 1d870c67..00000000 --- a/src/main/java/seng302/models/parsers/ConfigParser.java +++ /dev/null @@ -1,78 +0,0 @@ -package seng302.models.parsers; - - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.util.DoubleSummaryStatistics; - -public class ConfigParser extends FileParser { - - private Document doc; - - public ConfigParser(String path) { - super(path); - this.doc = this.parseFile(); - } - - /** - * Gets wind direction from config file. - * - * @return a double type degree, or 0 if no value or invalid value is found - */ - public double getWindDirection() { - return getDoubleByTagName("wind-direction", 0.0); - } - - /** - * Gets a non negative time scale for the race - * - * @return a double type scale, or 0 if no scale or invalid scale is found - */ - public double getTimeScale() { - return getDoubleByTagName("time-scale", 1.0); - } - - /** - * Gets a double type number by given tag name found in xml file - * - * @param tagName a string of tag name - * @param defaultVal value returned if no value or invalid value is found - * @return value found - */ - public double getDoubleByTagName(String tagName, double defaultVal) { - double val = defaultVal; - try { - Node node = this.doc.getElementsByTagName(tagName).item(0); - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) node; - val = Double.valueOf(element.getTextContent()); - } - } catch (Exception e) { - } finally { - return val; - } - } - - /** - * Gets a string by given tag name found in xml file - * - * @param tagName a string of tag name - * @param defaultVal a string returned if no value or invalid value is found - * @return string found - */ - public String getStringByTagName(String tagName, String defaultVal) { - String string = defaultVal; - try { - Node node = this.doc.getElementsByTagName(tagName).item(0); - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) node; - string = element.getTextContent(); - } - } catch (Exception e) { - } finally { - return string; - } - } -} diff --git a/src/main/java/seng302/models/parsers/CourseParser.java b/src/main/java/seng302/models/parsers/CourseParser.java deleted file mode 100644 index ae7f7856..00000000 --- a/src/main/java/seng302/models/parsers/CourseParser.java +++ /dev/null @@ -1,145 +0,0 @@ -package seng302.models.parsers; - -import org.w3c.dom.*; -import seng302.models.mark.*; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.NoSuchElementException; - -/** - * parse a course xml file - * Created by Haoming Yin (hyi25) on 16/3/2017 - */ -public class CourseParser extends FileParser { - - private Document doc; - private HashMap marks = new HashMap<>(); - - public CourseParser(String path) { - super(path); - this.doc = this.parseFile(); - } - - /** - * create a mark by given node - * - * @param node - * @return a mark, or null if fails to create a mark - */ - private SingleMark generateSingleMark(Node node) { - try { - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) node; - String name = element.getElementsByTagName("name").item(0).getTextContent(); - double lat = Double.valueOf(element.getElementsByTagName("latitude").item(0).getTextContent()); - double lon = Double.valueOf(element.getElementsByTagName("longitude").item(0).getTextContent()); - int id = Integer.valueOf(element.getElementsByTagName("id").item(0).getTextContent()); - SingleMark singleMark = new SingleMark(name, lat, lon, id); - return singleMark; - } else { - throw new NoSuchElementException("Cannot generate a mark by given node."); - } - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - /** - * generate an arrayList of gates - * - * @return an arrayList of gates, or null if no gate has been found. - */ - private void generateGateMarks() { - ArrayList gateMarks = new ArrayList<>(); - - try { - NodeList nodes = doc.getElementsByTagName("gate"); - - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) node; - String name = element.getElementsByTagName("name").item(0).getTextContent(); - SingleMark mark1 = generateSingleMark(element.getElementsByTagName("mark").item(0)); - SingleMark mark2 = generateSingleMark(element.getElementsByTagName("mark").item(1)); - GateMark gateMark; - if (name.equals("Start") || name.equals("Finish")) - gateMark = new GateMark(name, MarkType.CLOSED_GATE, mark1, mark2, mark1.getLatitude(), mark1.getLongitude()); - else - gateMark = new GateMark(name, MarkType.OPEN_GATE, mark1, mark2, mark1.getLatitude(), mark1.getLongitude()); - marks.put(name, gateMark); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * generate an arrayList of marks - * - * @return an arrayList of marks, or null if no gate has been found. - */ - private void generateSingleMarks() { - ArrayList singleMarks = new ArrayList<>(); - - try { - // find the "marks" tag - Node node = doc.getElementsByTagName("marks").item(0); - // iterate all "marks"'s children - for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) { - // if node's tag name is "mark" - if (n.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) n; - if (element.getNodeName() == "mark") { - Mark mark = generateSingleMark(n); - marks.put(mark.getName(), mark); - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * return the order of all the marks along a course - * - * @return an arrayList of the names of ordered course marks - */ - private ArrayList getOrder() { - ArrayList markOrder = new ArrayList<>(); - - try { - Node orderNode = doc.getElementsByTagName("order").item(0); - for (Node node = orderNode.getFirstChild(); node != null; node = node.getNextSibling()) { - if (node.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) node; - String name = element.getTextContent(); - markOrder.add(name); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - return markOrder; - } - - public ArrayList getCourse() { - generateSingleMarks(); - generateGateMarks(); - ArrayList course = new ArrayList<>(); - try { - for (String mark : getOrder()) { - course.add(marks.get(mark)); - } - } catch (Exception e) { - e.printStackTrace(); - } - return course; - } -} diff --git a/src/main/java/seng302/models/parsers/FileParser.java b/src/main/java/seng302/models/parsers/FileParser.java deleted file mode 100644 index be162b9e..00000000 --- a/src/main/java/seng302/models/parsers/FileParser.java +++ /dev/null @@ -1,54 +0,0 @@ -package seng302.models.parsers; - -import org.w3c.dom.Document; -import org.xml.sax.InputSource; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; - -/** - * Created by Haoming Yin (hyi25) on 16/3/2017 - */ -public abstract class FileParser { - - private String filePath; - - public FileParser() {} - - public FileParser(String path) { - this.filePath = path; - } - - protected Document parseFile() { - try { - InputStream is = getClass().getResourceAsStream(this.filePath); - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(is); - // optional, in order to recover info from broken line. - doc.getDocumentElement().normalize(); - return doc; - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - protected Document parseFile(String xmlString) { - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(new InputSource(new StringReader(xmlString))); - // optional, in order to recover info from broken line. - doc.getDocumentElement().normalize(); - return doc; - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } -} diff --git a/src/main/java/seng302/models/parsers/TeamsParser.java b/src/main/java/seng302/models/parsers/TeamsParser.java deleted file mode 100644 index fae0a743..00000000 --- a/src/main/java/seng302/models/parsers/TeamsParser.java +++ /dev/null @@ -1,64 +0,0 @@ -//package seng302.models.parsers; -// -//import org.w3c.dom.*; -//import seng302.models.Yacht; -// -//import java.util.ArrayList; -//import java.util.NoSuchElementException; -// -//public class TeamsParser extends FileParser { -// -// private Document doc; -// -// public TeamsParser(String path) { -// super(path); -// this.doc = this.parseFile(); -// } -// -// /** -// * Create a boat instance by a given team node -// * @param node a boat node containing name, alias and velocity -// * @return an instance of Boat -// */ -// private Yacht parseBoat(Node node) { -// try { -// if (node.getNodeType() == Node.ELEMENT_NODE) { -// Element element = (Element) node; -// String name = element.getElementsByTagName("name").item(0).getTextContent(); -// String alias = element.getElementsByTagName("alias").item(0).getTextContent(); -// double velocity = Double.valueOf(element.getElementsByTagName("velocity").item(0).getTextContent()); -// int id = Integer.valueOf(element.getElementsByTagName("id").item(0).getTextContent()); -// Yacht boat = new Yacht(name, velocity, alias, id); -// return boat; -// } else { -// throw new NoSuchElementException("Cannot generate a boat by given node"); -// } -// } catch (Exception e) { -// e.printStackTrace(); -// return null; -// } -// } -// -// /** -// * Create an arraylist of boats instance. -// * @return an arraylist of boats in teams file -// */ -// public ArrayList getBoats() { -// ArrayList boats = new ArrayList<>(); -// -// try { -// NodeList nodes = this.doc.getElementsByTagName("team"); -// for (int i = 0; i < nodes.getLength(); i++) { -// Node node = nodes.item(i); -// boats.add(parseBoat(node)); -// } -// return boats; -// } catch (Exception e) { -// e.printStackTrace(); -// return null; -// } -// } -// -// -//} -// diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/stream/StreamParser.java similarity index 93% rename from src/main/java/seng302/models/parsers/StreamParser.java rename to src/main/java/seng302/models/stream/StreamParser.java index ef35a7ff..f7093306 100644 --- a/src/main/java/seng302/models/parsers/StreamParser.java +++ b/src/main/java/seng302/models/stream/StreamParser.java @@ -1,12 +1,12 @@ -package seng302.models.parsers; +package seng302.models.stream; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import seng302.models.Yacht; -import seng302.models.parsers.packets.BoatPositionPacket; -import seng302.models.parsers.packets.StreamPacket; +import seng302.models.stream.packets.BoatPositionPacket; +import seng302.models.stream.packets.StreamPacket; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -17,7 +17,9 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.PriorityBlockingQueue; +import seng302.models.stream.XMLParser; /** * The purpose of this class is to take in the stream of divided packets so they can be read @@ -36,8 +38,8 @@ public class StreamParser extends Thread{ private static boolean raceFinished = false; private static boolean streamStatus = false; private static long timeSinceStart = -1; - private static Map boats = new HashMap<>(); - private static Map boatsPos = new TreeMap<>(); + private static Map boats = new ConcurrentHashMap<>(); + private static Map boatsPos = new ConcurrentSkipListMap<>(); private static double windDirection = 0; private static Long currentTimeLong; private static String currentTimeString; @@ -67,24 +69,10 @@ public class StreamParser extends Thread{ Thread.sleep(1); } while (appRunning){ - StreamPacket packet = StreamReceiver.packetBuffer.peek(); - //this code adds a delay to reading from the packetBuffer so - //out of order packets have time to order themselves in the queue - int delayTime = 1000; - int loopTime = delayTime * 10; - long transitTime = (System.currentTimeMillis()%loopTime - packet.getTimeStamp()%loopTime); - if (transitTime < 0){ - transitTime = loopTime + transitTime; - } - if (transitTime < delayTime) { - long sleepTime = delayTime - (transitTime); - Thread.sleep(sleepTime); - } - packet = StreamReceiver.packetBuffer.take(); + StreamPacket packet = StreamReceiver.packetBuffer.take(); parsePacket(packet); Thread.sleep(1); while (StreamReceiver.packetBuffer.peek() == null) { - Thread.sleep(1); } } } catch (Exception e){ @@ -223,7 +211,6 @@ public class StreamParser extends Thread{ raceFinished = false; System.out.println("[CLIENT] Race has started"); } - //System.out.println("Time since start: " + -1 * timeTillStart + " Seconds"); timeSinceStart = timeTillStart; } @@ -232,11 +219,10 @@ public class StreamParser extends Thread{ int noBoats = payload[22]; int raceType = payload[23]; -// ArrayList boatStatuses = new ArrayList<>(); boatsPos = new TreeMap<>(); for (int i = 0; i < noBoats; i++){ - Long boatStatusSourceID = bytesToLong(Arrays.copyOfRange(payload,24 + (i * 20),28+ (i * 20))); - Yacht boat = boats.get((int)(long) boatStatusSourceID); + long boatStatusSourceID = bytesToLong(Arrays.copyOfRange(payload,24 + (i * 20),28+ (i * 20))); + Yacht boat = boats.get((int) boatStatusSourceID); boat.setBoatStatus((int)payload[28 + (i * 20)]); boat.setLegNumber((int)payload[29 + (i * 20)]); boat.setPenaltiesAwarded((int)payload[30 + (i * 20)]); @@ -364,7 +350,6 @@ public class StreamParser extends Thread{ long subjectId = bytesToLong(Arrays.copyOfRange(payload,9,13)); long incidentId = bytesToLong(Arrays.copyOfRange(payload,13,17)); int eventId = payload[17]; -// System.out.println("eventId = " + eventId); } /** @@ -402,19 +387,18 @@ public class StreamParser extends Thread{ double groundSpeed = bytesToLong(Arrays.copyOfRange(payload,38,40))/1000.0; //type 1 is a racing yacht and type 3 is a mark, needed for updating positions of the mark and boat - if (deviceType == 1 || deviceType == 3){ + if (deviceType == 1){ BoatPositionPacket boatPacket = new BoatPositionPacket(boatId, timeValid, lat, lon, heading, groundSpeed); //add a new priority que to the boatPositions HashMap if (!boatPositions.containsKey(boatId)){ - boatPositions.put(boatId, new PriorityBlockingQueue(256, new Comparator() { + boatPositions.put(boatId, new PriorityBlockingQueue<>(256, new Comparator() { @Override public int compare(BoatPositionPacket p1, BoatPositionPacket p2) { return (int) (p1.getTimeValid() - p2.getTimeValid()); } })); } - //Adding the boatPacket to the priority que boatPositions.get(boatId).put(boatPacket); } } diff --git a/src/main/java/seng302/models/parsers/StreamReceiver.java b/src/main/java/seng302/models/stream/StreamReceiver.java similarity index 97% rename from src/main/java/seng302/models/parsers/StreamReceiver.java rename to src/main/java/seng302/models/stream/StreamReceiver.java index 65d7c525..b3303806 100644 --- a/src/main/java/seng302/models/parsers/StreamReceiver.java +++ b/src/main/java/seng302/models/stream/StreamReceiver.java @@ -1,13 +1,11 @@ -package seng302.models.parsers; +package seng302.models.stream; -import seng302.models.parsers.packets.StreamPacket; +import seng302.models.stream.packets.StreamPacket; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; -import java.util.ArrayList; -import java.util.Collection; import java.util.Comparator; import java.util.concurrent.PriorityBlockingQueue; import java.util.zip.CRC32; diff --git a/src/main/java/seng302/models/parsers/XMLParser.java b/src/main/java/seng302/models/stream/XMLParser.java similarity index 98% rename from src/main/java/seng302/models/parsers/XMLParser.java rename to src/main/java/seng302/models/stream/XMLParser.java index f8d30460..181c1e2f 100644 --- a/src/main/java/seng302/models/parsers/XMLParser.java +++ b/src/main/java/seng302/models/stream/XMLParser.java @@ -1,14 +1,14 @@ -package seng302.models.parsers; +package seng302.models.stream; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import seng302.models.Yacht; +import seng302.models.mark.MarkType; import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -301,6 +301,7 @@ public class XMLParser { public class CompoundMark { private Integer markID; private String cMarkName; + private MarkType markType; private ArrayList marks; CompoundMark(Node compoundMark) { @@ -308,6 +309,12 @@ public class XMLParser { this.markID = getNodeAttributeInt(compoundMark, "CompoundMarkID"); this.cMarkName = getNodeAttributeString(compoundMark, "Name"); NodeList childMarks = compoundMark.getChildNodes(); + if (childMarks.getLength() > 1){ + markType = MarkType.OPEN_GATE; + } else { + markType = MarkType.SINGLE_MARK; + } + for (int i = 0; i < childMarks.getLength(); i++) { Node markNode = childMarks.item(i); if (markNode.getNodeName().equals("Mark")) { @@ -319,6 +326,7 @@ public class XMLParser { public Integer getMarkID() { return markID; } public String getcMarkName() { return cMarkName; } + public MarkType getMarkType() { return markType; } public ArrayList getMarks() { return marks; } public class Mark { diff --git a/src/main/java/seng302/models/parsers/packets/BoatPositionPacket.java b/src/main/java/seng302/models/stream/packets/BoatPositionPacket.java similarity index 95% rename from src/main/java/seng302/models/parsers/packets/BoatPositionPacket.java rename to src/main/java/seng302/models/stream/packets/BoatPositionPacket.java index d6f0700d..859223e0 100644 --- a/src/main/java/seng302/models/parsers/packets/BoatPositionPacket.java +++ b/src/main/java/seng302/models/stream/packets/BoatPositionPacket.java @@ -1,4 +1,4 @@ -package seng302.models.parsers.packets; +package seng302.models.stream.packets; public class BoatPositionPacket { private long boatId; diff --git a/src/main/java/seng302/models/parsers/packets/PacketType.java b/src/main/java/seng302/models/stream/packets/PacketType.java similarity index 96% rename from src/main/java/seng302/models/parsers/packets/PacketType.java rename to src/main/java/seng302/models/stream/packets/PacketType.java index f522dec9..0fd0be84 100644 --- a/src/main/java/seng302/models/parsers/packets/PacketType.java +++ b/src/main/java/seng302/models/stream/packets/PacketType.java @@ -1,4 +1,4 @@ -package seng302.models.parsers.packets; +package seng302.models.stream.packets; /** * Created by Kusal on 4/24/2017. @@ -48,6 +48,4 @@ public enum PacketType { } return OTHER; } - - } diff --git a/src/main/java/seng302/models/parsers/packets/StreamPacket.java b/src/main/java/seng302/models/stream/packets/StreamPacket.java similarity index 95% rename from src/main/java/seng302/models/parsers/packets/StreamPacket.java rename to src/main/java/seng302/models/stream/packets/StreamPacket.java index 4f10008c..22f2fe56 100644 --- a/src/main/java/seng302/models/parsers/packets/StreamPacket.java +++ b/src/main/java/seng302/models/stream/packets/StreamPacket.java @@ -1,4 +1,4 @@ -package seng302.models.parsers.packets; +package seng302.models.stream.packets; /** * Created by kre39 on 23/04/17. diff --git a/src/main/java/seng302/server/messages/BoatLocationMessage.java b/src/main/java/seng302/server/messages/BoatLocationMessage.java index 6cf0739d..5e605170 100644 --- a/src/main/java/seng302/server/messages/BoatLocationMessage.java +++ b/src/main/java/seng302/server/messages/BoatLocationMessage.java @@ -1,10 +1,7 @@ package seng302.server.messages; -import java.io.DataOutputStream; import java.io.IOException; -import java.nio.channels.Channels; import java.nio.channels.SocketChannel; -import java.nio.channels.WritableByteChannel; public class BoatLocationMessage extends Message { private final int MESSAGE_SIZE = 56; @@ -44,7 +41,7 @@ public class BoatLocationMessage extends Message { public BoatLocationMessage(int sourceId, int sequenceNum, double latitude, double longitude, double heading, long boatSpeed){ boatSpeed /= 10; messageVersionNumber = 1; - time = System.currentTimeMillis() / 1000L; + time = System.currentTimeMillis(); this.sourceId = sourceId; this.sequenceNum = sequenceNum; this.deviceType = DeviceType.RACING_YACHT; diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index cfb36f23..7b81dd19 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -1,21 +1,9 @@ - - - - - - - - - - - - diff --git a/src/test/java/seng302/ColorsTest.java b/src/test/java/seng302/ColorsTest.java index 6fc07b41..03bc9ad2 100644 --- a/src/test/java/seng302/ColorsTest.java +++ b/src/test/java/seng302/ColorsTest.java @@ -8,14 +8,11 @@ import seng302.models.Colors; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -/** - * Created by ryan_ on 16/03/2017. - */ public class ColorsTest { @Test public void testNextColor() { - Color expectedColors[] = {Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.BLUE, Color.PURPLE}; + Color expectedColors[] = {Color.RED, Color.PERU, Color.SEAGREEN, Color.GREEN, Color.BLUE, Color.PURPLE}; for (int i = 0; i<6; i++) { Assert.assertEquals(expectedColors[i], Colors.getColor()); diff --git a/src/test/java/seng302/RaceTest.java b/src/test/java/seng302/RaceTest.java deleted file mode 100644 index 2784cd47..00000000 --- a/src/test/java/seng302/RaceTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package seng302; - -import org.junit.Test; -import seng302.models.Race; -import seng302.models.Yacht; - -import java.lang.reflect.Array; - -import static org.junit.Assert.assertEquals; - -/** - * Unit test for the Race class. - */ -public class RaceTest { - /** - * Test that all boats were added to the race - */ - @Test - public void testAddingBoatsToRace() { - Yacht boat1 = new Yacht("Team 1"); - Yacht boat2 = new Yacht("Team 2"); - - Race race = new Race(); - race.addBoat(boat1); - race.addBoat(boat2); - - assertEquals(Array.getLength(race.getBoats()), 2); - } - - @Test - public void testGetShuffledBoats(){ - Yacht boat1 = new Yacht("Team 1"); - Yacht boat2 = new Yacht("Team 2"); - - Race race = new Race(); - race.addBoat(boat1); - race.addBoat(boat2); - - assertEquals(Array.getLength(race.getShuffledBoats()), 2); - } -} diff --git a/src/test/java/seng302/models/parsers/ConfigParserTest.java b/src/test/java/seng302/models/parsers/ConfigParserTest.java deleted file mode 100644 index 8a0c0c8c..00000000 --- a/src/test/java/seng302/models/parsers/ConfigParserTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package seng302.models.parsers; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Created by Haoming on 23/03/17. - */ -public class ConfigParserTest { - - private ConfigParser cp; - - @Before - public void initializeParser() throws Exception { - cp = new ConfigParser("/config/config.xml"); - } - - @Test - public void getWindDirection() throws Exception { - assertEquals(135, cp.getWindDirection(), 1e-10); - } - - @Test - public void getTimeScale() throws Exception { - assertEquals(10.0, cp.getTimeScale(), 1e-10); - } - - @Test - public void getDoubleByTagName() throws Exception { - assertEquals(6, cp.getDoubleByTagName("race-size", 0), 1e-10); - assertEquals(100, cp.getDoubleByTagName("noTag", 100), 1e-10); - } - - @Test - public void getStringByTagName() throws Exception { - assertEquals("AC35", cp.getStringByTagName("race-name", "11")); - assertEquals("oops", cp.getStringByTagName("noTag", "oops")); - } - -} \ No newline at end of file diff --git a/src/test/java/seng302/models/parsers/CourseParserTest.java b/src/test/java/seng302/models/parsers/CourseParserTest.java deleted file mode 100644 index 48bf9d92..00000000 --- a/src/test/java/seng302/models/parsers/CourseParserTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package seng302.models.parsers; - -import org.junit.Before; -import org.junit.Test; -import seng302.models.mark.*; - -import java.util.ArrayList; - -import static org.junit.Assert.*; - -/** - * To test if course parser works as expected. - * Created by Haoming on 17/03/17. - */ -public class CourseParserTest { - - private CourseParser cp; - - @Before - public void initializeParser() throws Exception { - cp = new CourseParser("/config/course.xml"); - } - - @Test - public void getGates() throws Exception { - ArrayList course = cp.getCourse(); - - - GateMark gateMark1 = (GateMark) course.get(0); - assertEquals(57.670633, gateMark1.getSingleMark2().getLatitude(), 0.00000001); - assertEquals(11.8281330, gateMark1.getSingleMark2().getLongitude(), 0.00000001); - - GateMark gateMark2 = (GateMark) course.get(5); - - assertEquals("Finish1", gateMark2.getSingleMark1().getName()); - assertEquals("Finish2", gateMark2.getSingleMark2().getName()); - assertEquals(57.671824, gateMark2.getSingleMark2().getLatitude(), 0.00000001); - assertEquals(11.844795, gateMark2.getSingleMark2().getLongitude(), 0.00000001); - } - - @Test - public void getMarks() throws Exception { - ArrayList course = cp.getCourse(); - assertEquals("Mid Mark", course.get(1).getName()); - } - - @Test - public void getOrder() { - ArrayList course = cp.getCourse(); - assertEquals(6, course.size()); - assertEquals("Start", course.get(0).getName()); - assertEquals("Mid Mark", course.get(1).getName()); - assertEquals("Leeward Gate", course.get(2).getName()); - assertEquals("Windward Gate", course.get(3).getName()); - assertEquals("Leeward Gate", course.get(4).getName()); - assertEquals("Finish", course.get(5).getName()); - } - -} \ No newline at end of file diff --git a/src/test/java/seng302/models/parsers/StreamReceiverTest.java b/src/test/java/seng302/models/stream/StreamReceiverTest.java similarity index 98% rename from src/test/java/seng302/models/parsers/StreamReceiverTest.java rename to src/test/java/seng302/models/stream/StreamReceiverTest.java index 698ee3c0..4d0bea44 100644 --- a/src/test/java/seng302/models/parsers/StreamReceiverTest.java +++ b/src/test/java/seng302/models/stream/StreamReceiverTest.java @@ -1,4 +1,4 @@ -package seng302.models.parsers; +package seng302.models.stream; import org.junit.Before; import org.junit.Test; @@ -8,7 +8,7 @@ import java.lang.reflect.Method; import java.net.Socket; import java.util.Comparator; import java.util.concurrent.PriorityBlockingQueue; -import seng302.models.parsers.packets.StreamPacket; +import seng302.models.stream.packets.StreamPacket; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/src/test/java/seng302/models/parsers/TeamsParserTest.java b/src/test/java/seng302/models/stream/TeamsParserTest.java similarity index 100% rename from src/test/java/seng302/models/parsers/TeamsParserTest.java rename to src/test/java/seng302/models/stream/TeamsParserTest.java