From 9c79897e013e07e3a59fa0942e0b6954c00ff0f8 Mon Sep 17 00:00:00 2001 From: William Muir Date: Thu, 10 Aug 2017 13:58:32 +1200 Subject: [PATCH] Tidied code, added MidPoint to CompoundMark class Compound Mark class is now constructed with a list of marks. A mid point is created on its construction for use in Geo Calculations #story[1124] #pair[wmu16, hyi25] --- .../simulator/parsers/CourseParser.java | 15 +-- src/main/java/seng302/model/Yacht.java | 95 ++++++++----------- .../java/seng302/model/mark/CompoundMark.java | 64 ++++--------- .../java/seng302/utilities/GeoUtility.java | 13 +++ .../java/seng302/utilities/XMLParser.java | 4 +- src/test/java/seng302/model/YachtTest.java | 9 +- 6 files changed, 87 insertions(+), 113 deletions(-) diff --git a/src/main/java/seng302/gameServer/server/simulator/parsers/CourseParser.java b/src/main/java/seng302/gameServer/server/simulator/parsers/CourseParser.java index 66def8a1..e4cbf676 100644 --- a/src/main/java/seng302/gameServer/server/simulator/parsers/CourseParser.java +++ b/src/main/java/seng302/gameServer/server/simulator/parsers/CourseParser.java @@ -79,18 +79,19 @@ public class CourseParser extends FileParser { if (node.getNodeType() == Node.ELEMENT_NODE) { Element e = (Element) node; Integer markID = Integer.valueOf(e.getAttribute("CompoundMarkID")); - String name = e.getAttribute("Name"); - CompoundMark cMark = new CompoundMark(markID, name); NodeList marks = e.getElementsByTagName("Mark"); - for (int i = 0; i < marks.getLength(); i++) { + List subMarks = new ArrayList<>(); + for (int i = 0; i < marks.getLength(); i++) { Mark mark = getMark(marks.item(i)); - if (mark != null) - cMark.addSubMarks(mark); + if (mark != null) { + subMarks.add(mark); + } } - return cMark; - } + + return new CompoundMark(markID, name, subMarks); + } System.out.println("Failed to create compound mark."); return null; } diff --git a/src/main/java/seng302/model/Yacht.java b/src/main/java/seng302/model/Yacht.java index 95d117fe..94f351c5 100644 --- a/src/main/java/seng302/model/Yacht.java +++ b/src/main/java/seng302/model/Yacht.java @@ -64,8 +64,7 @@ public class Yacht { //MARK ROUNDING INFO private GeoPoint lastLocation; //For purposes of mark rounding calculations private Boolean hasEnteredRoundingZone; //The distance that the boat must be from the mark to round - private Boolean hasPassedFirstLine; //The line extrapolated from the next mark to the current mark - private Boolean hasPassedSecondLine; + private Boolean hasPassedLine; private Boolean hasPassedThroughGate; private Boolean finishedRace; @@ -93,8 +92,7 @@ public class Yacht { this.velocity = 0d; //in mms-1 this.hasEnteredRoundingZone = false; - this.hasPassedFirstLine = false; - this.hasPassedSecondLine = false; + this.hasPassedLine = false; this.hasPassedThroughGate = false; this.finishedRace = false; } @@ -193,13 +191,13 @@ public class Yacht { * @param currentMark The current gate */ private void checkStartLineCrossing(CompoundMark currentMark) { - Integer crossedLine = GeoUtility.checkCrossedLine(currentMark.getSubMark(1), - currentMark.getSubMark(2), lastLocation, location); + Mark mark1 = currentMark.getSubMark(1); + Mark mark2 = currentMark.getSubMark(2); + CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID); + + Integer crossedLine = GeoUtility.checkCrossedLine(mark1, mark2, lastLocation, location); if (crossedLine > 0) { - CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID); - Boolean isClockwiseCross = GeoUtility.isClockwise(currentMark.getSubMark(1), - currentMark.getSubMark(2), - nextMark.getSubMark(1)); + Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, nextMark.getMidPoint()); if (crossedLine == 2 && isClockwiseCross || crossedLine == 1 && !isClockwiseCross) { currentMarkSeqID++; logMarkRounding(currentMark); @@ -211,49 +209,31 @@ public class Yacht { /** * This algorithm checks for mark rounding. And increments the currentMarSeqID number attribute * of the yacht if so. - * The algorithm works by using the last mark, the current mark, the next mark and the change in - * boats location, like so: - * -Condition 1: - * The boat has entered the mark rounding distance - * -Condition 2: - * The boat has passed the line extending from the last mark to the current mark - * -Condition 3: - * The boat has passed the line extending from the next mark to the current mark - * - * A more visual representation of this algorithm can be seen on the Wiki under + * A visual representation of this algorithm can be seen on the Wiki under * 'mark passing algorithm' */ private void checkMarkRounding(CompoundMark currentMark) { distanceToCurrentMark = calcDistanceToCurrentMark(); - CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID); - CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); + GeoPoint nextPoint = GameState.getMarkOrder().getNextMark(currentMarkSeqID).getMidPoint(); + GeoPoint prevPoint = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID) + .getMidPoint(); + GeoPoint midPoint = GeoUtility.getDirtyMidPoint(nextPoint, prevPoint); //1 TEST FOR ENTERING THE ROUNDING DISTANCE if (distanceToCurrentMark < ROUNDING_DISTANCE) { hasEnteredRoundingZone = true; } - //If the current mark is a gate mark we need to check both its marks for rounding, thus - //we loop + //In case current mark is a gate, loop through all marks just in case for (Mark thisCurrentMark : currentMark.getMarks()) { - //2 TEST FOR CROSSING NEXT - CURRENT LINE FIRST - if (GeoUtility - .isPointInTriangle(lastLocation, location, nextMark.getMarks().get(0), - thisCurrentMark)) { - hasPassedFirstLine = true; - } - //3 TEST FOR CROSSING PREV - CURRENT LINE SECOND - if (GeoUtility - .isPointInTriangle(lastLocation, location, prevMark.getMarks().get(0), - thisCurrentMark)) { - hasPassedSecondLine = true; + if (GeoUtility.isPointInTriangle(lastLocation, location, midPoint, thisCurrentMark)) { + hasPassedLine = true; } } - if (hasPassedSecondLine && hasPassedFirstLine && hasEnteredRoundingZone) { + if (hasPassedLine && hasEnteredRoundingZone) { currentMarkSeqID++; - hasPassedFirstLine = false; - hasPassedSecondLine = false; + hasPassedLine = false; hasEnteredRoundingZone = false; hasPassedThroughGate = false; logMarkRounding(currentMark); @@ -267,29 +247,28 @@ public class Yacht { * @param currentMark The current gate */ private void checkGateRounding(CompoundMark currentMark) { - Integer crossedLine = GeoUtility.checkCrossedLine(currentMark.getSubMark(1), - currentMark.getSubMark(2), lastLocation, location); + Mark mark1 = currentMark.getSubMark(1); + Mark mark2 = currentMark.getSubMark(2); + CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); + CompoundMark nextMark = GameState.getMarkOrder().getNextMark(currentMarkSeqID); + + Integer crossedLine = GeoUtility.checkCrossedLine(mark1, mark2, lastLocation, location); //We have crossed the line if (crossedLine > 0) { - CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); - Boolean isClockwiseCross = GeoUtility.isClockwise(currentMark.getSubMark(1), - currentMark.getSubMark(2), - prevMark.getSubMark(1)); + Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint()); + + //Check we cross the line in the correct direction if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) { hasPassedThroughGate = true; } } - Boolean prevMarkSide = GeoUtility.isClockwise(currentMark.getSubMark(1), - currentMark.getSubMark(2), - GameState.getMarkOrder().getPreviousMark(currentMarkSeqID).getSubMark(1)); - - Boolean nextMarkSide = GeoUtility.isClockwise(currentMark.getSubMark(1), - currentMark.getSubMark(2), - GameState.getMarkOrder().getNextMark(currentMarkSeqID).getSubMark(1)); + Boolean prevMarkSide = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint()); + Boolean nextMarkSide = GeoUtility.isClockwise(mark1, mark2, nextMark.getMidPoint()); if (hasPassedThroughGate) { + //Check if we need to round this gate after passing through if (prevMarkSide == nextMarkSide) { checkMarkRounding(currentMark); } else { @@ -300,18 +279,18 @@ public class Yacht { } /** - * If we pass the finish gate in the correct direction // TODO: 8/08/17 wmu16 - do something + * If we pass the finish gate in the correct direction * * @param currentMark The current gate */ private void checkFinishLineCrossing(CompoundMark currentMark) { - Integer crossedLine = GeoUtility.checkCrossedLine(currentMark.getSubMark(1), - currentMark.getSubMark(2), lastLocation, location); + Mark mark1 = currentMark.getSubMark(1); + Mark mark2 = currentMark.getSubMark(2); + CompoundMark prevMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); + + Integer crossedLine = GeoUtility.checkCrossedLine(mark1, mark2, lastLocation, location); if (crossedLine > 0) { - CompoundMark previousMark = GameState.getMarkOrder().getPreviousMark(currentMarkSeqID); - Boolean isClockwiseCross = GeoUtility.isClockwise(currentMark.getSubMark(1), - currentMark.getSubMark(2), - previousMark.getSubMark(1)); + Boolean isClockwiseCross = GeoUtility.isClockwise(mark1, mark2, prevMark.getMidPoint()); if (crossedLine == 1 && isClockwiseCross || crossedLine == 2 && !isClockwiseCross) { currentMarkSeqID++; finishedRace = true; diff --git a/src/main/java/seng302/model/mark/CompoundMark.java b/src/main/java/seng302/model/mark/CompoundMark.java index af4cd8a8..a4cc8d0c 100644 --- a/src/main/java/seng302/model/mark/CompoundMark.java +++ b/src/main/java/seng302/model/mark/CompoundMark.java @@ -1,8 +1,9 @@ package seng302.model.mark; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import seng302.model.GeoPoint; +import seng302.utilities.GeoUtility; public class CompoundMark { @@ -10,18 +11,17 @@ public class CompoundMark { private String name; private List marks = new ArrayList<>(); + private GeoPoint midPoint; - public CompoundMark(int markID, String name) { - this.compoundMarkId = markID; + public CompoundMark(int markID, String name, List marks) { + this.compoundMarkId = markID; this.name = name; - } - - public void addSubMarks(Mark... marks) { - this.marks.addAll(Arrays.asList(marks)); - } - - public void addSubMarks(List marks) { - this.marks.addAll(marks); + this.marks.addAll(marks); + if (marks.size() > 1) { + this.midPoint = GeoUtility.getDirtyMidPoint(marks.get(0), marks.get(1)); + } else { + this.midPoint = marks.get(0); + } } /** @@ -68,6 +68,16 @@ public class CompoundMark { } } + /** + * NOTE: This is a 'dirty' mid point as it is simply calculated as an xy point would be. + * NO CHECKING FOR LAT / LNG WRAPPING IS DONE IN CREATION OF THIS MIDPOINT + * + * @return GeoPoint of the midpoint of the two marks, or the one mark if there is only one + */ + public GeoPoint getMidPoint() { + return midPoint; + } + /** * Returns whether or not this CompoundMark is a Gate. It is generally cleaner to program to a * specific singleMark or the list of marks. @@ -87,38 +97,6 @@ public class CompoundMark { return marks; } - -// @Override -// public boolean equals(Object other) { -// if (other == null) { -// return false; -// } -// -// if (!(other instanceof Mark)){ -// return false; -// } -// -// Mark otherMark = (Mark) other; -// -// if (otherMark.getLat() != getLat() || otherMark.getLongitude() != getLongitude()) { -// return false; -// } -// -// if (otherMark.getCompoundMarkID() != getCompoundMarkID()){ -// return false; -// } -// -// if (otherMark.getId() != getId()){ -// return false; -// } -// -// if (!otherMark.getName().equals(name)){ -// return false; -// } -// -// return true; -// } - @Override public int hashCode() { int hash = 0; diff --git a/src/main/java/seng302/utilities/GeoUtility.java b/src/main/java/seng302/utilities/GeoUtility.java index 5d96f33e..f6968601 100644 --- a/src/main/java/seng302/utilities/GeoUtility.java +++ b/src/main/java/seng302/utilities/GeoUtility.java @@ -45,6 +45,19 @@ public class GeoUtility { return (Math.toDegrees(getBearingRad(p1, p2)) + 360.0) % 360.0; } + + /** + * WARNING: this function DOES NOT account for wrapping around on lats / longs etc. + * SO BE CAREFUL IN USING THIS FUNCTION + * + * @param p1 GeoPoint 1 + * @param p2 GeoPoint 2 + * @return GeoPoint midPoint + */ + public static GeoPoint getDirtyMidPoint(GeoPoint p1, GeoPoint p2) { + return new GeoPoint((p1.getLat() + p2.getLat()) / 2, (p1.getLng() + p2.getLng()) / 2); + } + /** * Calculates the angle between to angular co-ordinates on a sphere in radians. * diff --git a/src/main/java/seng302/utilities/XMLParser.java b/src/main/java/seng302/utilities/XMLParser.java index c231535d..c4c4348b 100644 --- a/src/main/java/seng302/utilities/XMLParser.java +++ b/src/main/java/seng302/utilities/XMLParser.java @@ -256,9 +256,9 @@ public class XMLParser { if (cMarkNode.getNodeName().equals("CompoundMark")) { cMark = new CompoundMark( XMLParser.getNodeAttributeInt(cMarkNode, "CompoundMarkID"), - XMLParser.getNodeAttributeString(cMarkNode, "Name") + XMLParser.getNodeAttributeString(cMarkNode, "Name"), + createMarks(cMarkNode) ); - cMark.addSubMarks(createMarks(cMarkNode)); allMarks.add(cMark); } } diff --git a/src/test/java/seng302/model/YachtTest.java b/src/test/java/seng302/model/YachtTest.java index 78192e70..bc3b8c80 100644 --- a/src/test/java/seng302/model/YachtTest.java +++ b/src/test/java/seng302/model/YachtTest.java @@ -2,6 +2,8 @@ package seng302.model; import static org.junit.Assert.*; +import java.util.ArrayList; +import java.util.List; import org.junit.Before; import org.junit.Test; import seng302.model.mark.CompoundMark; @@ -33,11 +35,12 @@ public class YachtTest { yacht.setLocation(57.670333, 11.827833); - compoundMark = new CompoundMark(0, "HaomingsMark"); + List subMarks = new ArrayList<>(); Mark subMark1 = new Mark("H", 57.671524, 11.844495, 0); Mark subMark2 = new Mark("H", 57.670822, 11.843392, 0); - compoundMark.addSubMarks(subMark1, subMark2); - + subMarks.add(subMark1); + subMarks.add(subMark2); + compoundMark = new CompoundMark(0, "HaomingsMark", subMarks); }