diff --git a/src/main/java/com/treasure/hunt/geom/GeometryAngle.java b/src/main/java/com/treasure/hunt/geom/GeometryAngle.java index 77cc6d08..f275f848 100644 --- a/src/main/java/com/treasure/hunt/geom/GeometryAngle.java +++ b/src/main/java/com/treasure/hunt/geom/GeometryAngle.java @@ -1,6 +1,7 @@ package com.treasure.hunt.geom; import com.treasure.hunt.jts.AdvancedShapeWriter; +import com.treasure.hunt.utils.JTSUtils; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.LineString; @@ -22,7 +23,7 @@ public class GeometryAngle extends LineString implements Shapeable { /** - * Creates a new Geometry via the specified GeometryFactory. + * GeometryAngle constructor via three {@link Coordinate}s. * * @param factory The GeometryFactory suggested to create the Angle * @param right the right angles arm end point @@ -37,6 +38,34 @@ public GeometryAngle(GeometryFactory factory, Coordinate right, Coordinate cente ); } + /** + * GeometryAngle with standard geometry factory. + * + * @param right the right angles arm end point + * @param center the central point of the angle + * @param left the left angles arm end point + */ + public GeometryAngle(Coordinate right, Coordinate center, Coordinate left) { + this(JTSUtils.GEOMETRY_FACTORY, right, center, left); + } + + /** + * GeometryAngle constructor via the central {@link Coordinate}, the start angle and the angles extend. + * + * @param factory The GeometryFactory suggested to create the Angle + * @param center the central point of the angle + * @param start starting angle relative to x-axis + * @param extend angle extend + */ + public GeometryAngle(GeometryFactory factory, Coordinate center, double start, double extend) { + this( + factory, + Vector2D.create(1, 0).rotate(start).translate(center), + center, + Vector2D.create(1, 0).rotate(start + extend).translate(center) + ); + } + private void setCoordinate(int i, Coordinate c) { points.getCoordinate(i).setX(c.getX()); points.getCoordinate(i).setY(c.getY()); diff --git a/src/main/java/com/treasure/hunt/strategy/hider/impl/RandomAngleHintHider.java b/src/main/java/com/treasure/hunt/strategy/hider/impl/RandomAngleHintHider.java index cddd8158..5dc0bf73 100644 --- a/src/main/java/com/treasure/hunt/strategy/hider/impl/RandomAngleHintHider.java +++ b/src/main/java/com/treasure/hunt/strategy/hider/impl/RandomAngleHintHider.java @@ -4,7 +4,6 @@ import com.treasure.hunt.strategy.hint.impl.AngleHint; import com.treasure.hunt.strategy.searcher.Movement; import com.treasure.hunt.utils.JTSUtils; -import org.locationtech.jts.algorithm.Angle; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Point; @@ -26,27 +25,8 @@ public Point getTreasureLocation() { public AngleHint move(Movement movement) { Coordinate searcherPos = movement.getEndPoint().getCoordinate(); - // generate angle - double randomAngle = Math.random() * 2 * Math.PI; // in [0, PI) - double random = Math.random(); - double leftAngle = Angle.angle(searcherPos, - treasurePos.getCoordinate()) + random * randomAngle; - double leftX = searcherPos.getX() + (Math.cos(leftAngle) * 1); - double leftY = searcherPos.getY() + (Math.sin(leftAngle) * 1); - double rightAngle = Angle.angle(searcherPos, - treasurePos.getCoordinate()) - (1 - random) * randomAngle; - double rightX = searcherPos.getX() + (Math.cos(rightAngle) * 1); - double rightY = searcherPos.getY() + (Math.sin(rightAngle) * 1); - - /*double angleHintToTreasure = angleBetweenOriented(treasureLocation.getCoordinate(), middle.getCoordinate(), angleLeft.getCoordinate()); - if (angleHintToTreasure > angle || angleHintToTreasure < 0) { - throw new UserControlledAngleHintHider.WrongAngleException("Treasure Location not contained in angle"); - }*/ - return new AngleHint( - new Coordinate(rightX, rightY), - searcherPos, - new Coordinate(leftX, leftY) + JTSUtils.validRandomAngle(searcherPos, treasurePos.getCoordinate(), 2 * Math.PI) ); } } diff --git a/src/main/java/com/treasure/hunt/strategy/hint/impl/AngleHint.java b/src/main/java/com/treasure/hunt/strategy/hint/impl/AngleHint.java index a389f692..f45be68a 100644 --- a/src/main/java/com/treasure/hunt/strategy/hint/impl/AngleHint.java +++ b/src/main/java/com/treasure/hunt/strategy/hint/impl/AngleHint.java @@ -21,8 +21,12 @@ public class AngleHint extends Hint { GeometryAngle geometryAngle; public AngleHint(Coordinate right, Coordinate center, Coordinate left) { - geometryAngle = new GeometryAngle(JTSUtils.GEOMETRY_FACTORY, right, center, left); - log.trace(geometryAngle.toString()); + this(new GeometryAngle(JTSUtils.GEOMETRY_FACTORY, right, center, left)); + } + + public AngleHint(GeometryAngle angle) { + geometryAngle = angle; + log.trace(angle.toString()); } public List> getGeometryItems() { diff --git a/src/main/java/com/treasure/hunt/utils/JTSUtils.java b/src/main/java/com/treasure/hunt/utils/JTSUtils.java index f4c40f50..48eb4d95 100644 --- a/src/main/java/com/treasure/hunt/utils/JTSUtils.java +++ b/src/main/java/com/treasure/hunt/utils/JTSUtils.java @@ -2,6 +2,7 @@ import com.treasure.hunt.geom.GeometryAngle; import com.treasure.hunt.strategy.hint.impl.AngleHint; +import org.locationtech.jts.algorithm.Angle; import org.locationtech.jts.geom.*; import org.locationtech.jts.math.Vector2D; @@ -175,4 +176,14 @@ public static boolean pointInAngle(Coordinate right, Coordinate center, Coordina public static Vector2D lineVector(LineSegment lineSegment) { return new Vector2D(lineSegment.p0, lineSegment.p1); } + + public static GeometryAngle validRandomAngle(Coordinate searcher, Coordinate treasure, double maxExtend) { + if (maxExtend <= 0) { + return null; + } + double givenAngle = Angle.angle(searcher, treasure); + double extend = Math.random() * maxExtend; + double start = givenAngle - extend * Math.random(); + return new GeometryAngle(GEOMETRY_FACTORY, searcher, start, extend); + } } diff --git a/src/test/java/com/treasure/hunt/utils/JTSUtilsTest.java b/src/test/java/com/treasure/hunt/utils/JTSUtilsTest.java index b1d3e63b..8a7ee26d 100644 --- a/src/test/java/com/treasure/hunt/utils/JTSUtilsTest.java +++ b/src/test/java/com/treasure/hunt/utils/JTSUtilsTest.java @@ -1,10 +1,12 @@ package com.treasure.hunt.utils; +import com.treasure.hunt.geom.GeometryAngle; import com.treasure.hunt.strategy.hint.impl.AngleHint; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.locationtech.jts.geom.Coordinate; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class JTSUtilsTest { @@ -195,4 +197,33 @@ private void neq(Coordinate expected, Coordinate actual) { assertTrue(Math.abs(expected.x - actual.x) > 0.0001 || Math.abs(expected.y - actual.y) > 0.0001, "Expected: x≠" + expected.x + " or y≠" + expected.y + ", but was ≈(" + actual.x + ", " + actual.y + ")."); } + + @Test + void randomAngleTest() { + for (int i = 0; i < 100; i++) { + final Coordinate searcher = new Coordinate(Math.random() * 100, Math.random() * 100); + final Coordinate treasure = new Coordinate(Math.random() * 100, Math.random() * 100); + final GeometryAngle angle = JTSUtils.validRandomAngle(searcher, treasure, 2 * Math.PI); + assertInAngle(angle, treasure); + } + } + + @Test + void invalidRandomAngles() { + final Coordinate searcher = new Coordinate(Math.random() * 100, Math.random() * 100); + final Coordinate treasure = new Coordinate(Math.random() * 100, Math.random() * 100); + final GeometryAngle invalidExtend = JTSUtils.validRandomAngle(searcher, treasure, 0); + assertNull(invalidExtend); + } + + @Test + void treasureAtRandomAngleCenter() { + final Coordinate sameCoordinate = new Coordinate(); + final GeometryAngle emptyAngle = JTSUtils.validRandomAngle(sameCoordinate, sameCoordinate, 2 * Math.PI); + assertInAngle(emptyAngle, sameCoordinate); + } + + void assertInAngle(GeometryAngle angle, Coordinate coordinate) { + assertTrue(JTSUtils.pointInAngle(angle, coordinate)); + } }