From 5a133e02e822911bc090d8d9c6a39092283bf757 Mon Sep 17 00:00:00 2001 From: Thomas ADAM Date: Fri, 20 Oct 2023 11:41:32 +0200 Subject: [PATCH] Refactor path finder usage Signed-off-by: Thomas ADAM --- .../powsybl/sld/layout/MatrixZoneLayout.java | 10 +- .../pathfinding/AbstractPathFinder.java | 23 +++ .../sld/layout/pathfinding/Dijkstra.java | 109 ------------ .../pathfinding/DijkstraPathFinder.java | 155 ++++++++++++++++++ .../powsybl/sld/layout/pathfinding/Grid.java | 105 ++++++++++-- .../sld/layout/pathfinding/PathFinder.java | 20 +++ .../layout/pathfinding/PathFinderFactory.java | 18 ++ .../powsybl/sld/layout/pathfinding/Point.java | 59 ++++++- .../MatrixZoneLayoutModel.java | 61 +++---- .../TestCase13ZoneGraphMatrix1x5.svg | 12 +- .../TestCase13ZoneGraphMatrix2x3.svg | 12 +- 11 files changed, 406 insertions(+), 178 deletions(-) create mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/AbstractPathFinder.java delete mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/Dijkstra.java create mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/DijkstraPathFinder.java create mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/PathFinder.java create mode 100644 single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/PathFinderFactory.java rename single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/{ => zonebygrid}/MatrixZoneLayoutModel.java (74%) diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/MatrixZoneLayout.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/MatrixZoneLayout.java index eb658fcd0..32f8705ff 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/MatrixZoneLayout.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/MatrixZoneLayout.java @@ -7,6 +7,8 @@ */ package com.powsybl.sld.layout; +import com.powsybl.sld.layout.pathfinding.*; +import com.powsybl.sld.layout.zonebygrid.*; import com.powsybl.sld.model.coordinate.*; import com.powsybl.sld.model.coordinate.Point; import com.powsybl.sld.model.graphs.*; @@ -22,10 +24,13 @@ public class MatrixZoneLayout extends AbstractZoneLayout { private final MatrixZoneLayoutModel model; private final String[][] matrix; + private final PathFinder pathFinder; + protected MatrixZoneLayout(ZoneGraph graph, String[][] matrix, SubstationLayoutFactory sLayoutFactory, VoltageLevelLayoutFactory vLayoutFactory) { super(graph, sLayoutFactory, vLayoutFactory); this.model = new MatrixZoneLayoutModel(90); this.matrix = matrix; + this.pathFinder = new PathFinderFactory().createDijkstra(); } /** @@ -89,7 +94,7 @@ protected List calculatePolylineSnakeLine(LayoutParameters layoutParam, P if (ss1Graph != null && ss1Graph == ss2Graph) { // in the same Substation polyline = layoutBySubstation.get(ss1Graph).calculatePolylineSnakeLine(layoutParam, nodes, increment); } else if (ss1Graph != null && ss2Graph != null && - model.gridContains(ss1Graph.getId()) && model.gridContains(ss2Graph.getId())) { // in the same Zone + model.contains(ss1Graph.getId()) && model.contains(ss2Graph.getId())) { // in the same Zone String ss1Id = ss1Graph.getId(); String ss2Id = ss2Graph.getId(); Point p1 = vlGraph1.getShiftedPoint(node1); @@ -99,10 +104,11 @@ protected List calculatePolylineSnakeLine(LayoutParameters layoutParam, P // Add starting point polyline.add(p1); // Find snakeline path - polyline.addAll(model.buildSnakeline(ss1Id, p1, dNode1, ss2Id, p2, dNode2)); + polyline.addAll(model.buildSnakeline(pathFinder, ss1Id, p1, dNode1, ss2Id, p2, dNode2)); // Add ending point polyline.add(p2); } else { + // FIXME : substation link but not displayed // not found } return polyline; diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/AbstractPathFinder.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/AbstractPathFinder.java new file mode 100644 index 000000000..3716b73d1 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/AbstractPathFinder.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.sld.layout.pathfinding; + +import java.util.*; + +/** + * @author Thomas Adam + */ +public abstract class AbstractPathFinder implements PathFinder { + @Override + public List toSnakeLine(List path) { + // Change class of Point + final List snakeLine = new ArrayList<>(); + path.forEach(p -> snakeLine.add(new com.powsybl.sld.model.coordinate.Point(p.x(), p.y()))); + return snakeLine; + } +} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/Dijkstra.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/Dijkstra.java deleted file mode 100644 index 00ed5e24a..000000000 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/Dijkstra.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright (c) 2023, RTE (http://www.rte-france.com) - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package com.powsybl.sld.layout.pathfinding; - -import java.util.*; - -/** - * @author Thomas Adam - */ -public final class Dijkstra { - private static final int[] DX = {1, 0, -1, 0}; // Horizontal moves - private static final int[] DY = {0, 1, 0, -1}; // Vertical moves - - public static List findShortestPath(Grid grid, int startX, int startY, int endX, int endY) { - Point start = new Point(startX, startY); - Point end = new Point(endX, endY); - Map parent = new HashMap<>(); - Map distance = new HashMap<>(); - - PriorityQueue queue = new PriorityQueue<>(Comparator.comparingInt(distance::get)); - - distance.put(start, 0); - queue.add(start); - - while (!queue.isEmpty()) { - Point current = queue.poll(); - if (current.equals(end)) { - return reconstructPath(parent, end); - } - - for (int i = 0; i < 4; i++) { - int newX = current.x() + DX[i]; - int newY = current.y() + DY[i]; - Point neighbor = new Point(newX, newY); - - if (grid.isValid(neighbor)) { - int newDist = distance.get(current) + 1; - if (!distance.containsKey(neighbor) || newDist < distance.get(neighbor)) { - distance.put(neighbor, newDist); - parent.put(neighbor, current); - queue.add(neighbor); - } - } - } - } - return new ArrayList<>(); // No path found - } - - private static List reconstructPath(Map parent, Point start) { - List path = new ArrayList<>(); - Point current = start; - while (parent.containsKey(current)) { - path.add(current); - current = parent.get(current); - } - Collections.reverse(path); - return simplify(path); - } - - private static List simplify(List points) { - if (points.size() < 3) { - return points; // Do not simplify if less of 3 points - } - - List simplifiedPoints = new ArrayList<>(); - simplifiedPoints.add(points.get(0)); // Add first point - - for (int i = 1; i < points.size() - 1; i++) { - Point currentPoint = points.get(i); - Point lastSimplifiedPoint = simplifiedPoints.get(simplifiedPoints.size() - 1); - Point nextPoint = points.get(i + 1); - - if (isRightAngle(lastSimplifiedPoint, currentPoint, nextPoint)) { - simplifiedPoints.add(currentPoint); - } - } - - simplifiedPoints.add(points.get(points.size() - 1)); // Add last point - - return simplifiedPoints; - } - - private static boolean isRightAngle(Point p1, - Point p2, - Point p3) { - int x1 = p1.x(); - int y1 = p1.y(); - int x2 = p2.x(); - int y2 = p2.y(); - int x3 = p3.x(); - int y3 = p3.y(); - - // Check if right angles when scalar products are null - int dx1 = x2 - x1; - int dy1 = y2 - y1; - int dx2 = x3 - x2; - int dy2 = y3 - y2; - - return dx1 * dx2 + dy1 * dy2 == 0; - } - - private Dijkstra() { - // Hide default constructor - } -} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/DijkstraPathFinder.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/DijkstraPathFinder.java new file mode 100644 index 000000000..28b97a1de --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/DijkstraPathFinder.java @@ -0,0 +1,155 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.sld.layout.pathfinding; + +import java.util.*; + +/** + * @author Thomas Adam + */ +public final class DijkstraPathFinder extends AbstractPathFinder { + + private static final int[] DX = {1, 0, -1, 0}; // Horizontal moves + private static final int[] DY = {0, 1, 0, -1}; // Vertical moves + + public DijkstraPathFinder() { + // Nothing to do + } + + @Override + public List findShortestPath(Grid grid, int startX, int startY, int endX, int endY, boolean setSnakeLineAsObstacle) { + Point start = new Point(startX, startY); + Point end = new Point(endX, endY); + Map distance = new HashMap<>(); + + PriorityQueue queue = new PriorityQueue<>(Comparator.comparingInt(distance::get)); + + distance.put(start, 0); + queue.add(start); + + while (!queue.isEmpty()) { + Point current = queue.poll(); + if (current.equals(end)) { + List path = reconstructPath(grid, end); + // Make path not available + grid.setAvailability(path, false); + // Reset parent link for next call + grid.setParent(path, null); + + return smoothPath(path); + } + + for (int i = 0; i < 4; i++) { + int newX = current.x() + DX[i]; + int newY = current.y() + DY[i]; + Point neighbor = new Point(newX, newY); + + if (grid.isValid(neighbor)) { + int newDist = distance.get(current) + 1; + // Calculate the angle between the current path and the new path + int angle = current.angleBetween(neighbor, start); + + if (!distance.containsKey(neighbor) || newDist < distance.get(neighbor)) { + distance.put(neighbor, newDist + angle); + grid.setParent(neighbor, current); + queue.add(neighbor); + } + } + } + } + return new ArrayList<>(); // No path found + } + + private List reconstructPath(Grid grid, Point end) { + List path = new ArrayList<>(); + Point current = end; + while (current != null) { + path.add(current); + current = grid.parent(current); + } + Collections.reverse(path); + return path; + } + + public static List smoothPath(List path) { + if (path.size() < 3) { + return path; + } + + List smoothedPath = new ArrayList<>(); + smoothedPath.add(path.get(0)); + + for (int i = 1; i < path.size() - 1; i++) { + Point prev = smoothedPath.get(smoothedPath.size() - 1); + Point current = path.get(i); + Point next = path.get(i + 1); + + if (isRightAngle(prev, current, next)) { + smoothedPath.add(current); + } + } + + smoothedPath.add(path.get(path.size() - 1)); + + return smoothedPath; + } + + private static boolean isRightAngle(Point p1, + Point p2, + Point p3) { + // Check if right angles when scalar products are null + int dx1 = p2.x() - p1.x(); + int dy1 = p2.y() - p1.y(); + int dx2 = p3.x() - p2.x(); + int dy2 = p3.y() - p2.y(); + + return dx1 * dx2 + dy1 * dy2 == 0; + } + + public static List simplify(List path, double tolerance) { + if (path == null || path.size() < 3) { + return path; + } + + List simplifiedPath = new ArrayList<>(); + simplifiedPath.add(path.get(0)); + simplifyRecursive(path, 0, path.size() - 1, tolerance, simplifiedPath); + simplifiedPath.add(path.get(path.size() - 1)); + + return simplifiedPath; + } + + private static void simplifyRecursive(List path, int start, int end, double tolerance, List simplifiedPath) { + double maxDistance = 0; + int index = 0; + + for (int i = start + 1; i < end; i++) { + double distance = perpendicularDistance(path.get(i), path.get(start), path.get(end)); + if (distance > maxDistance) { + maxDistance = distance; + index = i; + } + } + + if (maxDistance > tolerance) { + if (index - start > 1) { + simplifyRecursive(path, start, index, tolerance, simplifiedPath); + } + simplifiedPath.add(path.get(index)); + if (end - index > 1) { + simplifyRecursive(path, index, end, tolerance, simplifiedPath); + } + } + } + + private static double perpendicularDistance(Point point, Point start, Point end) { + double numerator = Math.abs((end.y() - start.y()) * point.x() - (end.x() - start.x()) * point.y() + end.x() * start.y() - end.y() * start.x()); + double denominator = Math.sqrt(Math.pow(end.y() - start.y(), 2) + Math.pow(end.x() - start.x(), 2)); + return numerator / denominator; + } +} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/Grid.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/Grid.java index 41b480f5f..aa8c384b1 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/Grid.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/Grid.java @@ -13,45 +13,116 @@ * @author Thomas Adam */ public class Grid { - private final int[][] values; - private final int width; - private final int height; + + static class Node { + private final Point point; + private Point parent; + private int cost; + + public Node(int x, int y) { + point = new Point(x, y); + parent = null; + cost = WALKABLE; + } + + public int x() { + return point.x(); + } + + public int y() { + return point.x(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Node node = (Node) o; + return point == node.point && + parent == node.parent && + cost == node.cost; + } + + @Override + public int hashCode() { + return Objects.hash(point, parent, cost); + } + } private static final int NOT_WALKABLE = -1; private static final int WALKABLE = 0; + private final Node[][] nodes; + private final int width; + private final int height; + public Grid(int width, int height) { this.width = width; this.height = height; - this.values = new int[width][height]; - for (int[] tile : values) { - Arrays.fill(tile, NOT_WALKABLE); + this.nodes = new Node[width][height]; + for (int x = 0; x < nodes.length; ++x) { + for (int y = 0; y < nodes[0].length; ++y) { + nodes[x][y] = new Node(x, y); + } } } - public void setFreePath(int x, int y) { - // Make sure we are not out of bounds - values[Math.min(x, width - 1)][Math.min(y, height - 1)] = WALKABLE; + public int getHeight() { + return height; + } + + public int getWidth() { + return width; } - public void setObstacle(int x, int y) { + private Node getNode(Point point) { + return getNode(point.x(), point.y()); + } + + private Node getNode(int x, int y) { // Make sure we are not out of bounds - values[Math.min(x, width - 1)][Math.min(y, height - 1)] = NOT_WALKABLE; + int nodeX = Math.max(0, Math.min(x, width - 1)); + int nodeY = Math.max(0, Math.min(y, height - 1)); + return nodes[nodeX][nodeY]; + } + + public Point parent(Point current) { + return getNode(current).parent; + } + + public void setParent(Point point, Point parent) { + getNode(point).parent = parent; + } + + public void setParent(List path, Point parent) { + path.forEach(p -> getNode(p).parent = parent); + } + + public void setAvailability(int x, int y, boolean available) { + getNode(x, y).cost = available ? WALKABLE : NOT_WALKABLE; + } + + public void setAvailability(Point point, boolean available) { + setAvailability(point.x(), point.y(), available); } - public void setObstacles(List path) { - path.forEach(p -> setObstacle(p.x(), p.y())); + public void setAvailability(List path, boolean available) { + path.forEach(p -> setAvailability(p, available)); } public boolean isValid(Point point) { - return point.x() >= 0 && point.x() < width && point.y() >= 0 && point.y() < height && values[point.x()][point.y()] != -1; + return point.x() >= 0 && point.x() < width && point.y() >= 0 && point.y() < height && nodes[point.x()][point.y()].cost != -1; } public void dumpToFile(String filename) { Objects.requireNonNull(filename); try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) { // Transpose from row / col to x / y - int[][] toDump = transpose(values); + int[][] toDump = transposeCost(nodes); for (int[] ints : toDump) { for (int y = 0; y < toDump[0].length; y++) { @@ -67,11 +138,11 @@ public void dumpToFile(String filename) { } } - private static int[][] transpose(int[][] mat) { + private static int[][] transposeCost(Node[][] mat) { int[][] result = new int[mat[0].length][mat.length]; for (int i = 0; i < mat.length; ++i) { for (int j = 0; j < mat[0].length; ++j) { - result[j][i] = mat[i][j]; + result[j][i] = mat[i][j].cost; } } return result; diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/PathFinder.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/PathFinder.java new file mode 100644 index 000000000..db7e71a37 --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/PathFinder.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.sld.layout.pathfinding; + +import java.util.*; + +/** + * @author Thomas Adam + */ +public interface PathFinder { + + List findShortestPath(Grid grid, int startX, int startY, int endX, int endY, boolean setSnakeLineAsObstacle); + + List toSnakeLine(List path); +} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/PathFinderFactory.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/PathFinderFactory.java new file mode 100644 index 000000000..2a08fd08d --- /dev/null +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/PathFinderFactory.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.sld.layout.pathfinding; + +/** + * @author Thomas Adam + */ +public class PathFinderFactory { + + public PathFinder createDijkstra() { + return new DijkstraPathFinder(); + } +} diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/Point.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/Point.java index 0a248a30d..8de442330 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/Point.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/pathfinding/Point.java @@ -6,8 +6,65 @@ */ package com.powsybl.sld.layout.pathfinding; +import java.util.*; + /** * @author Thomas Adam */ -public record Point(int x, int y) { +public class Point { + + private int x; + private int y; + + Point(int x, int y) { + this.x = x; + this.y = y; + } + + public int x() { + return x; + } + + public int y() { + return y; + } + + public Point setX(int x) { + this.x = x; + return this; + } + + public Point setY(int y) { + this.y = y; + return this; + } + + public int manhattanDistance(Point other) { + return Math.abs(this.x - other.x) + Math.abs(this.y - other.y); + } + + public int angleBetween(Point a, Point b) { + int dx1 = a.x - this.x; + int dy1 = a.y - this.y; + int dx2 = b.x - this.x; + int dy2 = b.y - this.y; + return Math.abs(dx1 * dx2 + dy1 * dy2); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Point point = (Point) o; + return x == point.x && y == point.y; + } + + @Override + public int hashCode() { + return Objects.hash(x, y); + } } diff --git a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/MatrixZoneLayoutModel.java b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/zonebygrid/MatrixZoneLayoutModel.java similarity index 74% rename from single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/MatrixZoneLayoutModel.java rename to single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/zonebygrid/MatrixZoneLayoutModel.java index 93a851159..bdf268208 100644 --- a/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/MatrixZoneLayoutModel.java +++ b/single-line-diagram/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/zonebygrid/MatrixZoneLayoutModel.java @@ -5,16 +5,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. * SPDX-License-Identifier: MPL-2.0 */ -package com.powsybl.sld.layout; +package com.powsybl.sld.layout.zonebygrid; +import com.powsybl.sld.layout.*; import com.powsybl.sld.layout.pathfinding.*; -import com.powsybl.sld.layout.zonebygrid.*; import com.powsybl.sld.model.coordinate.*; import com.powsybl.sld.model.coordinate.Point; import com.powsybl.sld.model.graphs.*; import java.util.*; -import java.util.function.*; /** * @author Thomas Adam @@ -34,9 +33,9 @@ public class MatrixZoneLayoutModel { private final int snakelineHallwayWidth; - private Grid pathFindinGrid; + private Grid pathFinderGrid; - protected MatrixZoneLayoutModel(int hallway) { + public MatrixZoneLayoutModel(int hallway) { this.snakelineHallwayWidth = hallway; } @@ -91,30 +90,25 @@ public int getY(String id, Direction direction) { return getY(row, direction); } - public List buildSnakeline(String ss1Id, Point p1, Direction d1, + public List buildSnakeline(PathFinder pathfinder, + String ss1Id, Point p1, Direction d1, String ss2Id, Point p2, Direction d2) { - Grid grid = setFreePath(ss1Id, p1, d1, ss2Id, p2, d2); + insertFreePathInSubstation(ss1Id, p1, d1, ss2Id, p2, d2); // Use path finding algo - List path = Dijkstra.findShortestPath(grid, - (int) p1.getX(), (int) p1.getY(), - (int) p2.getX(), (int) p2.getY()); - - // Set snakeline as obstacles - pathFindinGrid.setObstacles(path); + List snakeLine = pathfinder.toSnakeLine(pathfinder.findShortestPath(pathFinderGrid, + (int) p1.getX(), (int) p1.getY(), + (int) p2.getX(), (int) p2.getY(), + true)); // Only for debug - grid.dumpToFile("out.txt"); - - // - final List snakeline = new ArrayList<>(); - path.forEach(p -> snakeline.add(new Point(p.x(), p.y()))); + pathFinderGrid.dumpToFile("out.txt"); - return snakeline; + return snakeLine; } - private Grid setFreePath(String id1, Point p1, Direction d1, - String id2, Point p2, Direction d2) { + private void insertFreePathInSubstation(String id1, Point p1, Direction d1, + String id2, Point p2, Direction d2) { int dy = 1; int x1 = (int) p1.getX(); @@ -123,7 +117,7 @@ private Grid setFreePath(String id1, Point p1, Direction d1, int min1Y = Math.max(Math.min(y1, ss1Y), 0); int max1Y = Math.max(y1, ss1Y) + dy; for (int y = min1Y; y < max1Y; y++) { - pathFindinGrid.setFreePath(x1, y); + pathFinderGrid.setAvailability(x1, y, true); } int x2 = (int) p2.getX(); @@ -132,9 +126,8 @@ private Grid setFreePath(String id1, Point p1, Direction d1, int min2Y = Math.max(Math.min(y2, ss2Y), 0); int max2Y = Math.max(y2, ss2Y) + dy; for (int y = min2Y; y < max2Y; y++) { - pathFindinGrid.setFreePath(x2, y); + pathFinderGrid.setAvailability(x2, y, true); } - return pathFindinGrid; } public void computePathFindingGrid(ZoneGraph graph, LayoutParameters layoutParameters) { @@ -142,10 +135,10 @@ public void computePathFindingGrid(ZoneGraph graph, LayoutParameters layoutParam int width = (int) graph.getWidth(); int height = (int) graph.getHeight(); - pathFindinGrid = new Grid(width, height); + pathFinderGrid = new Grid(width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { - pathFindinGrid.setObstacle(x, y); + pathFinderGrid.setAvailability(x, y, false); } } @@ -155,7 +148,7 @@ public void computePathFindingGrid(ZoneGraph graph, LayoutParameters layoutParam int ssY = getY(id); for (int x = ssX; x < ssX + matrixCellWidth; x++) { for (int y = ssY; y < ssY + matrixCellHeight; y++) { - pathFindinGrid.setObstacle(x, y); + pathFinderGrid.setAvailability(x, y, false); } } }); @@ -165,7 +158,7 @@ public void computePathFindingGrid(ZoneGraph graph, LayoutParameters layoutParam int ssY = getY(cell.row()); for (int x = ssX; x < ssX + matrixCellWidth; x++) { for (int y = ssY; y < ssY + matrixCellHeight; y++) { - pathFindinGrid.setObstacle(x, y); + pathFinderGrid.setAvailability(x, y, false); } } }); @@ -174,9 +167,8 @@ public void computePathFindingGrid(ZoneGraph graph, LayoutParameters layoutParam for (int r = 0; r < matrixNbRow; r++) { for (int x = 0; x < width; x++) { for (int hy = 0; hy < height; hy += snakelineHallwayWidth + matrixCellHeight) { - // FIXME : 30 need to be parametrised for (int y = hy; y < hy + snakelineHallwayWidth; y += layoutParameters.getHorizontalSnakeLinePadding()) { - pathFindinGrid.setFreePath(x, y); + pathFinderGrid.setAvailability(x, y, true); } } } @@ -185,21 +177,16 @@ public void computePathFindingGrid(ZoneGraph graph, LayoutParameters layoutParam for (int c = 0; c < matrixNbCol; c++) { for (int y = 0; y < height; y++) { for (int hx = 0; hx < width; hx += snakelineHallwayWidth + matrixCellWidth) { - // FIXME : 30 need to be parametrised for (int x = hx; x < hx + snakelineHallwayWidth; x += layoutParameters.getVerticalSnakeLinePadding()) { - pathFindinGrid.setFreePath(x, y); + pathFinderGrid.setAvailability(x, y, true); } } } } } - public boolean gridContains(String id) { + public boolean contains(String id) { Objects.requireNonNull(id); return cellsById.containsKey(id); } - - public void forEachCellGrid(Consumer consumer) { - cellsById.keySet().forEach(consumer); - } } diff --git a/single-line-diagram/single-line-diagram-core/src/test/resources/TestCase13ZoneGraphMatrix1x5.svg b/single-line-diagram/single-line-diagram-core/src/test/resources/TestCase13ZoneGraphMatrix1x5.svg index 8970e6a93..1a9d443e7 100644 --- a/single-line-diagram/single-line-diagram-core/src/test/resources/TestCase13ZoneGraphMatrix1x5.svg +++ b/single-line-diagram/single-line-diagram-core/src/test/resources/TestCase13ZoneGraphMatrix1x5.svg @@ -1075,22 +1075,22 @@ - + - + - + - + - + - + diff --git a/single-line-diagram/single-line-diagram-core/src/test/resources/TestCase13ZoneGraphMatrix2x3.svg b/single-line-diagram/single-line-diagram-core/src/test/resources/TestCase13ZoneGraphMatrix2x3.svg index 4f4451d9e..73d5a0525 100644 --- a/single-line-diagram/single-line-diagram-core/src/test/resources/TestCase13ZoneGraphMatrix2x3.svg +++ b/single-line-diagram/single-line-diagram-core/src/test/resources/TestCase13ZoneGraphMatrix2x3.svg @@ -1075,22 +1075,22 @@ - + - + - + - + - + - +