/*******************************************************************************
* 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.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
public class RectilinearAvoidObstaclesPathFinder implements IPathFinder {
/**
* The default spacing maintained between figure and path.
*
* @see #getSpacing()
* @see #setSpacing(int)
*/
public static final int DEFAULT_SPACING = 15;
/**
* The spacing maintained between figure and path.
*/
private int spacing = DEFAULT_SPACING;
private IHighwayMatrix matrix;
private Set<Rectangle> obstacles = new HashSet<>();
private List<Rectangle> temporaryObstaclesStore = null;
/**
* The empty constructor.
*/
public RectilinearAvoidObstaclesPathFinder() {
}
@Override
public void addObstacle(Rectangle rect) {
if (!isSmallObstacle(rect)) {
obstacles.add(addSpacing(rect));
matrix = null;
}
}
@Override
public void removeObstacle(Rectangle rect) {
if (!isSmallObstacle(rect)) {
obstacles.remove(addSpacing(rect));
matrix = null;
}
}
protected boolean isSmallObstacle(Rectangle rect) {
return rect.width() <= 1 && rect.height() <= 1;
}
protected Rectangle addSpacing(Rectangle rect) {
if (rect != null) {
return rect.getCopy().translate(-spacing, -spacing).resize(spacing * 2, spacing * 2);
}
return null;
}
@Override
public void updateObstacle(Rectangle oldBounds, Rectangle newBounds) {
obstacles.remove(oldBounds);
obstacles.remove(newBounds);
}
@Override
public RoutePath find(Position start, Position end, boolean strict) {
// System.out.println(String.format("From: %s, To: %s", start, end));
start = start.getCopy().moveOnDirection(spacing);
end = end.getCopy().moveOnDirection(spacing);
if (isDirect(start, end)) {
PointList points = new PointList();
points.addPoint(start.getPoint());
points.addPoint(end.getPoint());
int length = (int) end.getPoint().getDistance(start.getPoint());
return new RoutePath(points, 0, length);
}
filterObstacles(start, strict);
filterObstacles(end, strict);
try {
IHighwayMatrix matrix = getMatrix();
RoutePath minPath = null;
for (Highway highwayFrom : getHighways(start, strict)) {
int fromId = matrix.addHighway(highwayFrom);
for (Highway highwayTo : getHighways(end, strict)) {
int toId = matrix.addHighway(highwayTo);
RoutePath candidate = matrix.getPath(fromId, start.getPoint(), toId, end.getPoint());
minPath = RoutePath.min(minPath, candidate);
matrix.removeHighway(toId);
}
matrix.removeHighway(fromId);
}
return minPath;
} finally {
restoreObstacles();
}
}
protected void filterObstacles(Position pos, boolean strict) {
if (!strict) {
if (temporaryObstaclesStore == null) {
temporaryObstaclesStore = new ArrayList<>();
}
Rectangle owner = addSpacing(pos.getObstacle());
Point point = pos.getPoint();
for (Iterator<Rectangle> it = obstacles.iterator(); it.hasNext();) {
Rectangle obstacle = it.next();
if (!obstacle.equals(owner) && obstacle.contains(point)) {
it.remove();
temporaryObstaclesStore.add(obstacle);
// TODO: to remove highways from a matrix
matrix = null;
}
}
}
}
protected void restoreObstacles() {
if (temporaryObstaclesStore != null) {
for (Rectangle obstacle : temporaryObstaclesStore) {
addObstacle(obstacle);
// TODO: to add highways to the matrix
}
}
temporaryObstaclesStore = null;
}
protected boolean isDirect(Position start, Position end) {
Point ptStart = start.getPoint();
Point ptEnd = end.getPoint();
if (ptStart.x == ptEnd.x) {
return Math.abs(ptStart.y - ptEnd.y) <= spacing * 2;
}
if (ptStart.y == ptEnd.y) {
return Math.abs(ptStart.x - ptEnd.x) <= spacing * 2;
}
return false;
}
protected List<Highway> getHighways(Position position, boolean strict) {
List<Highway> highways = new ArrayList<>();
// In case position direction is undefined it horizontal and vertical at the same time
if (position.isVertical()) {
Rectangle owner = strict ? addSpacing(position.getObstacle()) : null;
Highway highway = createVerHighway(position.getPoint(), owner);
if (highway != null) {
highways.add(highway);
}
}
if (position.isHorizontal()) {
Rectangle owner = strict ? addSpacing(position.getObstacle()) : null;
Highway highway = createHorHighway(position.getPoint(), owner);
if (highway != null) {
highways.add(highway);
}
}
return highways;
}
protected IHighwayMatrix getMatrix() {
if (matrix == null) {
matrix = createMatrix();
}
return matrix;
}
protected IHighwayMatrix createMatrix() {
List<Highway> highways = new ArrayList<>();
for (Rectangle obstacle : obstacles) {
highways.add(createVerHighway(obstacle.getLeft(), obstacle));
highways.add(createHorHighway(obstacle.getTop(), obstacle));
highways.add(createVerHighway(obstacle.getRight(), obstacle));
highways.add(createHorHighway(obstacle.getBottom(), obstacle));
}
return new HighwayMatrixWave(highways);
}
private Highway createHorHighway(Point point, Rectangle owner) {
int minX = Integer.MIN_VALUE, maxX = Integer.MAX_VALUE;
for (Rectangle obstacle : obstacles) {
if (owner != null && !owner.equals(obstacle) && obstacle.contains(point)) {
return null;
}
if (point.y() > obstacle.y() && point.y() < obstacle.bottom()) {
if (point.x() >= obstacle.right()) {
minX = Math.max(minX, obstacle.right());
} else if (point.x() <= obstacle.x()) {
maxX = Math.min(maxX, obstacle.x());
}
}
}
return Highway.createHorizontal(minX, point.y(), maxX - minX);
}
private Highway createVerHighway(Point point, Rectangle owner) {
int minY = Integer.MIN_VALUE, maxY = Integer.MAX_VALUE;
for (Rectangle obstacle : obstacles) {
if (owner != null && !owner.equals(obstacle) && obstacle.contains(point)) {
return null;
}
if (point.x() > obstacle.x() && point.x() < obstacle.right()) {
if (point.y() >= obstacle.bottom()) {
minY = Math.max(minY, obstacle.bottom());
} else if (point.y() <= obstacle.y()) {
maxY = Math.min(maxY, obstacle.y());
}
}
}
return Highway.createVertical(point.x(), minY, maxY - minY);
}
/**
* Gets the spacing maintained between figure and path.<br>
* <b>Default: </b> 10.
*
* @return the spacing maintained between figure and path.
*/
@Override
public int getSpacing() {
return spacing;
}
/**
* Sets the spacing maintained between figure and path.
*
* @param spacing the spacing to set
*/
@Override
public void setSpacing(int spacing) {
this.spacing = spacing;
}
}