mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Fixed the bug that boats could round over a gate but still "across" it. Added unit test to ensure the algorithm works.
tags: #story[1124]
This commit is contained in:
@@ -177,7 +177,7 @@ public class Yacht {
|
|||||||
|
|
||||||
if (GameState.getMarkOrder().isLastMark(currentMarkSeqID) || currentMarkSeqID == 0) {
|
if (GameState.getMarkOrder().isLastMark(currentMarkSeqID) || currentMarkSeqID == 0) {
|
||||||
if (GeoUtility.checkCrossedLine(currentMark.getSubMark(1),
|
if (GeoUtility.checkCrossedLine(currentMark.getSubMark(1),
|
||||||
currentMark.getSubMark(2), lastLocation, location)) {
|
currentMark.getSubMark(2), lastLocation, location) > 0) {
|
||||||
System.out.println(
|
System.out.println(
|
||||||
"(" + currentMarkSeqID + ") Passed gate: " + currentMark.getMarks().get(0)
|
"(" + currentMarkSeqID + ") Passed gate: " + currentMark.getMarks().get(0)
|
||||||
.getName()
|
.getName()
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package seng302.utilities;
|
package seng302.utilities;
|
||||||
|
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import seng302.gameServer.GameState;
|
|
||||||
import seng302.model.GeoPoint;
|
import seng302.model.GeoPoint;
|
||||||
import seng302.model.mark.CompoundMark;
|
|
||||||
import seng302.model.mark.Mark;
|
|
||||||
|
|
||||||
public class GeoUtility {
|
public class GeoUtility {
|
||||||
|
|
||||||
@@ -96,7 +93,6 @@ public class GeoUtility {
|
|||||||
return new GeoPoint(Math.toDegrees(endLat), Math.toDegrees(endLng));
|
return new GeoPoint(Math.toDegrees(endLat), Math.toDegrees(endLng));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the line function on two points of a line and a test point to test which side of the
|
* Performs the line function on two points of a line and a test point to test which side of the
|
||||||
* line that point is on. If the return value is return 1, then the point is on one side of the
|
* line that point is on. If the return value is return 1, then the point is on one side of the
|
||||||
@@ -128,36 +124,32 @@ public class GeoUtility {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a point passes across a line, either direction
|
* Checks if the line formed by lastLocation and location doesn't intersect the line segment
|
||||||
* See the wiki Mark Rounding algorithm for more info
|
* formed by mark1 and mark2 See the wiki Mark Rounding algorithm for more info
|
||||||
*
|
*
|
||||||
* @param mark1 One mark of the line
|
* @param mark1 One mark of the line
|
||||||
* @param mark2 The second mark of the line
|
* @param mark2 The second mark of the line
|
||||||
* @param lastLocation The last location of the point crossing this line
|
* @param lastLocation The last location of the point crossing this line
|
||||||
* @param location The current location of the point crossing this line
|
* @param location The current location of the point crossing this line
|
||||||
* @return True if crossed since last location --> current location, false otherwise
|
* @return 0 if two line segment doesn't intersect, otherwise 1 if they intersect and
|
||||||
|
* lastLocation is on RHS of the line segment (mark1 -> mark2) or 2 if lastLocation on LHS of
|
||||||
|
* the line segment (mark1 -> mark2)
|
||||||
*/
|
*/
|
||||||
public static Boolean checkCrossedLine(GeoPoint mark1, GeoPoint mark2, GeoPoint lastLocation,
|
public static Integer checkCrossedLine(GeoPoint mark1, GeoPoint mark2, GeoPoint lastLocation,
|
||||||
GeoPoint location) {
|
GeoPoint location) {
|
||||||
//START GATE OR FINISH GATE
|
boolean enteredDirection = isClockwise(mark1, mark2, lastLocation);
|
||||||
Double alpha = GeoUtility.getBearing(mark1, lastLocation);
|
boolean exitedDirection = isClockwise(mark1, mark2, location);
|
||||||
Double beta = GeoUtility.getBearing(mark1, mark2);
|
if (enteredDirection != exitedDirection) {
|
||||||
Double theta = GeoUtility.getBearing(mark1, location);
|
if (!isPointInTriangle(mark1, lastLocation, location, mark2)
|
||||||
alpha = (alpha > 180) ? 360 - alpha : alpha;
|
&& !isPointInTriangle(mark2, lastLocation, location, mark1)) {
|
||||||
beta = (beta > 180) ? 360 - beta : beta;
|
|
||||||
theta = (theta > 180) ? 360 - theta : theta;
|
|
||||||
|
|
||||||
if (alpha < beta && theta > beta) {
|
return enteredDirection ? 1 : 2;
|
||||||
if (!GeoUtility.isPointInTriangle(mark1, lastLocation, location, mark2)) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a point and a vector (angle and vector length) Will create a new point, that vector
|
* Given a point and a vector (angle and vector length) Will create a new point, that vector
|
||||||
* away from the origin point
|
* away from the origin point
|
||||||
@@ -187,10 +179,24 @@ public class GeoUtility {
|
|||||||
* @param bearing2 the bearing of v2
|
* @param bearing2 the bearing of v2
|
||||||
* @return the difference of bearing from v1 to v2
|
* @return the difference of bearing from v1 to v2
|
||||||
*/
|
*/
|
||||||
private static double getBearingDiff(double bearing1, double bearing2) {
|
private static Double getBearingDiff(double bearing1, double bearing2) {
|
||||||
return ((360 - bearing1) + bearing2) % 360;
|
return ((360 - bearing1) + bearing2) % 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a geo point ins on the right hand side of the line segment, which
|
||||||
|
* formed by two geo points v1 -> v2. (Algorithm: point is clockwise to the
|
||||||
|
* line if the bearing difference is less than 180 deg.)
|
||||||
|
*
|
||||||
|
* @param v1 one end of the line segment
|
||||||
|
* @param v2 another end of the line segment
|
||||||
|
* @param point the point to be tested
|
||||||
|
* @return true if the point is on the RHS of the line
|
||||||
|
*/
|
||||||
|
private static Boolean isClockwise(GeoPoint v1, GeoPoint v2, GeoPoint point) {
|
||||||
|
return getBearingDiff(getBearing(v1, v2), getBearing(v1, point)) < 180;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given three geo points to form a triangle, the method returns true if the fourth point is
|
* Given three geo points to form a triangle, the method returns true if the fourth point is
|
||||||
* inside the triangle
|
* inside the triangle
|
||||||
@@ -201,15 +207,15 @@ public class GeoUtility {
|
|||||||
* @param point the point to be tested
|
* @param point the point to be tested
|
||||||
* @return true if the fourth point is inside the triangle
|
* @return true if the fourth point is inside the triangle
|
||||||
*/
|
*/
|
||||||
public static boolean isPointInTriangle(GeoPoint v1, GeoPoint v2, GeoPoint v3, GeoPoint point) {
|
public static Boolean isPointInTriangle(GeoPoint v1, GeoPoint v2, GeoPoint v3, GeoPoint point) {
|
||||||
// true, if diff of bearing from (v1->v2) to (v1->p) is less than 180 deg
|
// true, if diff of bearing from (v1->v2) to (v1->p) is less than 180 deg
|
||||||
boolean sideFlag = getBearingDiff(getBearing(v1, v2), getBearing(v1, point)) < 180;
|
boolean isCW = isClockwise(v1, v2, point);
|
||||||
|
|
||||||
if ((getBearingDiff(getBearing(v2, v3), getBearing(v2, point)) < 180) != sideFlag) {
|
if (isClockwise(v2, v3, point) != isCW) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((getBearingDiff(getBearing(v3, v1), getBearing(v3, point)) < 180) != sideFlag) {
|
if (isClockwise(v3, v1, point) != isCW) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,8 @@ import static org.junit.Assert.assertNotEquals;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import seng302.model.GeoPoint;
|
import seng302.model.GeoPoint;
|
||||||
import seng302.model.mark.CompoundMark;
|
|
||||||
import seng302.utilities.GeoUtility;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* http://www.geoplaner.com/ For plotting geo points for visualisation
|
* http://www.geoplaner.com/ For plotting geo points for visualisation
|
||||||
@@ -161,13 +158,16 @@ public class GeoUtilityTest {
|
|||||||
GeoPoint location1 = new GeoPoint(37.40964, -122.62196);
|
GeoPoint location1 = new GeoPoint(37.40964, -122.62196);
|
||||||
GeoPoint location2 = new GeoPoint(37.40910, -122.62189);
|
GeoPoint location2 = new GeoPoint(37.40910, -122.62189);
|
||||||
GeoPoint location3 = new GeoPoint(37.40949, -122.62202);
|
GeoPoint location3 = new GeoPoint(37.40949, -122.62202);
|
||||||
GeoPoint location4 = new GeoPoint(34.40955, -122.62176);
|
GeoPoint location4 = new GeoPoint(37.40927, -122.62152);
|
||||||
GeoPoint location5 = new GeoPoint(37.40927, -122.62152);
|
|
||||||
GeoPoint location6 = new GeoPoint(34.40933, -122.62163);
|
|
||||||
|
|
||||||
assertTrue(GeoUtility.checkCrossedLine(mark1, mark2, location1, location2));
|
// M1 -> M3 enters from CCW side
|
||||||
assertFalse(GeoUtility.checkCrossedLine(mark1, mark2, location4, location3));
|
assertTrue(GeoUtility.checkCrossedLine(mark1, mark2, location1, location2) == 2);
|
||||||
assertFalse(GeoUtility.checkCrossedLine(mark1, mark2, location1, location3));
|
// M1 -> M3 doesn't across
|
||||||
assertFalse(GeoUtility.checkCrossedLine(mark1, mark2, location5, location6));
|
assertFalse(GeoUtility.checkCrossedLine(mark1, mark2, location1, location3) > 0);
|
||||||
|
// M2 -> M3 enters from CW side
|
||||||
|
assertTrue(GeoUtility.checkCrossedLine(mark1, mark2, location2, location3) == 1);
|
||||||
|
// order changes intersect direction
|
||||||
|
assertTrue(GeoUtility.checkCrossedLine(mark2, mark1, location2, location3) == 2);
|
||||||
|
assertTrue(GeoUtility.checkCrossedLine(mark1, mark2, location3, location2) == 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user