Skip to content

Commit

Permalink
Merge pull request #406 from jotoh98/ImprovedStrategyFromPaper
Browse files Browse the repository at this point in the history
Redo some javadoc and handle TopologyExceptions in MinimumRectangleSearcher
  • Loading branch information
rank-and-files authored Apr 16, 2020
2 parents 0fe4d35 + adeeb78 commit 74f9a17
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public class GeometryStyle {
public static final GeometryStyle PREVIOUS_RECTANGLE = new GeometryStyle(true, new Color(0xBB67aa57, true));
public static final GeometryStyle CURRENT_POLYGON = new GeometryStyle(true, new Color(0x40E0D0), new Color(0x0840E0D0, true));
public static final GeometryStyle HALF_PLANE_CURRENT_RED = new GeometryStyle(true, new Color(0x99ff0000, true), new Color(0x11ff0000, true));
public static final GeometryStyle HALF_PLANE_PREVIOUS_BROWN = new GeometryStyle(true, new Color(0x99fc532d, true), new Color(0x05fc532d, true));
public static final GeometryStyle HALF_PLANE_BEFORE_PREVIOUS_LIGHT_BROWN = new GeometryStyle(true, new Color(0x99ff9933, true), new Color(0x05ff9933, true));
public static final GeometryStyle HALF_PLANE_PREVIOUS_BROWN = new GeometryStyle(true, new Color(0x99fc532d, true), new Color(0x09fc532d, true));
public static final GeometryStyle HALF_PLANE_BEFORE_PREVIOUS_LIGHT_BROWN = new GeometryStyle(true, new Color(0x99ff9933, true), new Color(0x09ff9933, true));
public static final GeometryStyle L1_DOUBLE_APOS = new GeometryStyle(true, Color.BLUE);
public static final GeometryStyle HALF_PLANE = new GeometryStyle(true, new Color(0x777777), new Color(0x22777777, true));
public static final GeometryStyle WAY_POINT_LINE = new GeometryStyle(true, new Color(0xFFFFFF));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
* @author Rank
*/
@Preference(name = PreferenceService.MAX_TREASURE_DISTANCE, value = 100)
@Preference(name = PreferenceService.TREASURE_DISTANCE, value = 100)
@Preference(name = PreferenceService.MIN_TREASURE_DISTANCE, value = 100)
public class RandomHalfPlaneHintHider implements Hider<HalfPlaneHint> {
HalfPlaneHint lastHint = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* A special ase of {@link AngleHint} with 180 degrees or {@link Math#PI} radians,
* defining a {@link HalfPlane} in which the treasure lies
*
* @author bsen
* @author Rank
*/

public class HalfPlaneHint extends AngleHint {
Expand Down Expand Up @@ -47,10 +47,6 @@ public HalfPlaneHint(Coordinate center, Coordinate right) {
/**
* This constructor can be used when unsure whether pointOne or pointTwo is the right/left point of the
* hint but the direction of the hint is known.
*
* @param pointOne
* @param pointTwo
* @param direction
*/
public HalfPlaneHint(Coordinate pointOne, Coordinate pointTwo, Direction direction) {
super(new Coordinate(), new Coordinate(), new Coordinate());
Expand Down Expand Up @@ -204,6 +200,10 @@ public List<GeometryItem<?>> getGeometryItems() {
}
}

/**
* Returns either the center or the right point
* The point with the lower y value gets returned.
*/
public Coordinate getLowerHintPoint() {
if (getCenter().getY() < getRight().getY()) {
return getCenter();
Expand All @@ -212,6 +212,10 @@ public Coordinate getLowerHintPoint() {
}
}

/**
* Returns either the center or the right point
* The point with the higher y value gets returned.
*/
public Coordinate getUpperHintPoint() {
if (getCenter().getY() < getRight().getY()) {
return getRight();
Expand All @@ -220,11 +224,17 @@ public Coordinate getUpperHintPoint() {
}
}

/**
* @return true if the treasure is above this hints line, false otherwise
*/
public boolean pointsUpwards() {
return (getDirection() == Direction.left && getLowerHintPoint().getX() < getUpperHintPoint().getX()) ||
(getDirection() == Direction.right && getLowerHintPoint().getX() > getUpperHintPoint().getX());
}

/**
* @return true if the treasure is below this hints line, false otherwise
*/
public boolean pointsDownwards() {
return (getDirection() == Direction.left && getLowerHintPoint().getX() > getUpperHintPoint().getX()) ||
(getDirection() == Direction.right && getLowerHintPoint().getX() < getUpperHintPoint().getX());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.treasure.hunt.strategy.searcher.SearchPath;
import com.treasure.hunt.utils.JTSUtils;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.operation.buffer.BufferParameters;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -91,8 +92,7 @@ static Polygon intersectHints(List<HalfPlaneHint> hintListOne, List<HalfPlaneHin
newPolygonCorners.add(newPolygonCorners.get(0));
Geometry newPolygon = JTSUtils.GEOMETRY_FACTORY.createPolygon(
newPolygonCorners.toArray(new Coordinate[]{}));
newPolygon = newPolygon.convexHull(); //todo maybe that step could be implemented by normalizing the polygon

newPolygon = newPolygon.convexHull();
if (newPolygon.getArea() == 0) {
return null;
}
Expand Down Expand Up @@ -150,17 +150,7 @@ static Polygon reduceConvexPolygon(Polygon convexPolygon, HalfPlaneHint halfPlan
return (Polygon) newPolygon;
}

static Polygon reduceConvexPolygon(Polygon convexPolygon, List<HalfPlaneHint> halfPlaneHints) {//todo do better
for (HalfPlaneHint halfPlaneHint : halfPlaneHints) {
convexPolygon = reduceConvexPolygon(convexPolygon, halfPlaneHint);
if (convexPolygon == null) {
return JTSUtils.GEOMETRY_FACTORY.createPolygon();
}
}
return convexPolygon;
}

static Geometry visitedPolygon(Point lastLocation, SearchPath move) {
static Geometry visitedPolygon(Point lastLocation, SearchPath move, int endCapStyle) {
if (lastLocation == null || move == null) {
throw new IllegalArgumentException("lastLocation or move is null");
}
Expand All @@ -173,6 +163,10 @@ static Geometry visitedPolygon(Point lastLocation, SearchPath move) {
return JTSUtils.GEOMETRY_FACTORY.createPoint(movesCoordinates[0]).buffer(1);
}
LineString path = JTSUtils.GEOMETRY_FACTORY.createLineString(movesCoordinates);
return path.buffer(1);
return path.buffer(1, 8, endCapStyle);
}

static Geometry visitedPolygon(Point lastLocation, SearchPath move) {
return visitedPolygon(lastLocation, move, BufferParameters.CAP_ROUND);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import com.treasure.hunt.strategy.searcher.impl.strategyFromPaper.StrategyFromPaper;
import com.treasure.hunt.utils.JTSUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.operation.buffer.BufferParameters;
import org.locationtech.jts.util.AssertionFailedException;

import java.util.ArrayList;
Expand Down Expand Up @@ -62,14 +64,12 @@
*
* @author Rank
*/
public class MinimumRectangleStrategy extends StrategyFromPaper implements Searcher<HalfPlaneHint> {
@Slf4j
public class MinimumRectangleSearcher extends StrategyFromPaper implements Searcher<HalfPlaneHint> {
Point realSearcherStartPosition;
RectangleScanEnhanced rectangleScanEnhanced = new RectangleScanEnhanced(this);
private boolean firstMoveWithHint = true;
private TransformForAxisParallelism transformer;
/**
* received after the last update of the phase's rectangle
*/
@Getter
private List<HalfPlaneHint> obtainedHints; // stored in internal coordinates
/**
Expand Down Expand Up @@ -235,6 +235,10 @@ public SearchPath move(HalfPlaneHint hint) {
return returnHandlingMinimumRectangleStrategy(goodHintSubroutine());
}

/**
* Reduces hintPolygon and currentMultiPolygon, by excluding the area hint can exclude.
* @param hint a received hint (in internal representation)
*/
void reducePolygons(HalfPlaneHint hint) {
if (hintPolygon == null) {
currentMultiPolygon = null;
Expand All @@ -243,11 +247,19 @@ void reducePolygons(HalfPlaneHint hint) {
if (hintPolygon == null || currentMultiPolygonIsEmpty()) {
currentMultiPolygon = null;
} else {
currentMultiPolygon = hintPolygon.difference(visitedPolygon);
try {
currentMultiPolygon = hintPolygon.difference(visitedPolygon);
} catch (TopologyException e) {
log.info("Due to precision error, the strategy is exiting.");
throw e;
}
}
}
}

/**
* @return the search-path to go in case the hint was good
*/
SearchPath goodHintSubroutine() {
SearchPath move = new SearchPath();
currentHintQuality = HintQuality.good;
Expand All @@ -257,6 +269,13 @@ SearchPath goodHintSubroutine() {
return move;
}

/**
* Increments the phase (possibly multiple times) until the currentMultiPolygon of this phase is not empty.
* Then it moves to the middle of the new current rectangle (which gets determined by the currentMultiPolygon).
*
* @param move the search-path the moves should be added to
* @return the search-path with the appropriate moves added
*/
SearchPath setNewPhaseAndMove(SearchPath move) {
phaseGotIncrementedThisMove = true;
if (!currentMultiPolygonIsEmpty()) {
Expand All @@ -282,6 +301,10 @@ SearchPath setNewPhaseAndMove(SearchPath move) {
return move;
}

/**
* @param hintInInternal the current hint in internal representation
* @return true if the hint is good, false otherwise
*/
boolean hintIsGood(HalfPlaneHint hintInInternal) {
LineSegment hintLine = hintInInternal.getHalfPlaneLine();
LineSegment AB = new LineSegment(searchAreaCornerA.getCoordinate(), searchAreaCornerB.getCoordinate());
Expand Down Expand Up @@ -309,14 +332,23 @@ boolean hintIsGood(HalfPlaneHint hintInInternal) {
return true;
}


void updateVisitedPolygon(SearchPath move) {// lastLocation has to be set right
visitedPolygon = (Polygon) visitedPolygon.union(visitedPolygon(lastLocation, move));
/**
* Adds the areas seen by move to the visitedPolygon
*/
void updateVisitedPolygon(SearchPath move) {
Polygon newVisitedPolygon = (Polygon) visitedPolygon.union(visitedPolygon(lastLocation, move));
if (hintPolygon != null && !currentMultiPolygonIsEmpty()) {
currentMultiPolygon = hintPolygon.difference(visitedPolygon);
try {
currentMultiPolygon = hintPolygon.difference(newVisitedPolygon);
} catch (TopologyException e) {
newVisitedPolygon = (Polygon) visitedPolygon.union(visitedPolygon(lastLocation, move, BufferParameters.CAP_FLAT));
log.info("due to precision problem, the seen area of the player gets calculated different");
}
} else {
currentMultiPolygon = null;
}

visitedPolygon = newVisitedPolygon;
}

/**
Expand Down Expand Up @@ -424,6 +456,9 @@ protected SearchPath specificRectangleScan(Coordinate rectangleCorner1, Coordina
move);
}

/**
* @return the current rectangle in internal representation
*/
private Coordinate[] searchRectangle() {
return new Coordinate[]{searchAreaCornerA.getCoordinate(), searchAreaCornerB.getCoordinate(),
searchAreaCornerC.getCoordinate(), searchAreaCornerD.getCoordinate()};
Expand All @@ -444,10 +479,17 @@ private SearchPath scanCurrentRectangle(SearchPath move, HalfPlaneHint hint) {
searchAreaCornerC.getCoordinate(), searchAreaCornerD.getCoordinate(), move);
}

/**
* @return true if the currentMultiPolygon is empty, false otherwise
*/
private boolean currentMultiPolygonIsEmpty() {
return currentMultiPolygon == null || JTSUtils.doubleEqual(currentMultiPolygon.getArea(), 0);
}

/**
* Does set searchAreaCornerA, searchAreaCornerB, etc. so that the searchRectangle covers all areas in currentMultiPolygon
* and is parallel to the phase rectangle
*/
private void setABCDinStrategy() {
if (currentMultiPolygonIsEmpty()) {
searchAreaCornerA = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,30 @@
import static com.treasure.hunt.strategy.searcher.impl.strategyFromPaper.RoutinesFromPaper.meanderThroughLines;

/**
* Implements some alternatives to the RectangleScan-Routine used by the paper "Deterministic Treasure Hunt in the
* Plane with Angular Hints" from Bouchard et al.
*
* @author Rank
*/
public class RectangleScanEnhanced {
MinimumRectangleStrategy strategy;
MinimumRectangleSearcher strategy;

public RectangleScanEnhanced(MinimumRectangleStrategy strategy) {
public RectangleScanEnhanced(MinimumRectangleSearcher strategy) {
this.strategy = strategy;
}

/**
* Meanders through the rectangle to scan it like the RectangleScan Routine from the paper but uses fewer distance
*
* @param a
* @param b
* @param c
* @param d
* @param searchPath
* @return
* @param a a corner of the rectangle which is to be scanned, neighboring d and b
* @param b a corner of the rectangle which is to be scanned, neighboring a and c
* @param c a corner of the rectangle which is to be scanned, neighboring b and d
* @param d a corner of the rectangle which is to be scanned, neighboring c and a
* @param move the search-path the scan should be added to
* @return the resulting search-path
*/
public static SearchPath rectangleScanEnhanced(Coordinate a, Coordinate b, Coordinate c, Coordinate d,
SearchPath searchPath) {
SearchPath move) {
if (a.distance(b) > a.distance(d)) {
Coordinate temp = a;
a = d;
Expand All @@ -40,19 +43,27 @@ public static SearchPath rectangleScanEnhanced(Coordinate a, Coordinate b, Coord
if (numberOfPointsInOneLine == 1) {
Vector2D aToBHalf = new Vector2D(a, b);
aToBHalf = aToBHalf.divide(2);
searchPath.addPoint(JTSUtils.createPoint(a.x + aToBHalf.getX()
move.addPoint(JTSUtils.createPoint(a.x + aToBHalf.getX()
, a.y + aToBHalf.getY()));
searchPath.addPoint(JTSUtils.createPoint(d.x + aToBHalf.getX(),
move.addPoint(JTSUtils.createPoint(d.x + aToBHalf.getX(),
d.y + aToBHalf.getY()
));
return searchPath;
return move;
}

Point[] a_k = lineOfPointsWithDistanceAtMostTwo(numberOfPointsInOneLine, a, b);
Point[] b_k = lineOfPointsWithDistanceAtMostTwo(numberOfPointsInOneLine, d, c);
return meanderThroughLines(a_k, b_k, numberOfPointsInOneLine - 1, searchPath);
return meanderThroughLines(a_k, b_k, numberOfPointsInOneLine - 1, move);
}

/**
* Returns a list of points on the line-segment from p1 to p2
* The first point is in distance 1 to p1 and the last point is in distance 1 to p2.
* The other points go consecutive from the first to the last point and have equal distances to their neighboring
* points. There are numberOfPointsOnLine points in this returned list.
*
* @param numberOfPointsOnLine the number of points in the returned list
*/
static private Point[] lineOfPointsWithDistanceAtMostTwo(int numberOfPointsOnLine, Coordinate p1, Coordinate p2) {
if (numberOfPointsOnLine <= 1) {
throw new IllegalArgumentException("numberOfPointsOnLine must be bigger than 1 but equals " + numberOfPointsOnLine);
Expand All @@ -76,13 +87,25 @@ static private Point[] lineOfPointsWithDistanceAtMostTwo(int numberOfPointsOnLin
return res;
}

SearchPath rectangleScanMinimal(Coordinate rectangleCorner1, Coordinate rectangleCorner2,
Coordinate rectangleCorner3, Coordinate rectangleCorner4, SearchPath move) {
/**
* Replaces RectangleScan and does so by only scanning the minimum to the rectangle abcd parallel rectangle
* which covers all areas not seen and not excludable by hints, inside the current phase rectangle and the input rectangle.
* It then uses EnhancedRectangleScan to scan this rectangle.
*
* @param a a corner of the rectangle which is to be scanned, neighboring d and b
* @param b a corner of the rectangle which is to be scanned, neighboring a and c
* @param c a corner of the rectangle which is to be scanned, neighboring b and d
* @param d a corner of the rectangle which is to be scanned, neighboring c and a
* @param move the search-path the scan should be added to
* @return the resulting search-path
*/
SearchPath rectangleScanMinimal(Coordinate a, Coordinate b,
Coordinate c, Coordinate d, SearchPath move) {
TransformForAxisParallelism transformerForRectangleAxisParallelism =
new TransformForAxisParallelism(new LineSegment(rectangleCorner1, rectangleCorner2));
new TransformForAxisParallelism(new LineSegment(a, b));
strategy.updateVisitedPolygon(move);
Polygon rectanglePolygon = JTSUtils.GEOMETRY_FACTORY.createPolygon(new Coordinate[]{
rectangleCorner1, rectangleCorner2, rectangleCorner3, rectangleCorner4, rectangleCorner1});
a, b, c, d, a});

Geometry newAreaToScan = strategy.getCurrentMultiPolygon().intersection(rectanglePolygon);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import org.locationtech.jts.geom.util.AffineTransformation;

/**
* For more information what internal and external refers to please look in the documentation of MinimumRectangleSearcher
* @see MinimumRectangleSearcher
*
* @author Rank
*/
public class TransformForAxisParallelism {
Expand All @@ -35,8 +38,6 @@ public TransformForAxisParallelism(HalfPlaneHint hint, Point internalCenterInExt
/**
* Creates a transformer where the line line in internal coordinate is parallel to the x-axis and
* the point line.p0 is (0,0) in internal coordinates.
*
* @param line
*/
public TransformForAxisParallelism(LineSegment line) {
this.internalCenterInExternalRepresentation = JTSUtils.GEOMETRY_FACTORY.createPoint(line.p0);
Expand Down
Loading

0 comments on commit 74f9a17

Please sign in to comment.