From bd31bae58630e381c95c79890b30f38746f4ae2d Mon Sep 17 00:00:00 2001 From: William Muir Date: Tue, 12 Sep 2017 16:08:29 +1200 Subject: [PATCH] Changed the lobby map view to the 2D race rather than the 3D one. Stripped back game view class appropriately. #story[1245] --- .../java/seng302/visualiser/GameView.java | 538 +----------------- .../fxObjects/assets_2D/Marker2D.java | 100 ++++ 2 files changed, 108 insertions(+), 530 deletions(-) create mode 100644 src/main/java/seng302/visualiser/fxObjects/assets_2D/Marker2D.java diff --git a/src/main/java/seng302/visualiser/GameView.java b/src/main/java/seng302/visualiser/GameView.java index a866976d..a7e68ce3 100644 --- a/src/main/java/seng302/visualiser/GameView.java +++ b/src/main/java/seng302/visualiser/GameView.java @@ -1,38 +1,22 @@ package seng302.visualiser; -import javafx.animation.AnimationTimer; -import javafx.animation.KeyFrame; -import javafx.animation.KeyValue; -import javafx.animation.Timeline; import javafx.application.Platform; import javafx.collections.ObservableList; import javafx.geometry.Point2D; import javafx.scene.*; import javafx.scene.image.ImageView; -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.gameServer.messages.RoundingSide; -import seng302.model.ClientYacht; import seng302.model.GeoPoint; import seng302.model.Limit; import seng302.model.mark.CompoundMark; import seng302.model.mark.Corner; import seng302.model.mark.Mark; -import seng302.model.token.Token; import seng302.utilities.GeoUtility; -import seng302.utilities.Sounds; import seng302.visualiser.fxObjects.assets_2D.*; -import seng302.visualiser.fxObjects.assets_3D.Marker3D; -import seng302.visualiser.fxObjects.assets_3D.ModelFactory; -import seng302.visualiser.fxObjects.assets_3D.ModelType; -import seng302.visualiser.map.Boundary; -import seng302.visualiser.map.CanvasMap; import java.util.*; @@ -43,8 +27,6 @@ public class GameView extends Pane { private double bufferSize = 50; private double horizontalBuffer = 0; - private double panelWidth = 1280; - private double panelHeight = 960; private double canvasWidth = 1100; private double canvasHeight = 920; @@ -54,233 +36,31 @@ public class GameView extends Pane { private ScaleDirection scaleDirection; private GeoPoint minLatPoint, minLonPoint, maxLatPoint, maxLonPoint; private double referencePointX, referencePointY; - private double metersPerPixelX, metersPerPixelY; - private boolean isZoom = false; - - private Text fpsDisplay = new Text(); private Polygon raceBorder = new CourseBoundary(); /* Note that if either of these is null then values for it have not been added and the other should be used as the limits of the map. */ private List borderPoints; - private Map markerObjects; + private Map markerObjects; - private Map boatObjects = new HashMap<>(); - private Map annotations = new HashMap<>(); private ObservableList gameObjects; - private BoatObject selectedBoat = null; - private Group annotationsGroup = new Group(); - private Group wakesGroup = new Group(); - private Group boatObjectGroup = new Group(); - private Group trails = new Group(); private Group markers = new Group(); private Group tokens = new Group(); private List course = new ArrayList<>(); - private List mapTokens; private ImageView mapImage = new ImageView(); - private Camera camera; - - //FRAME RATE - - private AnimationTimer timer; - private int NUM_SAMPLES = 10; - private final long[] frameTimes = new long[NUM_SAMPLES]; - private Double frameRate = 60.0; - private int frameTimeIndex = 0; - private boolean arrayFilled = false; - private ClientYacht playerYacht; - private double windDir = 0.0; - - double scaleFactor = 1; - - public void setRes(Integer x, Integer y){ - this.panelHeight = y; - this.panelWidth = x; - } - - private void zoomOut() { - scaleFactor = 0.1; - if (this.getScaleX() > 0.5) { - this.setScaleX(this.getScaleX() - scaleFactor); - this.setScaleY(this.getScaleY() - scaleFactor); - } - } - - private void zoomIn() { - scaleFactor = 0.10; - if (this.getScaleX() < 2.5) { - this.setScaleX(this.getScaleX() + scaleFactor); - this.setScaleY(this.getScaleY() + scaleFactor); - } - } private enum ScaleDirection { HORIZONTAL, VERTICAL } - - private void trackBoat() { - if (selectedBoat != null) { - double x = selectedBoat.getBoatLayoutX(); - double y = selectedBoat.getBoatLayoutY(); - double displacementX = this.getWidth(); - double displacementY = this.getHeight(); - this.setLayoutX((-x + (displacementX / 2.0)) * this.getScaleX()); - this.setLayoutY((-y + (displacementY / 2.0)) * this.getScaleY()); - } else { - this.setLayoutX(0); - this.setLayoutY(0); - } - } - public GameView () { gameObjects = this.getChildren(); -// AmbientLight ambientLight = new AmbientLight(new Color(1,1,1,0.4)); -// ambientLight.setOpacity(0.5); -// gameObjects.add(ambientLight); - // create image view for map, bind panel size to image - camera = new ParallelCamera(); - camera.setTranslateZ(-500); - camera.setFarClip(Double.MAX_VALUE); - camera.setNearClip(0.1); - PointLight pl = new PointLight(); - pl.setLightOn(true); - pl.layoutYProperty().bind(camera.layoutYProperty()); - pl.layoutXProperty().bind(camera.layoutXProperty()); -// gameObjects.add(camera); - this.sceneProperty().addListener((obs, oldValue, scene) -> { - if (scene != null) { - scene.setCamera(camera); - } - }); - initializeTimer(); - gameObjects.addAll(mapImage, raceBorder, markers, tokens, pl); - // TODO: 11/09/17 ajm412: do you even zoom bro? -// this.sceneProperty().addListener(((observable, oldValue, scene) -> { -// if (scene != null) { -// setupZoom(); -// } else { -// disableZoom(); -// } -// })); -// -// this.widthProperty().addListener(new ChangeListener() { -// @Override -// public void changed(ObservableValue observable, Number oldValue, -// Number newValue) { -// scaleFactor = getWidth() / panelWidth; -// -// if (panelHeight * scaleFactor < getHeight()) { -// Scale scale = new Scale(scaleFactor, scaleFactor, 0, 0); -// getTransforms().remove(0, getTransforms().size()); -// getTransforms().add(scale); -// -// setPrefWidth(getWidth() / scaleFactor); -// setPrefHeight(getHeight() / scaleFactor); -// } -// } -// }); -// -// this.heightProperty().addListener(new ChangeListener() { -// @Override -// public void changed(ObservableValue observable, Number oldValue, -// Number newValue) { -// scaleFactor = getHeight() / panelHeight; -// -// if (panelWidth * scaleFactor < getWidth()) { -// Scale scale = new Scale(scaleFactor, scaleFactor, 0, 0); -// getTransforms().remove(0, getTransforms().size()); -// getTransforms().add(scale); -// -// setPrefWidth(getWidth() / scaleFactor); -// setPrefHeight(getHeight() / scaleFactor); -// } -// } -// }); + gameObjects.addAll(mapImage, raceBorder, markers, tokens); } - private void initializeTimer() { - Arrays.fill(frameTimes, 1_000_000_000 / 60); - timer = new AnimationTimer() { - private long lastTime = 0; - private int FPSCount = 30; - private Double frameRate = 60.0; - private int index = 0; - private boolean arrayFilled = false; - private long sum = 1_000_000_000 / 3; - - @Override - public void handle(long now) { - trackBoat(); - if (lastTime == 0) { - lastTime = now; - } else { - 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; - if (frameTimeIndex == 0) { - arrayFilled = true; - } - long elapsedNanos; - if (arrayFilled) { - elapsedNanos = now - oldFrameTime; - long elapsedNanosPerFrame = elapsedNanos / frameTimes.length; - frameRate = 1_000_000_000.0 / elapsedNanosPerFrame; - if (FPSCount-- == 0) { - FPSCount = 30; - drawFps(frameRate); - } - } - lastTime = now; - } - } -// boatObjects.forEach((boat, boatObject) -> boatObject.updateLocation()); - } - }; - } - - /** - * 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(); - Point2D topLeftPoint = findScaledXY(maxLatPoint.getLat(), minLonPoint.getLng()); - // distance from top left extreme to panel origin (top left corner) - double distanceFromTopLeftToOrigin = Math.sqrt( - Math.pow(topLeftPoint.getX() * metersPerPixelX, 2) + Math - .pow(topLeftPoint.getY() * metersPerPixelY, 2)); - // angle from top left extreme to panel origin - double bearingFromTopLeftToOrigin = Math - .toDegrees(Math.atan2(-topLeftPoint.getX(), topLeftPoint.getY())); - // the top left extreme - GeoPoint topLeftPos = new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng()); - GeoPoint originPos = GeoUtility - .getGeoCoordinate(topLeftPos, bearingFromTopLeftToOrigin, distanceFromTopLeftToOrigin); - - // distance from origin corner to bottom right corner of the panel - double distanceFromOriginToBottomRight = Math.sqrt( - Math.pow(panelHeight * metersPerPixelY, 2) + Math - .pow(panelWidth * metersPerPixelX, 2)); - double bearingFromOriginToBottomRight = Math - .toDegrees(Math.atan2(panelWidth, -panelHeight)); - GeoPoint bottomRightPos = GeoUtility - .getGeoCoordinate(originPos, bearingFromOriginToBottomRight, - distanceFromOriginToBottomRight); - - Boundary boundary = new Boundary(originPos.getLat(), bottomRightPos.getLng(), - bottomRightPos.getLat(), originPos.getLng()); - CanvasMap canvasMap = new CanvasMap(boundary); - mapImage.setImage(canvasMap.getMapImage()); - mapImage.fitWidthProperty().bind(((AnchorPane) this.getParent()).heightProperty()); - mapImage.fitHeightProperty().bind(((AnchorPane) this.getParent()).heightProperty()); - } - - // TODO: 16/08/17 Break up this function /** * Adds a course to the GameView. The view is scaled accordingly unless a border is set in which * case the course is added relative ot the border. @@ -343,10 +123,10 @@ public class GameView extends Pane { rescaleRace(new ArrayList<>(markerObjects.keySet())); } //Move the Markers to initial position. - markerObjects.forEach(((mark, marker3D) -> { + markerObjects.forEach(((mark, marker2D) -> { Point2D p2d = findScaledXY(mark.getLat(), mark.getLng()); - marker3D.setLayoutX(p2d.getX()); - marker3D.setLayoutY(p2d.getY()); + marker2D.setLayoutX(p2d.getX()); + marker2D.setLayoutY(p2d.getY()); })); Platform.runLater(() -> { markers.getChildren().clear(); @@ -436,9 +216,9 @@ public class GameView extends Pane { * @param colour The desired colour of the mark */ private void makeAndBindMarker(Mark observableMark, Paint colour) { - Marker3D marker3D = new Marker3D(colour); + Marker2D marker2D = new Marker2D(colour); // marker.addArrows(MarkArrowFactory.RoundingSide.PORT, ThreadLocalRandom.current().nextDouble(91, 180), ThreadLocalRandom.current().nextDouble(1, 90)); - markerObjects.put(observableMark, marker3D); + markerObjects.put(observableMark, marker2D); observableMark.addPositionListener((mark, lat, lon) -> { Point2D p2d = findScaledXY(lat, lon); markerObjects.get(mark).setLayoutX(p2d.getX()); @@ -454,7 +234,7 @@ public class GameView extends Pane { * @param colour The desired colour of the gate. * @return the new gate. */ - private Gate makeAndBindGate(Marker3D m1, Marker3D m2, Paint colour) { + private Gate makeAndBindGate(Marker2D m1, Marker2D m2, Paint colour) { Gate gate = new Gate(colour); gate.startXProperty().bind( m1.layoutXProperty() @@ -494,62 +274,6 @@ public class GameView extends Pane { raceBorder.getPoints().setAll(boundaryPoints); } -// /** -// * Rescales the race to the size of the window. -// * -// * @param limitingCoordinates the set of geo points that contains the extremities of the race. -// */ -// private void rescaleRace(List limitingCoordinates) { -// //Check is called once to avoid unnecessarily change the course limits once the race is running -// findMinMaxPoint(limitingCoordinates); -// double minLonToMaxLon = scaleRaceExtremities(); -// calculateReferencePointLocation(minLonToMaxLon); -//// drawGoogleMap(); -// } - - /** - * Replaces all tokens in the course with those passed in - * - * @param newTokens the tokens to be put on the course. - */ - public void updateTokens(List newTokens) { - mapTokens = new ArrayList<>(); - for (Token token : newTokens) { - Point2D location = findScaledXY(token.getLat(), token.getLng()); - Node tokenObject = ModelFactory.importModel(ModelType.VELOCITY_PICKUP).getAssets(); - tokenObject.setLayoutX(location.getX()); - tokenObject.setLayoutY(location.getY()); - mapTokens.add(tokenObject); - } - Platform.runLater(() -> { - tokens.getChildren().clear(); - tokens.getChildren().addAll(mapTokens); - }); - } - -// // TODO: 16/08/17 initialize zooming internal to GameView only -// /** -// * Enables zoom. Has to be called after this is added to a scene. -// */ -// private void setupZoom() { -// this.getScene().addEventHandler(KeyEvent.KEY_PRESSED, (event) -> { -// if (event.getCode() == KeyCode.Z) { -// zoomIn(); -// } else if (event.getCode() == KeyCode.X) { -// zoomOut(); -// } -// }); -// enableZoom(); -// } -//// -// public void enableZoom() { -// isZoom = true; -// } -// -// public void disableZoom() { -// isZoom = false; -// } - /** * Rescales the race to the size of the window. * @@ -560,97 +284,6 @@ public class GameView extends Pane { findMinMaxPoint(limitingCoordinates); double minLonToMaxLon = scaleRaceExtremities(); calculateReferencePointLocation(minLonToMaxLon); -// drawGoogleMap(); - } - - private void setSelectedBoat(BoatObject bo, Boolean isSelected) { - if (this.selectedBoat == bo && !isSelected) { - this.selectedBoat = null; - boatObjects.forEach((boat, group) -> - group.setIsSelected(false) - ); - } else if (isSelected) { - this.selectedBoat = bo; - for (BoatObject group : boatObjects.values()) { - if (group != bo) { - group.setIsSelected(false); - } - } - } - } - - /** - * Draws all the boats. - * @param yachts The yachts to set in the race - */ - public void setBoats(List yachts) { - BoatObject newBoat; - final List wakes = new ArrayList<>(); - for (ClientYacht clientYacht : yachts) { - Color colour = clientYacht.getColour(); - newBoat = new BoatObject(); - newBoat.addSelectedBoatListener(this::setSelectedBoat); - newBoat.setFill(colour); - boatObjects.put(clientYacht, newBoat); - createAndBindAnnotationBox(clientYacht, colour); -// wakesGroup.getChildren().add(newBoat.getWake()); - wakes.add(newBoat.getWake()); - boatObjectGroup.getChildren().add(newBoat); - trails.getChildren().add(newBoat.getTrail()); - - clientYacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> { - BoatObject bo = boatObjects.get(boat); - Point2D p2d = findScaledXY(lat, lon); - bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir); - annotations.get(boat).setLocation(p2d.getX(), p2d.getY()); - bo.setTrajectory( - heading, - velocity, - metersPerPixelX, - metersPerPixelY); - }); - } - annotationsGroup.getChildren().addAll(annotations.values()); - Platform.runLater(() -> { - gameObjects.addAll(trails); - gameObjects.addAll(wakes); - gameObjects.addAll(annotationsGroup); - gameObjects.addAll(boatObjectGroup); - }); - } - - private void createAndBindAnnotationBox(ClientYacht clientYacht, Paint colour) { - AnnotationBox newAnnotation = new AnnotationBox(); - newAnnotation.setFill(colour); - newAnnotation.addAnnotation( - "name", "Player: " + clientYacht.getShortName() - ); -// newAnnotation.addAnnotation( -// "velocity", -// yacht.getVelocityProperty(), -// (velocity) -> String.format("Speed: %.2f ms", velocity.doubleValue()) -// ); -// newAnnotation.addAnnotation( -// "nextMark", -// yacht.timeTillNextProperty(), -// (time) -> { -// DateFormat format = new SimpleDateFormat("mm:ss"); -// return format.format(time); -// } -// ); -// newAnnotation.addAnnotation( -// "lastMark", -// yacht.timeTillNextProperty(), -// (time) -> { -// DateFormat format = new SimpleDateFormat("mm:ss"); -// return format.format(time); -// } -// ); - annotations.put(clientYacht, newAnnotation); - } - - private void drawFps(Double fps) { - //Platform.runLater(() -> fpsDisplay.setText(String.format("%d FPS", Math.round(fps)))); } /** @@ -755,10 +388,6 @@ public class GameView extends Pane { return horiDistance; } - private Point2D findScaledXY(GeoPoint unscaled) { - return findScaledXY(unscaled.getLat(), unscaled.getLng()); - } - private Point2D findScaledXY(double unscaledLat, double unscaledLon) { double distanceFromReference; double angleFromReference; @@ -801,161 +430,10 @@ public class GameView extends Pane { return new Point2D(xAxisLocation, yAxisLocation); } - /** - * Find the number of meters per pixel. - */ - private void findMetersPerPixel() { - Point2D p1, p2; - GeoPoint g1, g2; - double theta, distance, dx, dy, dHorizontal, dVertical; - g1 = new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng()); - g2 = new GeoPoint(minLatPoint.getLat(), maxLatPoint.getLng()); - p1 = findScaledXY(new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng())); - p2 = findScaledXY(new GeoPoint(minLatPoint.getLat(), maxLatPoint.getLng())); - theta = GeoUtility.getBearingRad(g1, g2); - distance = GeoUtility.getDistance(g1, g2); - dHorizontal = Math.abs(Math.sin(theta) * distance); - dVertical = Math.abs(Math.cos(theta) * distance); - dx = Math.abs(p1.getX() - p2.getX()); - dy = Math.abs(p1.getY() - p2.getY()); - metersPerPixelX = dHorizontal / dx; - metersPerPixelY = dVertical / dy; - } - - 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); - } - for (AnnotationBox ag : annotations.values()) { - ag.setAnnotationVisibility("name", teamName); - ag.setAnnotationVisibility("velocity", velocity); - ag.setAnnotationVisibility("nextMark", estTime); - ag.setAnnotationVisibility("lastMark", legTime); - } - } - - public void setFPSVisibility(boolean visibility) { - fpsDisplay.setVisible(visibility); - } - - public void selectBoat(ClientYacht selectedClientYacht) { - boatObjects.forEach((boat, group) -> - group.setIsSelected(boat == selectedClientYacht) - ); - } - - public void pauseRace() { - timer.stop(); - } - - public void setWindDir(double windDir) { - this.windDir = windDir; - } - - public void startRace() { - timer.start(); - } - - public ClientYacht getPlayerYacht() { - return playerYacht; - } - - public void setBoatAsPlayer (ClientYacht playerYacht) { - this.playerYacht = playerYacht; - playerYacht.toggleSail(); - boatObjects.get(playerYacht).setAsPlayer(); - CompoundMark currentMark = course.get(playerYacht.getLegNumber()); - for (Mark mark : currentMark.getMarks()) { - markerObjects.get(mark).showNextExitArrow(); - } - annotations.get(playerYacht).addAnnotation( - "velocity", - playerYacht.getVelocityProperty(), - (velocity) -> String.format("Speed: %.2f ms", velocity.doubleValue()) - ); - Platform.runLater(() -> { - boatObjectGroup.getChildren().remove(boatObjects.get(playerYacht)); - gameObjects.add(boatObjects.get(playerYacht)); - annotationsGroup.getChildren().remove(annotations.get(playerYacht)); - gameObjects.add(annotations.get(playerYacht)); - }); - playerYacht.addMarkRoundingListener(this::updateMarkArrows); - } - - private void updateMarkArrows (ClientYacht yacht, int legNumber) { - //Only show arrows for this and next leg. - CompoundMark nextMark = null; - if (legNumber < course.size() - 1) { - Sounds.playMarkRoundingSound(); - nextMark = course.get(legNumber); - for (Mark mark : nextMark.getMarks()) { - markerObjects.get(mark).showNextEnterArrow(); - } - } - if (legNumber - 2 >= 0) { - CompoundMark lastMark = course.get(Math.max(0, legNumber - 2)); - if (lastMark != nextMark) { - for (Mark mark : lastMark.getMarks()) { - markerObjects.get(mark).hideAllArrows(); - } - } - } - if (legNumber - 1 >= 0) { - CompoundMark thisMark = course.get(Math.max(0, legNumber - 1)); - if (thisMark != nextMark) { - for (Mark mark : thisMark.getMarks()) { - markerObjects.get(mark).showNextExitArrow(); - } - } - } - } - - /** - * 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) { - Point2D point = findScaledXY(collisionPoint); - double circleRadius = 0.0; - Circle circle = new Circle(point.getX(), point.getY(), circleRadius, Color.RED); - - circle.setFill(Color.TRANSPARENT); - circle.setStroke(Color.RED); - circle.setStrokeWidth(3); - - Timeline timeline = new Timeline(); - timeline.setCycleCount(1); - - KeyFrame keyframe1 = new KeyFrame(Duration.ZERO, - new KeyValue(circle.radiusProperty(), 0), - new KeyValue(circle.strokeProperty(), Color.TRANSPARENT)); - KeyFrame keyFrame2 = new KeyFrame(new Duration(1000), - new KeyValue(circle.radiusProperty(), 50), - new KeyValue(circle.strokeProperty(), Color.RED)); - KeyFrame keyFrame3 = new KeyFrame(new Duration(1500), - new KeyValue(circle.strokeProperty(), Color.TRANSPARENT)); - - timeline.getKeyFrames().addAll(keyframe1, keyFrame2, keyFrame3); - - Platform.runLater(() -> gameObjects.add(circle)); - timeline.setOnFinished(event -> Platform.runLater(() -> gameObjects.remove(circle))); - timeline.play(); - } - - public void setFrameRateFXText(Text fpsDisplay) { - this.fpsDisplay = null; - this.fpsDisplay = fpsDisplay; - } public void setSize(Double width, Double height){ this.canvasWidth = width; this.canvasHeight = height; - - this.panelWidth = width; - this.panelHeight = height; } public void setHorizontalBuffer(Double buff){ diff --git a/src/main/java/seng302/visualiser/fxObjects/assets_2D/Marker2D.java b/src/main/java/seng302/visualiser/fxObjects/assets_2D/Marker2D.java new file mode 100644 index 00000000..d9d03478 --- /dev/null +++ b/src/main/java/seng302/visualiser/fxObjects/assets_2D/Marker2D.java @@ -0,0 +1,100 @@ +package seng302.visualiser.fxObjects.assets_2D; + +import java.util.ArrayList; +import java.util.List; +import javafx.application.Platform; +import javafx.scene.Group; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Circle; + +/** + * Visual object for a mark. Contains a coloured circle and any specified arrows. + */ +public class Marker2D extends Group { + + private Circle mark = new Circle(); + private Paint colour = Color.BLACK; + private List enterArrows = new ArrayList<>(); + private List exitArrows = new ArrayList<>(); + private int enterArrowIndex = 0; + private int exitArrowIndex = 0; + + /** + * Creates a new Marker containing only a circle. The default colour is black. + */ + public Marker2D() { + mark.setRadius(5); + mark.setCenterX(0); + mark.setCenterY(0); + Platform.runLater(() -> this.getChildren() + .addAll(mark, new Group())); //Empty group placeholder or arrows. + } + + /** + * Creates a new Marker containing only a circle of the given colour. + * + * @param colour the desired colour for the marker. + */ + public Marker2D(Paint colour) { + this(); + this.colour = colour; + mark.setFill(colour); + } + + /** + * Adds an exit and entry arrow pair to the mark. Arrows are hidden and shown in the order they + * are created by calling showNextEnterArrow() or showNextExitArrow() + * + * @param roundingSide the side the marker will be from the perspective of the arrow. + * @param entryAngle The angle the arrow will point towards a marker + * @param exitAngle The angle the arrow wil point from the marker. + */ + public void addArrows(MarkArrowFactory.RoundingSide roundingSide, double entryAngle, + double exitAngle) { + //Change Color.GRAY to this.colour to revert all gray arrows. + enterArrows.add( + MarkArrowFactory.constructEntryArrow(roundingSide, entryAngle, exitAngle, Color.GRAY) + ); + exitArrows.add( + MarkArrowFactory.constructExitArrow(roundingSide, exitAngle, Color.GRAY) + ); + } + + /** + * Shows the next EnterArrow. Does nothing if there are no more enter arrows. Other arrows + * become hidden. + */ + public void showNextEnterArrow() { + showArrow(enterArrows, enterArrowIndex); + enterArrowIndex++; + } + + /** + * Shows the next ExitArrow. Does nothing if there are no more enter arrows. Other arrows become + * hidden. + */ + public void showNextExitArrow() { + showArrow(exitArrows, exitArrowIndex); + exitArrowIndex++; + } + + private void showArrow(List arrowList, int arrowListIndex) { + if (arrowListIndex < arrowList.size()) { + if (arrowListIndex == 1) { + ; + } + Platform.runLater(() -> { + this.getChildren().remove(1); + this.getChildren().add(arrowList.get(arrowListIndex)); + }); + } + } + + /** + * Hides all arrows. + */ + public void hideAllArrows() { + Platform.runLater(() -> this.getChildren().setAll(mark, new Group())); + } +} \ No newline at end of file