From a185c9dc962e23a18cf00631f315ab3625dd2db3 Mon Sep 17 00:00:00 2001 From: cir27 Date: Mon, 14 Aug 2017 03:44:46 +1200 Subject: [PATCH] Implemented a factory class that creates mark arrows as needed. Needs to integrated with the real world data and tested to work. Works with all test data so far. #implement #story[1118] --- .../java/seng302/visualiser/GameView.java | 25 ++-- .../fxObjects/MarkArrowFactory.java | 111 +++++++++++++----- .../seng302/visualiser/fxObjects/Marker.java | 13 ++ 3 files changed, 105 insertions(+), 44 deletions(-) diff --git a/src/main/java/seng302/visualiser/GameView.java b/src/main/java/seng302/visualiser/GameView.java index 73d49b89..e8552533 100644 --- a/src/main/java/seng302/visualiser/GameView.java +++ b/src/main/java/seng302/visualiser/GameView.java @@ -6,6 +6,8 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + import javafx.animation.AnimationTimer; import javafx.application.Platform; import javafx.collections.ObservableList; @@ -27,11 +29,7 @@ import seng302.model.mark.CompoundMark; import seng302.model.mark.Corner; import seng302.model.mark.Mark; import seng302.utilities.GeoUtility; -import seng302.visualiser.fxObjects.AnnotationBox; -import seng302.visualiser.fxObjects.BoatObject; -import seng302.visualiser.fxObjects.CourseBoundary; -import seng302.visualiser.fxObjects.Gate; -import seng302.visualiser.fxObjects.Marker; +import seng302.visualiser.fxObjects.*; import seng302.visualiser.map.Boundary; import seng302.visualiser.map.CanvasMap; @@ -222,8 +220,8 @@ public class GameView extends Pane { //Move the Markers to initial position. markerObjects.forEach(((mark, marker) -> { Point2D p2d = findScaledXY(mark.getLat(), mark.getLng()); - marker.setCenterX(p2d.getX()); - marker.setCenterY(p2d.getY()); + marker.setLayoutX(p2d.getX()); + marker.setLayoutY(p2d.getY()); })); Platform.runLater(() -> { markers.getChildren().clear(); @@ -240,11 +238,12 @@ public class GameView extends Pane { */ private void makeAndBindMarker(Mark observableMark, Paint colour) { Marker marker = new Marker(colour); + marker.constructArrows(MarkArrowFactory.RoundingSide.PORT, ThreadLocalRandom.current().nextDouble(91, 180), ThreadLocalRandom.current().nextDouble(1, 90)); markerObjects.put(observableMark, marker); observableMark.addPositionListener((mark, lat, lon) -> { Point2D p2d = findScaledXY(lat, lon); - markerObjects.get(mark).setCenterX(p2d.getX()); - markerObjects.get(mark).setCenterY(p2d.getY()); + markerObjects.get(mark).setLayoutX(p2d.getX()); + markerObjects.get(mark).setLayoutY(p2d.getY()); }); } @@ -259,16 +258,16 @@ public class GameView extends Pane { private Gate makeAndBindGate(Marker m1, Marker m2, Paint colour) { Gate gate = new Gate(colour); gate.startXProperty().bind( - m1.centerXProperty() + m1.layoutXProperty() ); gate.startYProperty().bind( - m1.centerYProperty() + m1.layoutYProperty() ); gate.endXProperty().bind( - m2.centerXProperty() + m2.layoutXProperty() ); gate.endYProperty().bind( - m2.centerYProperty() + m2.layoutYProperty() ); return gate; } diff --git a/src/main/java/seng302/visualiser/fxObjects/MarkArrowFactory.java b/src/main/java/seng302/visualiser/fxObjects/MarkArrowFactory.java index 9ab67b2b..e362cca6 100644 --- a/src/main/java/seng302/visualiser/fxObjects/MarkArrowFactory.java +++ b/src/main/java/seng302/visualiser/fxObjects/MarkArrowFactory.java @@ -1,56 +1,105 @@ package seng302.visualiser.fxObjects; import javafx.scene.Group; +import javafx.scene.paint.Color; import javafx.scene.paint.Paint; -import javafx.scene.shape.Line; -import javafx.scene.shape.Polyline; +import javafx.scene.shape.*; +import javafx.scene.transform.Rotate; /** - * Created by cir27 on 9/08/17. + * Factory class for making rounding arrows for mark objects out of JavaFX objects. */ public class MarkArrowFactory { - public enum RoundingType { + /** + * The side of the boat that will be closest to the mark. + */ + public enum RoundingSide { PORT, STARBOARD, } - public static Group constructEntryArrow (RoundingType roundingSide, double angleOfEntry, - double angleOfExit, Paint colour) { + public static final double MARK_ARROW_SEPARATION = 15; + public static final double ARROW_LENGTH = 75; + public static final double ARROW_HEAD_DEPTH = 10; + public static final double ARROW_HEAD_WIDTH = 6; + public static final double STROKE_WIDTH = 3; + + /** + * Creates an entry arrow group showing an arrow into and out of the rounding area. It is centered on (0, 0). + * @param roundingSide The side of the boat that will be closest to the mark. + * @param angleOfEntry The angle between this mark and the last one as a heading from north in degrees. + * @param angleOfExit The angle between this mark and the next one as a heading from north in degrees. + * @param colour The desired colour of the arrows. + * @return The group containing all JavaFX objects. + */ + public static Group constructEntryArrow (RoundingSide roundingSide, double angleOfEntry, + double angleOfExit, Paint colour) { Group arrow = new Group(); + Group exitSection = constructExitArrow(roundingSide, angleOfExit, colour); + double minAngle = Math.min(angleOfEntry, angleOfExit); + double arcLen = Math.max(angleOfEntry, angleOfExit) - minAngle; + Arc roundSection = new Arc( + 0, 0, MARK_ARROW_SEPARATION, MARK_ARROW_SEPARATION, + angleOfEntry, 180 - (angleOfEntry - angleOfExit) + ); + roundSection.setStrokeWidth(STROKE_WIDTH); + roundSection.setType(ArcType.OPEN); + roundSection.setStroke(colour); + roundSection.setFill(new Color(0,0,0,0)); + Polygon entrySection = constructLineSegment( + roundingSide == RoundingSide.PORT ? RoundingSide.STARBOARD : RoundingSide.PORT, angleOfEntry, colour + ); + arrow.getChildren().addAll(exitSection, roundSection, entrySection); return arrow; } - public static Group constructExitArrow (RoundingType roundingSide, double angleOfEntry, Paint colour) { + /** + * Creates an exit arrow group pointing towards the next mark. + * @param roundingSide The side of the boat that will be closest to the mark. + * @param angle The angle to the next mark as a heading from north in degrees. + * @param colour The colour of the arrow. + * @return The group containing all the JavaFX objects. + */ + public static Group constructExitArrow (RoundingSide roundingSide, double angle, Paint colour) { Group arrow = new Group(); - Line arrowBody; - Polyline arrowHead = constructArrowHead(); - if (roundingSide == RoundingType.PORT) { - arrowBody = new Line( - -10, -10, - -10, -30 - ); - arrowHead.setLayoutX(-10); - arrowHead.setLayoutY(-10); - } else { - arrowBody = new Line( - 10, -10, - 10, -30 - ); - arrowHead.setLayoutX(10); - arrowHead.setLayoutY(-10); - } - arrowBody.setFill(colour); - arrowHead.setFill(colour); + Polygon arrowBody = constructLineSegment(roundingSide, angle, colour); + Polyline arrowHead = constructArrowHead(angle, colour); + arrowHead.setLayoutX(arrowBody.getPoints().get(2)); + arrowHead.setLayoutY(arrowBody.getPoints().get(3)); arrow.getChildren().addAll(arrowBody, arrowHead); return arrow; } - private static Polyline constructArrowHead () { - return new Polyline( - -5, -5, - 0, 0, - 5, -5 + private static Polygon constructLineSegment (RoundingSide roundingSide, double angle, Paint colour) { + Polygon lineSegment; + angle = Math.toRadians(angle); + int multiplier = roundingSide == RoundingSide.PORT ? -1 : 1; + double xStart = multiplier * MARK_ARROW_SEPARATION * Math.sin(angle + Math.PI / 2); + double yStart = multiplier * MARK_ARROW_SEPARATION * Math.cos(angle + Math.PI / 2); + double xEnd = xStart + (ARROW_LENGTH * Math.sin(angle)); + double yEnd = yStart + (ARROW_LENGTH * Math.cos(angle)); + lineSegment = new Polygon( + xStart, yStart, + xEnd, yEnd ); + lineSegment.setStroke(colour); + lineSegment.setFill(Color.BLUE); + lineSegment.setStrokeWidth(STROKE_WIDTH); + lineSegment.setStrokeLineCap(StrokeLineCap.ROUND); + return lineSegment; + } + + private static Polyline constructArrowHead (double rotation, Paint colour) { + Polyline arrow = new Polyline( + -ARROW_HEAD_WIDTH, -ARROW_HEAD_DEPTH, + 0, 0, + ARROW_HEAD_WIDTH, -ARROW_HEAD_DEPTH + ); + arrow.getTransforms().add(new Rotate(-rotation)); + arrow.setStrokeLineCap(StrokeLineCap.ROUND); + arrow.setStroke(colour); + arrow.setStrokeWidth(STROKE_WIDTH); + return arrow; } } diff --git a/src/main/java/seng302/visualiser/fxObjects/Marker.java b/src/main/java/seng302/visualiser/fxObjects/Marker.java index 22942a2b..6fade144 100644 --- a/src/main/java/seng302/visualiser/fxObjects/Marker.java +++ b/src/main/java/seng302/visualiser/fxObjects/Marker.java @@ -2,6 +2,7 @@ package seng302.visualiser.fxObjects; import javafx.application.Platform; import javafx.scene.Group; +import javafx.scene.paint.Color; import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; @@ -11,18 +12,30 @@ import javafx.scene.shape.Circle; public class Marker extends Group { Circle mark = new Circle(); + Paint colour = Color.BLACK; Group enterArrow; Group exitArrow; public Marker() { mark.setRadius(5); + mark.setCenterX(0); + mark.setCenterY(0); + Platform.runLater(() -> this.getChildren().add(mark)); } public Marker(Paint colour) { this(); + this.colour = colour; mark.setFill(colour); } + public void constructArrows(MarkArrowFactory.RoundingSide roundingSide, double entryAngle, double exitAngle) { + enterArrow = MarkArrowFactory.constructEntryArrow(roundingSide, entryAngle, exitAngle, colour); + exitArrow = MarkArrowFactory.constructExitArrow(roundingSide, exitAngle, colour); + Platform.runLater(() -> this.getChildren().add(enterArrow)); +// Platform.runLater(() -> this.getChildren().add(exitArrow)); + } + public void showEnterArrow () { Platform.runLater(() -> this.getChildren().setAll(enterArrow)); }