/******************************************************************************* * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * *******************************************************************************/ package com.cisco.yangide.ext.model.editor.util.connection; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.PointList; /** * The implementation of the {@link IHighwayMatrix} using wave algorithm. */ public class HighwayMatrixWave extends AbstractHighwayMatrix implements IHighwayMatrix { /** * Constructor.<br> * Creates the instance, using starting set of the highways. Before a call of the * {@link #getPath(int, Point, int, Point)} this set can be changed using * {@link #addHighway(Highway)} or {@link #removeHighway(int)}. * * @param highways - starting set of the highways. */ public HighwayMatrixWave(List<Highway> highways) { super(highways); } /** * Looks for a path, using algorithm of a wave.<br> * If path is not found, then {@code null} will be returned.<br> * Returned path contain the {@code start} and {@code end} points. * * @param idFrom the identifier of the starting highway * @param start the start point * @param idTo the identifier of the ending highway * @param end the end point and required direction from from this point * @return finded path or @{code null} */ @Override public RoutePath getPath(int indexFrom, Point start, int indexTo, Point end) { if (indexFrom == indexTo) { PointList points = new PointList(); points.addPoint(start); points.addPoint(end); int length = (int) end.getDistance(start); return new RoutePath(points, 0, length); } // Init vertex info (neighbours, wave label) List<Vertex> vertexes = new ArrayList<>(); for (int index = 0; index < getSize(); index++) { vertexes.add(new Vertex(getHighwayPtr(index))); } // Init Wave LinkedList<Vertex> front = new LinkedList<>(); Vertex vertexFrom = vertexes.get(indexFrom); Vertex vertexTo = vertexes.get(indexTo); vertexFrom.setStartPoint(start); front.add(vertexFrom); // Start wave while (!front.isEmpty()) { Vertex vertex = front.removeFirst(); if (vertex == vertexTo) { int[] path = vertex.getPath(vertexes, indexFrom, indexTo); PointList points = getPoints(path, start, end); Point last = vertex.right.point; int length = vertex.length + (int) end.getDistance(last); return new RoutePath(points, points.size() - 2, length); } int step = vertex.label + 1; for (Integer idxNeighbour : vertex.neighbours) { Vertex neighbour = vertexes.get(idxNeighbour); if (neighbour.updateLabel(vertex, step)) { front.add(neighbour); } } } return null; } /** * Vertex of the graph. Contains the collection of the neighbour vertexes and also the data * needed for algorithm work. NOTE: for internal use only. * * @author Ruslan */ private class Vertex { private static final int UNDEFINED = -1; private final Highway highway; private final List<Integer> neighbours = new ArrayList<>(); private int label = UNDEFINED; private int length = Integer.MAX_VALUE; private Cross left; private Cross right; public Vertex(HighwayPtr ptrHighway) { this.highway = ptrHighway.highway; for (int index = 0; index < getSize(); index++) { if (ptrHighway.index != index && ptrHighway.highway.isIntersect(getHighway(index))) { neighbours.add(index); } } } public void setStartPoint(Point point) { left = new Cross(null, point, false); right = new Cross(null, point, true); length = 0; label = 0; } public boolean updateLabel(Vertex from, int step) { boolean result = label == UNDEFINED; if (result || label == step) { Point cross = from.highway.getIntersection(highway); int newLeftLength = calcLength(cross, from.left.point); int newRightLength = calcLength(cross, from.right.point); int newLength = Math.min(newLeftLength, newRightLength) + from.length; if (newLength < length) { length = newLength; left = new Cross(from, cross, false); right = new Cross(from, cross, true); label = step; } else if (newLength == length) { if (less(cross, left.point)) { left = new Cross(from, cross, false); } else if (more(cross, right.point)) { right = new Cross(from, cross, true); } label = step; } } return result; } private int calcLength(Point cross, Point point) { return highway.isHorizontal() ? Math.abs(point.y - cross.y) : Math.abs(point.x - cross.x); } private boolean less(Point p1, Point p2) { return highway.isHorizontal() ? p1.x < p2.x : p1.y < p2.y; } private boolean more(Point p1, Point p2) { return highway.isHorizontal() ? p1.x > p2.x : p1.y > p2.y; } public int[] getPath(List<Vertex> vertexes, int indexFrom, int indexTo) { int[] result = new int[label + 1]; result[label] = indexTo; result[0] = indexFrom; fillPath(result, label - 1, true, vertexes); return result; } protected void fillPath(int[] result, int index, boolean useRight, List<Vertex> vertexes) { if (index > 0) { Vertex from = useRight ? right.from : left.from; int idxFrom = vertexes.indexOf(from); result[index--] = idxFrom; useRight = useRight ? right.useRight : left.useRight; from.fillPath(result, index, useRight, vertexes); } } private class Cross { public final Vertex from; public final boolean useRight; public final Point point; public Cross(Vertex from, Point point, boolean useRight) { this.from = from; this.point = point; this.useRight = useRight; } } } }