/******************************************************************************* | |
* Copyright (c) 2008, 2012 IBM Corporation and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License 2.0 | |
* which accompanies this distribution, and is available at | |
* https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.bpel.ui.figures; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import org.eclipse.draw2d.AbstractRouter; | |
import org.eclipse.draw2d.Connection; | |
import org.eclipse.draw2d.ConnectionAnchor; | |
import org.eclipse.draw2d.ConnectionRouter; | |
import org.eclipse.draw2d.geometry.Point; | |
import org.eclipse.draw2d.geometry.PointList; | |
import org.eclipse.draw2d.geometry.Ray; | |
import org.eclipse.draw2d.geometry.Rectangle; | |
/** | |
* A ManhattanConnectionRouter with the additional support of specifying | |
* the start and end direction of the connection | |
* @author ascharf | |
* | |
*/ | |
public class ManhattanConnectionRouterEx extends AbstractRouter { | |
private Map rowsUsed = new HashMap(); | |
private Map colsUsed = new HashMap(); | |
//private Hashtable offsets = new Hashtable(7); | |
private Map reservedInfo = new HashMap(); | |
private class ReservedInfo { | |
public List reservedRows = new ArrayList(2); | |
public List reservedCols = new ArrayList(2); | |
} | |
public static Ray UP = new Ray(0, -1), | |
DOWN = new Ray(0, 1), | |
LEFT = new Ray(-1, 0), | |
RIGHT = new Ray(1, 0); | |
private Ray prefStartDirection, prefEndDirection; | |
public ManhattanConnectionRouterEx(){ | |
this(null,null); | |
} | |
/** | |
* Construct a router with the preferred start and end direction. | |
* Possible types are: | |
* <ul> | |
* <li>ManhattanConnectionRouterEx.UP</li> | |
* <li>ManhattanConnectionRouterEx.LEFT</li> | |
* <li>ManhattanConnectionRouterEx.DOWN</li> | |
* <li>ManhattanConnectionRouterEx.RIGHT</li> | |
* </ul> | |
* @param startDirection | |
* @param endDirection | |
*/ | |
public ManhattanConnectionRouterEx(Ray startDirection, Ray endDirection){ | |
this.prefStartDirection = startDirection; | |
this.prefEndDirection = endDirection; | |
} | |
/** | |
* @see ConnectionRouter#invalidate(Connection) | |
*/ | |
@Override | |
public void invalidate(Connection connection) { | |
removeReservedLines(connection); | |
} | |
private int getColumnNear(Connection connection, int r, int n, int x) { | |
int min = Math.min(n, x), | |
max = Math.max(n, x); | |
if (min > r) { | |
max = min; | |
min = r - (min - r); | |
} | |
if (max < r) { | |
min = max; | |
max = r + (r - max); | |
} | |
int proximity = 0; | |
int direction = -1; | |
if (r % 2 != 0) | |
r--; | |
Integer i; | |
while (proximity < r) { | |
i = Integer.valueOf( r + proximity * direction ); | |
if (!colsUsed.containsKey(i)) { | |
colsUsed.put(i, i); | |
reserveColumn(connection, i); | |
return i.intValue(); | |
} | |
int j = i.intValue(); | |
if (j <= min) | |
return j + 2; | |
if (j >= max) | |
return j - 2; | |
if (direction == 1) | |
direction = -1; | |
else { | |
direction = 1; | |
proximity += 2; | |
} | |
} | |
return r; | |
} | |
/** | |
* Returns the direction the point <i>p</i> is in relation to the given rectangle. | |
* Possible values are LEFT (-1,0), RIGHT (1,0), UP (0,-1) and DOWN (0,1). | |
* | |
* @param r the rectangle | |
* @param p the point | |
* @return the direction from <i>r</i> to <i>p</i> | |
*/ | |
protected Ray getDirection(Rectangle r, Point p) { | |
int i, distance = Math.abs(r.x - p.x); | |
Ray direction; | |
direction = LEFT; | |
i = Math.abs(r.y - p.y); | |
if (i <= distance) { | |
distance = i; | |
direction = UP; | |
} | |
i = Math.abs(r.bottom() - p.y); | |
if (i <= distance) { | |
distance = i; | |
direction = DOWN; | |
} | |
i = Math.abs(r.right() - p.x); | |
if (i < distance) { | |
distance = i; | |
direction = RIGHT; | |
} | |
return direction; | |
} | |
protected Ray getEndDirection(Connection conn) { | |
ConnectionAnchor anchor = conn.getTargetAnchor(); | |
Point p = getEndPoint(conn); | |
Rectangle rect; | |
if (anchor.getOwner() == null) | |
rect = new Rectangle(p.x - 1, p.y - 1, 2, 2); | |
else { | |
rect = conn.getTargetAnchor().getOwner().getBounds().getCopy(); | |
conn.getTargetAnchor().getOwner().translateToAbsolute(rect); | |
} | |
return getDirection(rect, p); | |
} | |
protected int getRowNear(Connection connection, int r, int n, int x) { | |
int min = Math.min(n, x), | |
max = Math.max(n, x); | |
if (min > r) { | |
max = min; | |
min = r - (min - r); | |
} | |
if (max < r) { | |
min = max; | |
max = r + (r - max); | |
} | |
int proximity = 0; | |
int direction = -1; | |
if (r % 2 != 0) | |
r--; | |
Integer i; | |
while (proximity < r) { | |
i = Integer.valueOf( r + proximity * direction ); | |
if (!rowsUsed.containsKey(i)) { | |
rowsUsed.put(i, i); | |
reserveRow(connection, i); | |
return i.intValue(); | |
} | |
int j = i.intValue(); | |
if (j <= min) | |
return j + 2; | |
if (j >= max) | |
return j - 2; | |
if (direction == 1) | |
direction = -1; | |
else { | |
direction = 1; | |
proximity += 2; | |
} | |
} | |
return r; | |
} | |
protected Ray getStartDirection(Connection conn) { | |
ConnectionAnchor anchor = conn.getSourceAnchor(); | |
Point p = getStartPoint(conn); | |
Rectangle rect; | |
if (anchor.getOwner() == null) | |
rect = new Rectangle(p.x - 1, p.y - 1, 2, 2); | |
else { | |
rect = conn.getSourceAnchor().getOwner().getBounds().getCopy(); | |
conn.getSourceAnchor().getOwner().translateToAbsolute(rect); | |
} | |
return getDirection(rect, p); | |
} | |
protected void processPositions(Ray start, Ray end, List positions, | |
boolean horizontal, Connection conn) { | |
removeReservedLines(conn); | |
int pos[] = new int[positions.size() + 2]; | |
if (horizontal) | |
pos[0] = start.x; | |
else | |
pos[0] = start.y; | |
int i; | |
for (i = 0; i < positions.size(); i++) { | |
pos[i + 1] = ((Integer)positions.get(i)).intValue(); | |
} | |
if (horizontal == (positions.size() % 2 != 0)) | |
pos[++i] = end.x; | |
else | |
pos[++i] = end.y; | |
PointList points = new PointList(); | |
points.addPoint(new Point(start.x, start.y)); | |
Point p; | |
int current, prev, min, max; | |
boolean adjust; | |
for (i = 2; i < pos.length - 1; i++) { | |
horizontal = !horizontal; | |
prev = pos[i - 1]; | |
current = pos[i]; | |
adjust = (i != pos.length - 2); | |
if (horizontal) { | |
if (adjust) { | |
min = pos[i - 2]; | |
max = pos[i + 2]; | |
pos[i] = current = getRowNear(conn, current, min, max); | |
} | |
p = new Point(prev, current); | |
} else { | |
if (adjust) { | |
min = pos[i - 2]; | |
max = pos[i + 2]; | |
pos[i] = current = getColumnNear(conn, current, min, max); | |
} | |
p = new Point(current, prev); | |
} | |
points.addPoint(p); | |
} | |
points.addPoint(new Point(end.x, end.y)); | |
conn.setPoints(points); | |
} | |
/** | |
* @see ConnectionRouter#remove(Connection) | |
*/ | |
@Override | |
public void remove(Connection connection) { | |
removeReservedLines(connection); | |
} | |
protected void removeReservedLines(Connection connection) { | |
ReservedInfo rInfo = (ReservedInfo) reservedInfo.get(connection); | |
if (rInfo == null) | |
return; | |
for (int i = 0; i < rInfo.reservedRows.size(); i++) { | |
rowsUsed.remove(rInfo.reservedRows.get(i)); | |
} | |
for (int i = 0; i < rInfo.reservedCols.size(); i++) { | |
colsUsed.remove(rInfo.reservedCols.get(i)); | |
} | |
reservedInfo.remove(connection); | |
} | |
protected void reserveColumn(Connection connection, Integer column) { | |
ReservedInfo info = (ReservedInfo) reservedInfo.get(connection); | |
if (info == null) { | |
info = new ReservedInfo(); | |
reservedInfo.put(connection, info); | |
} | |
info.reservedCols.add(column); | |
} | |
protected void reserveRow(Connection connection, Integer row) { | |
ReservedInfo info = (ReservedInfo) reservedInfo.get(connection); | |
if (info == null) { | |
info = new ReservedInfo(); | |
reservedInfo.put(connection, info); | |
} | |
info.reservedRows.add(row); | |
} | |
/** | |
* @see ConnectionRouter#route(Connection) | |
*/ | |
public void route(Connection conn) { | |
if ((conn.getSourceAnchor() == null) || (conn.getTargetAnchor() == null)) | |
return; | |
int i; | |
Point startPoint = getStartPoint(conn); | |
conn.translateToRelative(startPoint); | |
Point endPoint = getEndPoint(conn); | |
conn.translateToRelative(endPoint); | |
Ray start = new Ray(startPoint); | |
Ray end = new Ray(endPoint); | |
Ray average = start.getAveraged(end); | |
Ray direction = new Ray(start, end); | |
Ray startNormal = null, endNormal = null; | |
if(prefEndDirection != null && prefStartDirection != null){ | |
startNormal = prefStartDirection; | |
endNormal = prefEndDirection; | |
}else{ | |
startNormal = getStartDirection(conn); | |
endNormal = getEndDirection(conn); | |
} | |
List positions = new ArrayList(5); | |
boolean horizontal = startNormal.isHorizontal(); | |
if (horizontal) | |
positions.add(new Integer(start.y)); | |
else | |
positions.add(new Integer(start.x)); | |
horizontal = !horizontal; | |
if (startNormal.dotProduct(endNormal) == 0) { | |
if ((startNormal.dotProduct(direction) >= 0) | |
&& (endNormal.dotProduct(direction) <= 0)) { | |
// 0 | |
} else { | |
// 2 | |
if (startNormal.dotProduct(direction) < 0) | |
i = startNormal.similarity(start.getAdded(startNormal.getScaled(10))); | |
else { | |
if (horizontal) | |
i = average.y; | |
else | |
i = average.x; | |
} | |
positions.add(new Integer(i)); | |
horizontal = !horizontal; | |
if (endNormal.dotProduct(direction) > 0) | |
i = endNormal.similarity(end.getAdded(endNormal.getScaled(10))); | |
else { | |
if (horizontal) | |
i = average.y; | |
else | |
i = average.x; | |
} | |
positions.add(new Integer(i)); | |
horizontal = !horizontal; | |
} | |
} else { | |
if (startNormal.dotProduct(endNormal) > 0) { | |
//1 | |
if (startNormal.dotProduct(direction) >= 0) | |
i = startNormal.similarity(start.getAdded(startNormal.getScaled(10))); | |
else | |
i = endNormal.similarity(end.getAdded(endNormal.getScaled(10))); | |
positions.add(new Integer(i)); | |
horizontal = !horizontal; | |
} else { | |
//3 or 1 | |
if (startNormal.dotProduct(direction) < 0) { | |
i = startNormal.similarity(start.getAdded(startNormal.getScaled(10))); | |
positions.add(new Integer(i)); | |
horizontal = !horizontal; | |
} | |
if (horizontal) | |
i = average.y; | |
else | |
i = average.x; | |
positions.add( Integer.valueOf( i )); | |
horizontal = !horizontal; | |
if (startNormal.dotProduct(direction) < 0) { | |
i = endNormal.similarity(end.getAdded(endNormal.getScaled(10))); | |
positions.add( Integer.valueOf( i )); | |
horizontal = !horizontal; | |
} | |
} | |
} | |
if (horizontal) | |
positions.add( Integer.valueOf( end.y )); | |
else | |
positions.add( Integer.valueOf( end.x )); | |
processPositions(start, end, positions, startNormal.isHorizontal(), conn); | |
} | |
public void setPrefStartDirection(Ray prefStartDirection) { | |
this.prefStartDirection = prefStartDirection; | |
} | |
public void setPrefEndDirection(Ray prefEndDirection) { | |
this.prefEndDirection = prefEndDirection; | |
} | |
} |