diff --git a/src/games/strategy/triplea/ui/MapRouteDrawer.java b/src/games/strategy/triplea/ui/MapRouteDrawer.java index 97d896cc360..78e302c6540 100644 --- a/src/games/strategy/triplea/ui/MapRouteDrawer.java +++ b/src/games/strategy/triplea/ui/MapRouteDrawer.java @@ -60,16 +60,13 @@ public void drawRoute(final Graphics2D graphics, final RouteDescription routeDes final int numTerritories = route.getAllTerritories().size(); final int xOffset = mapPanel.getXOffset(); final int yOffset = mapPanel.getYOffset(); - - Point[] routePoints = getRoutePoints(routeDescription, mapData); - Point[] points = routeOptimizer.normalizeForHorizontalWrapping(routePoints).toArray(new Point[routePoints.length]); - + final Point[] points = routeOptimizer.getTranslatedRoute(getRoutePoints(routeDescription, mapData)); final boolean tooFewTerritories = numTerritories <= 1; final boolean tooFewPoints = points.length <= 2; final double scale = mapPanel.getScale(); if (tooFewTerritories || tooFewPoints) { if (routeDescription.getEnd() != null) {// AI has no End Point - drawDirectPath(graphics, points[0], points[points.length-1], xOffset, yOffset, scale); + drawDirectPath(graphics, routeDescription.getStart(), routeDescription.getEnd(), xOffset, yOffset, scale); } else { drawDirectPath(graphics, points[0], points[points.length - 1], xOffset, yOffset, scale); } @@ -120,7 +117,7 @@ private void drawCustomCursor(final Graphics2D graphics, final RouteDescription final int yOffset, final double scale) { final BufferedImage cursorImage = (BufferedImage) routeDescription.getCursorImage(); if (cursorImage != null) { - for (Point[] endPoint : routeOptimizer.getAllPoints(routeDescription.getEnd())) { + for (Point[] endPoint : routeOptimizer.getAllPoints(routeOptimizer.getLastEndPoint())) { graphics.drawImage(cursorImage, (int) (((endPoint[0].x - xOffset) - (cursorImage.getWidth() / 2)) * scale), (int) (((endPoint[0].y - yOffset) - (cursorImage.getHeight() / 2)) * scale), null); @@ -143,7 +140,8 @@ private void drawCustomCursor(final Graphics2D graphics, final RouteDescription */ private void drawDirectPath(final Graphics2D graphics, final Point start, final Point end, final int xOffset, final int yOffset, final double scale) { - for (Point[] newPoints : routeOptimizer.getAllPoints(start, end)) { + final Point[] points = routeOptimizer.getTranslatedRoute(start, end); + for (Point[] newPoints : routeOptimizer.getAllPoints(points)) { drawLineWithTranslate(graphics, new Line2D.Float(newPoints[0], newPoints[1]), xOffset, yOffset, scale); if (newPoints[0].distance(newPoints[1]) > arrowLength) { diff --git a/src/games/strategy/triplea/ui/RouteOptimizer.java b/src/games/strategy/triplea/ui/RouteOptimizer.java index 67b6983ac8e..58cb82ac010 100644 --- a/src/games/strategy/triplea/ui/RouteOptimizer.java +++ b/src/games/strategy/triplea/ui/RouteOptimizer.java @@ -22,59 +22,114 @@ public class RouteOptimizer { private static final int commonAdditionalScreens = 2; private static final int minAdditionalScreens = 0; - private final double gameMapWidth; + private Point endPoint; private int mapWidth; private int mapHeight; public RouteOptimizer(MapData mapData, MapPanel mapPanel) { checkNotNull(mapData); this.mapPanel = checkNotNull(mapPanel); - mapWidth = mapPanel.getImageWidth(); - mapHeight = mapPanel.getImageHeight(); - isInfiniteY = mapData.scrollWrapY(); - isInfiniteX = mapData.scrollWrapX(); - - gameMapWidth = mapData.getMapDimensions().getWidth(); - } - List normalizeForHorizontalWrapping(Point... route) { - final boolean crossesMapSeam = crossMapSeam(route); - final List points = Arrays.asList(route); - - if (crossesMapSeam) { - final int minX = minX(route); - for (Point p : points) { - if ((p.x - minX) > mapPanel.getWidth()) { - p.x -= this.gameMapWidth; + /** + * Algorithm for finding the shortest path for the given Route + * + * @param route The joints on the Map + * @return A Point array which goes through Map Borders if necessary + */ + public Point[] getTranslatedRoute(Point... route) { + mapWidth = mapPanel.getImageWidth(); + mapHeight = mapPanel.getImageHeight(); + if (route == null || route.length == 0) { + // Or the array is too small - } + return route; + } + if (!isInfiniteX && !isInfiniteY) { + // If the Map is not infinite scrolling, we can safely return the given Points + endPoint = route[route.length - 1]; + return route; + } + List result = new ArrayList<>(); + Point previousPoint = null; + for (Point point : route) { + if (previousPoint == null) { + previousPoint = point; + result.add(point); + continue; } + Point closestPoint = getClosestPoint(previousPoint, getPossiblePoints(point)); + result.add(closestPoint); + previousPoint = closestPoint; } - return points; - } - - private boolean crossMapSeam(Point... route) { - final int minX = minX(route); - return Arrays.stream(route).filter(point -> (point.x - minX) > mapPanel.getWidth()).findAny().isPresent(); + endPoint = result.get(result.size() - 1); + return result.toArray(new Point[result.size()]); } - private static int minX(Point... route) { - int minX = Integer.MAX_VALUE; - for (Point p : Arrays.asList(route)) { - if (p.x < minX) { - minX = p.x; + /** + * Returns the Closest Point out of the given Pool + * + * @param source the reference Point + * @param pool Point2D List with all possible options + * @return the closest point in the Pool to the source + */ + private Point getClosestPoint(Point source, List pool) { + double closestDistance = Double.MAX_VALUE; + Point closestPoint = null; + for (Point2D possibleClosestPoint : pool) { + if (closestPoint == null) { + closestDistance = source.distance(possibleClosestPoint); + closestPoint = normalizePoint(getPoint(possibleClosestPoint)); + } else { + double distance = source.distance(possibleClosestPoint); + if (closestDistance > distance) { + closestPoint = getPoint(possibleClosestPoint); + closestDistance = distance; + } } } - return minX; + return closestPoint; } private Point normalizePoint(Point point) { return new Point(point.x % mapWidth, point.y % mapHeight); } + /** + * Method for getting Points, which are a mapHeight/Width away from the actual Point + * Used to display routes with higher offsets than the map width/height + * + * @param point The Point to "clone" + * @return A List of all possible Points depending in map Properties + * size may vary + */ + private List getPossiblePoints(Point2D point) { + List result = new ArrayList<>(); + result.add(point); + if (isInfiniteX && isInfiniteY) { + result.addAll(Arrays.asList( + new Point2D.Double(point.getX() - mapWidth, point.getY() - mapHeight), + new Point2D.Double(point.getX() - mapWidth, point.getY() + mapHeight), + new Point2D.Double(point.getX() + mapWidth, point.getY() - mapHeight), + new Point2D.Double(point.getX() + mapWidth, point.getY() + mapHeight))); + } + if (isInfiniteX) { + result.addAll(Arrays.asList( + new Point2D.Double(point.getX() - mapWidth, point.getY()), + new Point2D.Double(point.getX() + mapWidth, point.getY()))); + + } + if (isInfiniteY) { + result.addAll(Arrays.asList( + new Point2D.Double(point.getX(), point.getY() - mapHeight), + new Point2D.Double(point.getX(), point.getY() + mapHeight))); + + } + return result; + } + /** * Helper Method to convert a {@linkplain Point2D} to a {@linkplain Point} * @@ -85,6 +140,10 @@ public static Point getPoint(Point2D point) { return new Point((int) point.getX(), (int) point.getY()); } + public Point getLastEndPoint() { + return endPoint; + } + /** * Gives a List of Point arrays (Routes) which are the offset equivalent of the given points * Size may vary depending on MapProperties @@ -101,21 +160,20 @@ private List getAlternativePoints(Point... points) { } int counter = 0; for (Point point : points) { - Point normalizedPoint = normalizePoint(point); if (isInfiniteX) { - alternativePoints.get(0)[counter] = new Point(normalizedPoint.x - mapWidth, normalizedPoint.y); - alternativePoints.get(1)[counter] = new Point(normalizedPoint.x + mapWidth, normalizedPoint.y); + alternativePoints.get(0)[counter] = new Point(point.x - mapWidth, point.y); + alternativePoints.get(1)[counter] = new Point(point.x + mapWidth, point.y); } if (isInfiniteY) { int index = altArrayCount == maxAdditionalScreens ? 2 : 0; - alternativePoints.get(index)[counter] = new Point(normalizedPoint.x, normalizedPoint.y - mapHeight); - alternativePoints.get(index + 1)[counter] = new Point(normalizedPoint.x, normalizedPoint.y + mapHeight); + alternativePoints.get(index)[counter] = new Point(point.x, point.y - mapHeight); + alternativePoints.get(index + 1)[counter] = new Point(point.x, point.y + mapHeight); } if (isInfiniteX && isInfiniteY) { - alternativePoints.get(4)[counter] = new Point(normalizedPoint.x - mapWidth, normalizedPoint.y - mapHeight); - alternativePoints.get(5)[counter] = new Point(normalizedPoint.x - mapWidth, normalizedPoint.y + mapHeight); - alternativePoints.get(6)[counter] = new Point(normalizedPoint.x + mapWidth, normalizedPoint.y - mapHeight); - alternativePoints.get(7)[counter] = new Point(normalizedPoint.x + mapWidth, normalizedPoint.y + mapHeight); + alternativePoints.get(4)[counter] = new Point(point.x - mapWidth, point.y - mapHeight); + alternativePoints.get(5)[counter] = new Point(point.x - mapWidth, point.y + mapHeight); + alternativePoints.get(6)[counter] = new Point(point.x + mapWidth, point.y - mapHeight); + alternativePoints.get(7)[counter] = new Point(point.x + mapWidth, point.y + mapHeight); } counter++; } @@ -131,11 +189,7 @@ private List getAlternativePoints(Point... points) { */ public List getAllPoints(Point... points) { List allPoints = getAlternativePoints(points); - Point[] normalizedPoints = new Point[points.length]; - for (int i = 0; i < points.length; i++) { - normalizedPoints[i] = normalizePoint(points[i]); - } - allPoints.add(normalizedPoints); + allPoints.add(points); return allPoints; } @@ -165,7 +219,7 @@ private List getNormalizedLines(double[] xcoords, double[] ycoords) { List lines = new ArrayList<>(); Point2D previousPoint = null; for (int i = 0; i < xcoords.length; i++) { - Point2D trimmedPoint = normalizePoint(getPoint(new Point2D.Double(xcoords[i], ycoords[i]))); + Point2D trimmedPoint = new Point2D.Double(xcoords[i], ycoords[i]); if (previousPoint != null) { lines.add(new Line2D.Double(previousPoint, trimmedPoint)); }