diff --git a/src/main/java/seng302/model/stream/parser/YachtEventData.java b/src/main/java/seng302/model/stream/parser/YachtEventData.java new file mode 100644 index 00000000..635bd11f --- /dev/null +++ b/src/main/java/seng302/model/stream/parser/YachtEventData.java @@ -0,0 +1,34 @@ +package seng302.model.stream.parser; + +/** + * Stores parsed data from yacht event code packet + */ +public class YachtEventData { + private Long subjectId; + private Long incidentId; + private Integer eventId; + private Long timeStamp; + + public YachtEventData(Long subjectId, Long incidentId, Integer eventId, Long timeStamp) { + this.subjectId = subjectId; + this.incidentId = incidentId; + this.eventId = eventId; + this.timeStamp = timeStamp; + } + + public Long getSubjectId() { + return subjectId; + } + + public Long getIncidentId() { + return incidentId; + } + + public Integer getEventId() { + return eventId; + } + + public Long getTimeStamp() { + return timeStamp; + } +} diff --git a/src/main/java/seng302/utilities/StreamParser.java b/src/main/java/seng302/utilities/StreamParser.java index 0f4c48c0..304b105d 100644 --- a/src/main/java/seng302/utilities/StreamParser.java +++ b/src/main/java/seng302/utilities/StreamParser.java @@ -13,15 +13,12 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; import seng302.model.stream.packets.PacketType; import seng302.model.stream.packets.StreamPacket; -import seng302.model.stream.parser.MarkRoundingData; -import seng302.model.stream.parser.PositionUpdateData; +import seng302.model.stream.parser.*; import seng302.model.stream.parser.PositionUpdateData.DeviceType; -import seng302.model.stream.parser.RaceStartData; -import seng302.model.stream.parser.RaceStatusData; /** - * StreamParser is a utilities class for taking byte data, formatted according to the AC35 - * streaming protocol, and parsing it into basic data types or collections. + * StreamParser is a utilities class for taking byte data, formatted according to the AC35 streaming + * protocol, and parsing it into basic data types or collections. * * Created by kre39 on 23/04/17. */ @@ -34,8 +31,9 @@ public class StreamParser { * @return the packet sequence number if the packet is of type HEARTBEAT, null otherwise. */ public static Long extractHeartBeat(StreamPacket packet) { - if (packet.getType() != PacketType.HEARTBEAT) + if (packet.getType() != PacketType.HEARTBEAT) { return null; + } long heartbeat = bytesToLong(packet.getPayload()); System.out.println("heartbeat = " + heartbeat); return heartbeat; @@ -52,16 +50,17 @@ public class StreamParser { * containing the parsed packet data. */ public static RaceStatusData extractRaceStatus(StreamPacket packet) { - if (packet.getType() != PacketType.RACE_STATUS) + if (packet.getType() != PacketType.RACE_STATUS) { return null; + } byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; long currentTime = bytesToLong(Arrays.copyOfRange(payload, 1, 7)); long raceId = bytesToLong(Arrays.copyOfRange(payload, 7, 11)); int raceStatus = payload[11]; - long expectedStartTime = bytesToLong(Arrays.copyOfRange(payload,12,18)); - long windDir = bytesToLong(Arrays.copyOfRange(payload,18,20)); - long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload,20,22)); + long expectedStartTime = bytesToLong(Arrays.copyOfRange(payload, 12, 18)); + long windDir = bytesToLong(Arrays.copyOfRange(payload, 18, 20)); + long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload, 20, 22)); // DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); // currentTime = format.format((new Date(currentTime))) @@ -70,7 +69,6 @@ public class StreamParser { windDir, rawWindSpeed, raceStatus, currentTime, expectedStartTime ); - // long timeTillStart = // ((new Date(expectedStartTime)).getTime() - (new Date(currentTime)).getTime()) / 1000; // @@ -110,7 +108,7 @@ public class StreamParser { // boat.setEstimateTimeAtFinish(estTimeAtFinish); data.addBoatData(boatID, estTimeAtNextMark, estTimeAtFinish, leg, boatStatus); } - return data; + return data; } // private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){ @@ -139,8 +137,9 @@ public class StreamParser { * DISPLAY_TEXT_MESSAGE. */ public static List extractDisplayMessage(StreamPacket packet) { - if (packet.getType() != PacketType.DISPLAY_TEXT_MESSAGE) + if (packet.getType() != PacketType.DISPLAY_TEXT_MESSAGE) { return null; + } List message = new ArrayList<>(); byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; @@ -166,10 +165,11 @@ public class StreamParser { * XML_MESSAGE. */ public static Document extractXmlMessage(StreamPacket packet) { - if ( packet.getType() != PacketType.RACE_XML && - packet.getType() != PacketType.REGATTA_XML && - packet.getType() != PacketType.BOAT_XML ) + if (packet.getType() != PacketType.RACE_XML && + packet.getType() != PacketType.REGATTA_XML && + packet.getType() != PacketType.BOAT_XML) { return null; + } byte[] payload = packet.getPayload(); int messageType = payload[9]; @@ -194,8 +194,8 @@ public class StreamParser { * Extracts the race start status from the packet and returns it as a long array. * * @param packet Packet parsed in to use the payload - * @return An array of form [raceID, raceStartTime, notificationType, timeStamp] or null if - * the packet type is not of RACE_START_STATUS. + * @return An array of form [raceID, raceStartTime, notificationType, timeStamp] or null if the + * packet type is not of RACE_START_STATUS. */ public static RaceStartData extractRaceStartStatus(StreamPacket packet) { if (packet.getType() != PacketType.RACE_START_STATUS) { @@ -212,23 +212,25 @@ public class StreamParser { /** * Parses the the byte array in a StreamPacket for yacht events to retrieve the necessary info - * and returns it a an array of longs. + * and returns it as YachtEventData. * * @param packet Packet parsed in to use the payload - * @return the event data in the form [boatID, incidentID, eventID, timeStamp]. Returns null if - * the packet is not of type YACHT_EVENT_CODE. + * @return the event data in the form of YachtEventData. Returns null if the packet is not of + * type YACHT_EVENT_CODE. */ - public static long[] extractYachtEventCode(StreamPacket packet) { - if (packet.getType() != PacketType.YACHT_EVENT_CODE) + public static YachtEventData extractYachtEventCode(StreamPacket packet) { + if (packet.getType() != PacketType.YACHT_EVENT_CODE) { return null; + } byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7)); + long ackNumber = bytesToLong(Arrays.copyOfRange(payload, 7, 9)); long raceId = bytesToLong(Arrays.copyOfRange(payload, 9, 13)); long subjectId = bytesToLong(Arrays.copyOfRange(payload, 13, 17)); long incidentId = bytesToLong(Arrays.copyOfRange(payload, 17, 21)); int eventId = payload[21]; - return new long[] {subjectId, incidentId, eventId, timeStamp}; + return new YachtEventData(subjectId, incidentId, eventId, timeStamp); } /** @@ -239,15 +241,16 @@ public class StreamParser { * Returns null if the packet is not of type YACHT_ACTION_CODE. */ public static long[] extractYachtActionCode(StreamPacket packet) { - if (packet.getType() != PacketType.YACHT_ACTION_CODE) + if (packet.getType() != PacketType.YACHT_ACTION_CODE) { return null; + } byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7)); long subjectId = bytesToLong(Arrays.copyOfRange(payload, 9, 13)); long incidentId = bytesToLong(Arrays.copyOfRange(payload, 13, 17)); int eventId = payload[17]; - return new long[] {subjectId, incidentId, eventId, timeStamp}; + return new long[]{subjectId, incidentId, eventId, timeStamp}; } /** @@ -258,8 +261,9 @@ public class StreamParser { * CHATTER_TEXT. */ public static String extractChatterText(StreamPacket packet) { - if (packet.getType() != PacketType.CHATTER_TEXT) + if (packet.getType() != PacketType.CHATTER_TEXT) { return null; + } byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; int messageType = payload[1]; @@ -276,8 +280,9 @@ public class StreamParser { * is not of type BOAT_LOCATION. */ public static PositionUpdateData extractBoatLocation(StreamPacket packet) { - if (packet.getType() != PacketType.BOAT_LOCATION) + if (packet.getType() != PacketType.BOAT_LOCATION) { return null; + } byte[] payload = packet.getPayload(); int deviceType = (int) payload[15]; long timeValid = bytesToLong(Arrays.copyOfRange(payload, 1, 7)); @@ -293,10 +298,11 @@ public class StreamParser { double groundSpeed = bytesToLong(Arrays.copyOfRange(payload, 38, 40)) / 1000.0; DeviceType type; - if (deviceType == 1) + if (deviceType == 1) { type = DeviceType.YACHT_TYPE; - else + } else { type = DeviceType.MARK_TYPE; + } return new PositionUpdateData((int) boatId, type, lat, lon, heading, groundSpeed); } @@ -309,8 +315,9 @@ public class StreamParser { * if packet is not of type MARK_ROUNDING. */ public static MarkRoundingData extractMarkRounding(StreamPacket packet) { - if (packet.getType() != PacketType.MARK_ROUNDING) + if (packet.getType() != PacketType.MARK_ROUNDING) { return null; + } byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7)); @@ -325,16 +332,17 @@ public class StreamParser { } /** - * Returns a list containing the string value of data within the given stream packet for - * course wind. + * Returns a list containing the string value of data within the given stream packet for course + * wind. * * @param packet The packet containing the payload * @return the string values of the wind packet. Returns null if the packet is not of type * COURSE_WIND. */ public static List extractCourseWind(StreamPacket packet) { - if (packet.getType() != PacketType.COURSE_WIND) + if (packet.getType() != PacketType.COURSE_WIND) { return null; + } byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; int selectedWindId = payload[1]; @@ -366,13 +374,13 @@ public class StreamParser { * Returns the parsed data from a StreamPacket for average wind data. * * @param packet The packet containing the payload - * @return The wind data in the form - * [rawPeriod, rawSamplePeriod, period2, speed2, period3, speed3, period4, speed4, timestamp] - * or null if the packet is not of type AVG_WIND. + * @return The wind data in the form [rawPeriod, rawSamplePeriod, period2, speed2, period3, + * speed3, period4, speed4, timestamp] or null if the packet is not of type AVG_WIND. */ public static long[] extractAvgWind(StreamPacket packet) { - if (packet.getType() != PacketType.AVG_WIND) + if (packet.getType() != PacketType.AVG_WIND) { return null; + } byte[] payload = packet.getPayload(); int messageVersionNo = payload[0]; long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7)); @@ -384,7 +392,7 @@ public class StreamParser { long speed3 = bytesToLong(Arrays.copyOfRange(payload, 17, 19)); long period4 = bytesToLong(Arrays.copyOfRange(payload, 19, 21)); long speed4 = bytesToLong(Arrays.copyOfRange(payload, 21, 23)); - return new long[] { + return new long[]{ rawPeriod, rawSamplePeriod, period2, speed2, period3, speed3, period4, speed4, timeStamp }; } @@ -410,8 +418,7 @@ public class StreamParser { } /** - * takes an array of up to 7 bytes and returns a positive - * long constructed from the input bytes + * takes an array of up to 7 bytes and returns a positive long constructed from the input bytes * * @param bytes the byte array to conver to Long * @return a positive long if there is less than 7 bytes -1 otherwise diff --git a/src/main/java/seng302/visualiser/GameClient.java b/src/main/java/seng302/visualiser/GameClient.java index 6278e06f..c5dd1fc9 100644 --- a/src/main/java/seng302/visualiser/GameClient.java +++ b/src/main/java/seng302/visualiser/GameClient.java @@ -20,6 +20,7 @@ import seng302.model.stream.parser.MarkRoundingData; import seng302.model.stream.parser.PositionUpdateData; import seng302.model.stream.parser.PositionUpdateData.DeviceType; import seng302.model.stream.parser.RaceStatusData; +import seng302.model.stream.parser.YachtEventData; import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RegattaXMLData; import seng302.gameServer.server.messages.BoatActionMessage; @@ -117,7 +118,8 @@ public class GameClient { * @return the lobby controller. */ private LobbyController loadLobby() { - FXMLLoader fxmlLoader = new FXMLLoader(GameClient.class.getResource("/views/LobbyView.fxml")); + FXMLLoader fxmlLoader = new FXMLLoader( + GameClient.class.getResource("/views/LobbyView.fxml")); try { holderPane.getChildren().clear(); holderPane.getChildren().add(fxmlLoader.load()); @@ -200,13 +202,18 @@ public class GameClient { case MARK_ROUNDING: updateMarkRounding(StreamParser.extractMarkRounding(packet)); break; + + case YACHT_EVENT_CODE: + showCollisionAlert(StreamParser.extractYachtEventCode(packet)); + break; } } } private void startRaceIfAllDataReceived() { - if (allXMLReceived() && raceView == null) + if (allXMLReceived() && raceView == null) { loadRaceView(); + } } private boolean allXMLReceived() { @@ -263,8 +270,9 @@ public class GameClient { int placing = 1; for (Yacht otherYacht : allBoatsMap.values()) { if (otherYacht.getSourceId() != boatData[0] && - yacht.getLegNumber() <= otherYacht.getLegNumber()) + yacht.getLegNumber() <= otherYacht.getLegNumber()) { placing++; + } } yacht.setPositionInteger(placing); } @@ -279,6 +287,7 @@ public class GameClient { /** * Handle the key-pressed event from the text field. + * * @param e The key event triggering this call */ public void keyPressed(KeyEvent e) { @@ -328,4 +337,14 @@ public class GameClient { public RaceXMLData getCourseData() { return courseData; } + + /** + * Tells race view to show a collision animation. + */ + private void showCollisionAlert(YachtEventData yachtEventData) { + // 1 is used by team 28 to show collision + if (yachtEventData.getEventId() == 1) { + raceView.showCollision(yachtEventData.getSubjectId()); + } + } } diff --git a/src/main/java/seng302/visualiser/GameView.java b/src/main/java/seng302/visualiser/GameView.java index 73d49b89..f13fa27c 100644 --- a/src/main/java/seng302/visualiser/GameView.java +++ b/src/main/java/seng302/visualiser/GameView.java @@ -6,9 +6,14 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import javafx.animation.Animation; import javafx.animation.AnimationTimer; +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; import javafx.application.Platform; import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; import javafx.geometry.Point2D; import javafx.scene.Group; import javafx.scene.Node; @@ -17,8 +22,10 @@ import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.paint.Paint; +import javafx.scene.shape.Circle; import javafx.scene.shape.Polygon; import javafx.scene.text.Text; +import javafx.util.Duration; import seng302.model.Colors; import seng302.model.GeoPoint; import seng302.model.Limit; @@ -40,10 +47,10 @@ import seng302.visualiser.map.CanvasMap; */ public class GameView extends Pane { - private double bufferSize = 50; - private double panelWidth = 1260; // it should be 1280 but, minors 40 to cancel the bias. - private double panelHeight = 960; - private double canvasWidth = 1100; + private double bufferSize = 50; + private double panelWidth = 1260; // it should be 1280 but, minors 40 to cancel the bias. + private double panelHeight = 960; + private double canvasWidth = 1100; private double canvasHeight = 920; private boolean horizontalInversion = false; @@ -86,7 +93,7 @@ public class GameView extends Pane { VERTICAL } - public GameView () { + public GameView() { gameObjects = this.getChildren(); // create image view for map, bind panel size to image gameObjects.add(mapImage); @@ -99,7 +106,7 @@ public class GameView extends Pane { initializeTimer(); } - private void initializeTimer () { + private void initializeTimer() { Arrays.fill(frameTimes, 1_000_000_000 / 60); timer = new AnimationTimer() { private long lastTime = 0; @@ -114,7 +121,8 @@ public class GameView extends Pane { if (lastTime == 0) { lastTime = now; } else { - if (now - lastTime >= (1e8 / 60)) { //Fix for framerate going above 60 when minimized + if (now - lastTime >= (1e8 + / 60)) { //Fix for framerate going above 60 when minimized long oldFrameTime = frameTimes[frameTimeIndex]; frameTimes[frameTimeIndex] = now; frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length; @@ -142,8 +150,8 @@ public class GameView extends Pane { } /** - * First find the top right and bottom left points' geo locations, then retrieve - * map from google to display on image view. - Haoming 22/5/2017 + * First find the top right and bottom left points' geo locations, then retrieve map from google + * to display on image view. - Haoming 22/5/2017 */ private void drawGoogleMap() { findMetersPerPixel(); @@ -207,7 +215,7 @@ public class GameView extends Pane { gates.add( makeAndBindGate( markerObjects.get(cMark.getSubMark(i)), - markerObjects.get(cMark.getSubMark(i+1)), + markerObjects.get(cMark.getSubMark(i + 1)), colour ) ); @@ -270,7 +278,7 @@ public class GameView extends Pane { gate.endYProperty().bind( m2.centerYProperty() ); - return gate; + return gate; } /** @@ -308,7 +316,8 @@ public class GameView extends Pane { /** * Draws all the boats. - * @param yachts The yachts to set in the race + * + * @param yachts The yachts to set in the race */ public void setBoats(List yachts) { BoatObject newBoat; @@ -324,7 +333,7 @@ public class GameView extends Pane { boatObjectGroup.getChildren().add(newBoat); trails.getChildren().add(newBoat.getTrail()); // TODO: 1/08/17 Make this less vile to look at. - yacht.addLocationListener((boat, lat, lon, heading, velocity) ->{ + yacht.addLocationListener((boat, lat, lon, heading, velocity) -> { BoatObject bo = boatObjects.get(boat); Point2D p2d = findScaledXY(lat, lon); bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity); @@ -348,7 +357,7 @@ public class GameView extends Pane { }); } - private void createAndBindAnnotationBox (Yacht yacht, Paint colour) { + private void createAndBindAnnotationBox(Yacht yacht, Paint colour) { AnnotationBox newAnnotation = new AnnotationBox(); newAnnotation.setFill(colour); newAnnotation.addAnnotation( @@ -378,25 +387,25 @@ public class GameView extends Pane { annotations.put(yacht, newAnnotation); } - private void drawFps(Double fps){ + private void drawFps(Double fps) { Platform.runLater(() -> fpsDisplay.setText(String.format("%d FPS", Math.round(fps)))); } /** - * Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the point - * with the leftmost point, rightmost point, southern most point and northern most point + * Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the point with + * the leftmost point, rightmost point, southern most point and northern most point * respectively. */ private void findMinMaxPoint(List points) { List sortedPoints = new ArrayList<>(points); sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLat)); minLatPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng()); - GeoPoint maxLat = sortedPoints.get(sortedPoints.size()-1); + GeoPoint maxLat = sortedPoints.get(sortedPoints.size() - 1); maxLatPoint = new GeoPoint(maxLat.getLat(), maxLat.getLng()); sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLng)); minLonPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng()); - GeoPoint maxLon = sortedPoints.get(sortedPoints.size()-1); + GeoPoint maxLon = sortedPoints.get(sortedPoints.size() - 1); maxLonPoint = new GeoPoint(maxLon.getLat(), maxLon.getLng()); if (maxLonPoint.getLng() - minLonPoint.getLng() > 180) { horizontalInversion = true; @@ -416,15 +425,19 @@ public class GameView extends Pane { if (scaleDirection == ScaleDirection.HORIZONTAL) { referenceAngle = Math.abs( - GeoUtility.getBearingRad(referencePoint, minLonPoint) + GeoUtility.getBearingRad(referencePoint, minLonPoint) ); - referencePointX = bufferSize + distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility.getDistance(referencePoint, minLonPoint); + referencePointX = + bufferSize + distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility + .getDistance(referencePoint, minLonPoint); referenceAngle = Math.abs(GeoUtility.getDistance(referencePoint, maxLatPoint)); - referencePointY = canvasHeight - (bufferSize + bufferSize); - referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility.getDistance(referencePoint, maxLatPoint); - referencePointY = referencePointY / 2; + referencePointY = canvasHeight - (bufferSize + bufferSize); + referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility + .getDistance(referencePoint, maxLatPoint); + referencePointY = referencePointY / 2; referencePointY += bufferSize; - referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility.getDistance(referencePoint, maxLatPoint); + referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility + .getDistance(referencePoint, maxLatPoint); } else { referencePointY = canvasHeight - bufferSize; referenceAngle = Math.abs( @@ -432,11 +445,14 @@ public class GameView extends Pane { GeoUtility.getDistance(referencePoint, minLonPoint) ) ); - referencePointX = bufferSize; - referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility.getDistance(referencePoint, minLonPoint); - referencePointX += ((canvasWidth - (bufferSize + bufferSize)) - (minLonToMaxLon * distanceScaleFactor)) / 2; + referencePointX = bufferSize; + referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility + .getDistance(referencePoint, minLonPoint); + referencePointX += + ((canvasWidth - (bufferSize + bufferSize)) - (minLonToMaxLon * distanceScaleFactor)) + / 2; } - if(horizontalInversion) { + if (horizontalInversion) { referencePointX = canvasWidth - bufferSize - (referencePointX - bufferSize); } } @@ -449,12 +465,12 @@ public class GameView extends Pane { private double scaleRaceExtremities() { double vertAngle = Math.abs( - GeoUtility.getBearingRad(minLatPoint, maxLatPoint) + GeoUtility.getBearingRad(minLatPoint, maxLatPoint) ); double vertDistance = Math.cos(vertAngle) * GeoUtility.getDistance(minLatPoint, maxLatPoint); double horiAngle = Math.abs( - GeoUtility.getBearingRad(minLonPoint, maxLonPoint) + GeoUtility.getBearingRad(minLonPoint, maxLonPoint) ); if (horiAngle <= (Math.PI / 2)) { horiAngle = (Math.PI / 2) - horiAngle; @@ -480,36 +496,44 @@ public class GameView extends Pane { return findScaledXY(unscaled.getLat(), unscaled.getLng()); } - private Point2D findScaledXY (double unscaledLat, double unscaledLon) { + private Point2D findScaledXY(double unscaledLat, double unscaledLon) { double distanceFromReference; double angleFromReference; double xAxisLocation = referencePointX; double yAxisLocation = referencePointY; angleFromReference = GeoUtility.getBearingRad( - minLatPoint, new GeoPoint(unscaledLat, unscaledLon) + minLatPoint, new GeoPoint(unscaledLat, unscaledLon) ); distanceFromReference = GeoUtility.getDistance( minLatPoint, new GeoPoint(unscaledLat, unscaledLon) ); // System.out.println("distanceFromReference = " + distanceFromReference); if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) { - xAxisLocation += Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); - yAxisLocation -= Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); + xAxisLocation += Math + .round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); + yAxisLocation -= Math + .round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); } else if (angleFromReference >= 0) { angleFromReference = angleFromReference - Math.PI / 2; - xAxisLocation += Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); - yAxisLocation += Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); + xAxisLocation += Math + .round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); + yAxisLocation += Math + .round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); } else if (angleFromReference < 0 && angleFromReference >= -Math.PI / 2) { angleFromReference = Math.abs(angleFromReference); - xAxisLocation -= Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); - yAxisLocation -= Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); + xAxisLocation -= Math + .round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); + yAxisLocation -= Math + .round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); } else { angleFromReference = Math.abs(angleFromReference) - Math.PI / 2; - xAxisLocation -= Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); - yAxisLocation += Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); + xAxisLocation -= Math + .round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); + yAxisLocation += Math + .round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); } - if(horizontalInversion) { + if (horizontalInversion) { xAxisLocation = canvasWidth - bufferSize - (xAxisLocation - bufferSize); } // System.out.println("yAxisLocation = " + yAxisLocation + " " + unscaledLat); @@ -538,7 +562,7 @@ public class GameView extends Pane { metersPerPixelY = dVertical / dy; } - public void setAnnotationVisibilities (boolean teamName, boolean velocity, boolean estTime, + public void setAnnotationVisibilities(boolean teamName, boolean velocity, boolean estTime, boolean legTime, boolean trail, boolean wake) { for (BoatObject boatObject : boatObjects.values()) { boatObject.setVisibility(teamName, velocity, estTime, legTime, trail, wake); @@ -551,25 +575,25 @@ public class GameView extends Pane { } } - public void setFPSVisibility (boolean visibility) { + public void setFPSVisibility(boolean visibility) { fpsDisplay.setVisible(visibility); } - public void selectBoat (Yacht selectedYacht) { + public void selectBoat(Yacht selectedYacht) { boatObjects.forEach((boat, group) -> group.setIsSelected(boat == selectedYacht) ); } - public void pauseRace () { + public void pauseRace() { timer.stop(); } - public void startRace () { + public void startRace() { timer.start(); } - public void setBoatAsPlayer (Yacht playerYacht) { + public void setBoatAsPlayer(Yacht playerYacht) { boatObjects.get(playerYacht).setAsPlayer(); annotations.get(playerYacht).addAnnotation( "velocity", @@ -583,4 +607,35 @@ public class GameView extends Pane { gameObjects.add(annotations.get(playerYacht)); }); } + + /** + * Given yacht geopoint by race view controller, drawCollision will calculate canvas X and Y and + * display a flashing red circle on collision point. + * + * @param collisionPoint yacht collision point + */ + public void drawCollision(GeoPoint collisionPoint) { + System.out.println("ran"); + Point2D point = findScaledXY(collisionPoint); + Circle circle = new Circle(point.getX(), point.getY(), 10.0, Color.RED); + gameObjects.add(circle); + + Timeline timeline = new Timeline(); + timeline.setCycleCount(1); + EventHandler blink = (ActionEvent event) -> { + if (circle.getFill() == Color.RED) { + circle.setFill(Color.TRANSPARENT); + } else { + circle.setFill(Color.RED); + } + System.out.println("beep boop"); + }; + + KeyFrame keyframe = new KeyFrame(Duration.millis(200), blink); + + timeline.getKeyFrames().add(keyframe); + timeline.play(); + +// gameObjects.remove(circle); + } } diff --git a/src/main/java/seng302/visualiser/controllers/RaceViewController.java b/src/main/java/seng302/visualiser/controllers/RaceViewController.java index a6df4f61..d6bdf65c 100644 --- a/src/main/java/seng302/visualiser/controllers/RaceViewController.java +++ b/src/main/java/seng302/visualiser/controllers/RaceViewController.java @@ -100,7 +100,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel selectAnnotationBtn.setOnAction(event -> loadSelectAnnotationView()); } - public void loadRace ( + public void loadRace( Map participants, RaceXMLData raceData, RaceState raceState, Yacht player ) { this.participants = participants; @@ -208,9 +208,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel /** - * Used to add any new yachts into the race that may have started late or not have had data received yet + * Used to add any new yachts into the race that may have started late or not have had data + * received yet */ - private void updateSparkLine(){ + private void updateSparkLine() { // TODO: 2/08/17 there is about 0 chance of this working. Once we are keeping track of boat positions it can be fixed. // Collect the racing yachts that aren't already in the chart sparkLineData.clear(); @@ -228,14 +229,14 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel 1.0 + participants.size() - yacht.getPositionInteger() ) ); - sparkLineData.add(yachtData); + sparkLineData.add(yachtData); }); // Lambda function to sort the series in order of leg (later legs shown more to the right) sparkLineData.sort((o1, o2) -> { - Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size()-1).getXValue()); - Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size()-1).getXValue()); - if (leg2 < leg1){ + Integer leg1 = Integer.parseInt(o1.getData().get(o1.getData().size() - 1).getXValue()); + Integer leg2 = Integer.parseInt(o2.getData().get(o2.getData().size() - 1).getXValue()); + if (leg2 < leg1) { return 1; } else { return -1; @@ -248,7 +249,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel .filter(spark -> !raceSparkLine.getData().contains(spark)) .forEach(spark -> { raceSparkLine.getData().add(spark); - spark.getNode().lookup(".chart-series-line").setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName())); + spark.getNode().lookup(".chart-series-line") + .setStyle("-fx-stroke:" + getBoatColorAsRGB(spark.getName())); }); }); } @@ -260,10 +262,11 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel /** * Updates the yachts sparkline of the desired yacht and using the new leg number + * * @param yacht The yacht to be updated on the sparkline * @param legNumber the leg number that the position will be assigned to */ - void updateYachtPositionSparkline(Yacht yacht, Integer legNumber){ + void updateYachtPositionSparkline(Yacht yacht, Integer legNumber) { for (XYChart.Series positionData : sparkLineData) { positionData.getData().add( new Data<>( @@ -284,26 +287,27 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel /** * gets the rgb string of the yachts colour to use for the chart via css + * * @param yachtId id of yacht passed in to get the yachts colour * @return the colour as an rgb string */ - private String getBoatColorAsRGB(String yachtId){ + private String getBoatColorAsRGB(String yachtId) { Color color = participants.get(Integer.valueOf(yachtId)).getColour(); - if (color == null){ - return String.format("#%02X%02X%02X",255,255,255); + if (color == null) { + return String.format("#%02X%02X%02X", 255, 255, 255); } - return String.format( "#%02X%02X%02X", - (int)( color.getRed() * 255 ), - (int)( color.getGreen() * 255 ), - (int)( color.getBlue() * 255 ) + return String.format("#%02X%02X%02X", + (int) (color.getRed() * 255), + (int) (color.getGreen() * 255), + (int) (color.getBlue() * 255) ); } /** * Initialises a timer which updates elements of the RaceView such as wind direction, yacht - * orderings etc.. which are dependent on the info from the stream parser constantly. - * Updates of each of these attributes are called ONCE EACH SECOND + * orderings etc.. which are dependent on the info from the stream parser constantly. Updates of + * each of these attributes are called ONCE EACH SECOND */ private void initializeUpdateTimer() { timer.scheduleAtFixedRate(new TimerTask() { @@ -318,9 +322,10 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } /** - * Iterates over all corners until ones SeqID matches with the yachts current leg number. - * Then it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark - * Returns null if no next mark found. + * Iterates over all corners until ones SeqID matches with the yachts current leg number. Then + * it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark Returns + * null if no next mark found. + * * @param bg The BoatGroup to find the next mark of * @return The next Mark or null if none found */ @@ -475,15 +480,17 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } - private Point2D getPointRotation(Point2D ref, Double distance, Double angle){ - Double newX = ref.getX() + (ref.getX() + distance -ref.getX())*Math.cos(angle) - (ref.getY() + distance -ref.getY())*Math.sin(angle); - Double newY = ref.getY() + (ref.getX() + distance -ref.getX())*Math.sin(angle) + (ref.getY() + distance -ref.getY())*Math.cos(angle); + private Point2D getPointRotation(Point2D ref, Double distance, Double angle) { + Double newX = ref.getX() + (ref.getX() + distance - ref.getX()) * Math.cos(angle) + - (ref.getY() + distance - ref.getY()) * Math.sin(angle); + Double newY = ref.getY() + (ref.getX() + distance - ref.getX()) * Math.sin(angle) + + (ref.getY() + distance - ref.getY()) * Math.cos(angle); return new Point2D(newX, newY); } - public Line makeLeftLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) { + public Line makeLeftLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) { Point2D ep = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle); Line line = new Line(startPoint.getX(), startPoint.getY(), ep.getX(), ep.getY()); @@ -502,8 +509,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel /** - * Initialised the combo box with any yachts currently in the race and adds the required listener - * for the combobox to take action upon selection + * Initialised the combo box with any yachts currently in the race and adds the required + * listener for the combobox to take action upon selection */ private void initialiseBoatSelectionComboBox() { yachtSelectionComboBox.setItems( @@ -540,7 +547,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel TimeUnit.MILLISECONDS.toHours(milliseconds), TimeUnit.MILLISECONDS.toMinutes(milliseconds) % 60, //Modulus 60 minutes per hour TimeUnit.MILLISECONDS.toSeconds(milliseconds) % 60 //Modulus 60 seconds per minute - ); + ); } private void setAnnotations(Integer annotationLevel) { @@ -591,8 +598,21 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel // } } - public void updateRaceData (RaceXMLData raceData) { + public void updateRaceData(RaceXMLData raceData) { this.courseData = raceData; gameView.updateBorder(raceData.getCourseLimit()); } + + /** + * Called by game client after receiving yacht event packet. Parameter subject id is the + * offending yacht. This function in turn will pass the yacht location to game view to display a + * collision alert. + * + * @param subjectId source id of offending yacht + */ + public void showCollision(Long subjectId) { + System.out.println("showcollision " + subjectId); + System.out.println(participants.get((int) (long) subjectId).getLocation()); + gameView.drawCollision(participants.get((int) (long) subjectId).getLocation()); + } } \ No newline at end of file