blob: 148813a6c8988654b81a46cda4c9b17ac1de777c [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2002, 2006 IBM Corporation 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
*
* Contributors:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.draw2d.ui.internal.routers;
import java.util.ArrayList;
import java.util.HashMap;
import org.eclipse.draw2d.BendpointConnectionRouter;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gmf.runtime.common.core.util.Trace;
import org.eclipse.gmf.runtime.draw2d.ui.figures.PolylineConnectionEx;
import org.eclipse.gmf.runtime.draw2d.ui.geometry.LineSeg;
import org.eclipse.gmf.runtime.draw2d.ui.internal.Draw2dDebugOptions;
import org.eclipse.gmf.runtime.draw2d.ui.internal.Draw2dPlugin;
import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil;
import org.eclipse.jface.util.Assert;
/*
* @canBeSeenBy org.eclipse.gmf.runtime.draw2d.ui.*
*/
public class ObliqueRouter extends BendpointConnectionRouter {
static public class ArrayListMap {
private HashMap map = new HashMap();
public ArrayListMap() {
super();
}
public ArrayList get(Object key) {
Object value = map.get(key);
if (value == null)
return null;
if (value instanceof ArrayList)
return (ArrayList) value;
ArrayList v = new ArrayList(1);
v.add(value);
return v;
}
public void put(Object key, Object value) {
Object arrayListObject = map.get(key);
if (arrayListObject == null) {
map.put(key, value);
return;
}
if (arrayListObject instanceof ArrayList) {
ArrayList arrayList = (ArrayList) arrayListObject;
if (!arrayList.contains(value))
arrayList.add(value);
return;
}
if (arrayListObject != value) {
ArrayList arrayList = new ArrayList(2);
arrayList.add(arrayListObject);
arrayList.add(value);
map.put(key, arrayList);
}
}
public void remove(Object key, Object value) {
Object arrayListObject = map.get(key);
if (arrayListObject != null) {
if (arrayListObject instanceof ArrayList) {
ArrayList arrayList = (ArrayList) arrayListObject;
arrayList.remove(value);
if (arrayList.isEmpty())
map.remove(key);
return;
}
map.remove(key);
}
}
public int size() {
return map.size();
}
}
private ArrayListMap selfRelConnections = new ArrayListMap();
private class ArrayListKey {
private ConnectionAnchor connectAnchor1;
private ConnectionAnchor connectAnchor2;
ArrayListKey(Connection conn) {
connectAnchor1 = conn.getSourceAnchor();
connectAnchor2 = conn.getTargetAnchor();
}
public ConnectionAnchor getSourceAnchor() {
return connectAnchor1;
}
public ConnectionAnchor getTargetAnchor() {
return connectAnchor2;
}
public int hashCode() {
return connectAnchor1.hashCode() ^ connectAnchor2.hashCode();
}
public boolean equals(Object object) {
boolean isEqual = false;
ArrayListKey listKey;
if (object instanceof ArrayListKey) {
listKey = (ArrayListKey) object;
ConnectionAnchor lk1 = listKey.getSourceAnchor();
ConnectionAnchor lk2 = listKey.getTargetAnchor();
isEqual =
(lk1.equals(connectAnchor1) && lk2.equals(connectAnchor2))
|| (lk1.equals(connectAnchor2) && lk2.equals(connectAnchor1));
}
return isEqual;
}
}
public static final int ROUTER_FLAG_SKIPNORMALIZATION = 1;
protected int routerFlags;
public ObliqueRouter() {
routerFlags = 0;
}
/**
* Determines whether the router is going to avoid obstructions during the
* routing algorithm.
*/
public boolean isAvoidingObstructions(Connection conn) {
if (conn instanceof PolylineConnectionEx) {
return ((PolylineConnectionEx) conn).isAvoidObstacleRouting();
}
return false;
}
/**
* Determines whether the router is going use the closest distance during the
*/
public boolean isClosestDistance(Connection conn) {
if (conn instanceof PolylineConnectionEx) {
return ((PolylineConnectionEx) conn).isClosestDistanceRouting();
}
return false;
}
/**
* Check if this connection is currently being reoriented by seeing if the
* source or target owner are null.
*/
protected boolean isReorienting(Connection conn) {
if (conn.getSourceAnchor().getOwner() == null
|| conn.getTargetAnchor().getOwner() == null) {
return true;
}
return false;
}
/*
* (non-Javadoc)
* @see org.eclipse.draw2d.ConnectionRouter#route(org.eclipse.draw2d.Connection)
*/
final public void route(Connection conn) {
routeBendpoints(conn);
}
/**
* Route the connection accordingly to the router paradigm.
*/
public void routeBendpoints(Connection conn) {
if ((conn.getSourceAnchor() == null)
|| (conn.getTargetAnchor() == null))
return;
PointList points = new PointList();
if (isAvoidingObstructions(conn)) {
points = RouterHelper.getInstance().routeAroundObstructions(conn);
}
else if (isClosestDistance(conn)) {
points = RouterHelper.getInstance().routeClosestDistance(conn);
}
else {
points = RouterHelper.getInstance().routeFromConstraint(conn);
}
routeLine(conn, 0, points);
conn.setPoints(points);
}
/**
* Method removePointsInViews.
* This method will parse through all the points in the given
* polyline and remove any of the points that intersect with the
* start and end figures.
*
* @param conn Connection figure that is currently being routed
* @param newLine PointList that will contain the filtered list of points
* @return boolean true if newLine points changed, false otherwise.
* @throws IllegalArgumentException if either paramter is null.
*/
protected boolean removePointsInViews(
Connection conn,
PointList newLine) {
boolean bChanged = false;
// error checking
if (conn == null || newLine == null ) {
IllegalArgumentException iae = new IllegalArgumentException();
Trace.throwing(
Draw2dPlugin.getInstance(),
Draw2dDebugOptions.EXCEPTIONS_THROWING,
getClass(),
"removePointsInViews()", //$NON-NLS-1$
iae);
throw iae;
}
// check whether the method should be executed.
if (newLine.size() < 3)
return false;
if (conn.getSourceAnchor().getOwner() == null)
return false;
if (conn.getTargetAnchor().getOwner() == null)
return false;
Rectangle startRect = new Rectangle(conn.getSourceAnchor().getOwner().getBounds());
conn.getSourceAnchor().getOwner().translateToAbsolute(startRect);
conn.translateToRelative(startRect);
Rectangle endRect = new Rectangle(conn.getTargetAnchor().getOwner().getBounds());
conn.getTargetAnchor().getOwner().translateToAbsolute(endRect);
conn.translateToRelative(endRect);
// Ignore the first and last points
PointList newPoints = new PointList(newLine.size());
for (int i = 0; i < newLine.size(); i++) {
Point pt = newLine.getPoint(i);
if (i == 0 || i == newLine.size() - 1)
newPoints.addPoint(pt);
else if (!startRect.contains(pt) && !endRect.contains(pt)) {
newPoints.addPoint(pt);
}
else {
bChanged = true;
}
}
if (newPoints.size() != newLine.size()) {
newLine.removeAllPoints();
for (int i = 0; i < newPoints.size(); i++)
newLine.addPoint(new Point(newPoints.getPoint(i)));
}
return bChanged;
}
/**
* Helper method for "route" to just do the core routing of this router without any
* additional ideology (i.e. no closest distance, obstructions routing).
*/
public void routeLine(
Connection conn,
int nestedRoutingDepth,
PointList newLine) {
// get the original line
if (!checkSelfRelConnection(conn, newLine)) {
removePointsInViews(conn, newLine);
}
resetEndPointsToEdge(conn, newLine);
}
/**
* getStraightEdgePoint
* Gets the anchored edge point that intersects with the given line segment.
*
* @param ptEdge Point on the edge of the end shape in relative coordinates
* @param ptRef1 Point that is the first reference in relative coordinates
* @param ptRef2 Point that is the second reference in relative coordiantes
* @return Point that is the straight edge point in relative coordinates
*/
protected static Point getStraightEdgePoint(
final Point ptEdge,
final Point ptRef1,
final Point ptRef2) {
LineSeg lineSeg = new LineSeg(ptRef1, ptRef2);
Point ptProj = lineSeg.perpIntersect(ptEdge.x, ptEdge.y);
// account for possible rounding errors and ensure the
// resulting line is straight
if (Math.abs(ptProj.x - ptRef2.x) < Math.abs(ptProj.y - ptRef2.y))
ptProj.x = ptRef2.x;
else
ptProj.y = ptRef2.y;
return ptProj;
}
/**
* Reset the end points of the connection line to the appropriate anchor position on the start
* and end figures.
*/
protected void resetEndPointsToEdge(
Connection conn,
PointList newLine) {
RouterHelper.getInstance().resetEndPointsToEdge(conn, newLine);
}
protected static final int SELFRELSIZEINIT = 62;
protected static final int SELFRELSIZEINCR = 10;
/**
* Method checkSelfRelConnection.
* Checks to see if this connection should be routed specially as a self relation.
* @param conn Connection to check if it's a self relation
* @param newLine PointList of the routed points
* @return boolean True if Connection is a self relation, False otherwise.
*/
protected boolean checkSelfRelConnection(
Connection conn,
PointList newLine) {
if ((conn.getSourceAnchor().getOwner() == conn.getTargetAnchor()
.getOwner())
&& newLine.size() < 4) {
getSelfRelVertices(conn, newLine);
return true;
} else {
removeSelfRelConnection(conn);
return false;
}
}
/**
* Method removeSelfRelConnection.
* Removes the given connection from the self relation hash map
* @param conn Connection to remove from the map
*/
private void removeSelfRelConnection(Connection conn) {
if (conn.getSourceAnchor() == null || conn.getTargetAnchor() == null
|| conn.getSourceAnchor().getOwner() == null
|| conn.getTargetAnchor().getOwner() == null)
return;
ArrayListKey connectionKey = new ArrayListKey(conn);
ArrayList connectionList = selfRelConnections.get(connectionKey);
if (connectionList != null) {
int index = connectionList.indexOf(conn);
if (index == -1)
return;
selfRelConnections.remove(connectionKey, conn);
}
}
/**
* Method insertSelfRelVertices.
* This method will create a routed line that routes from and to the same figure.
* @param conn
* @param newLine
*/
protected void getSelfRelVertices(Connection conn, PointList newLine) {
if (conn.getSourceAnchor().getOwner() == null)
return;
ArrayListKey connectionKey = new ArrayListKey(conn);
int nSelfIncr = 0;
int nIndex = 0;
ArrayList connectionList = selfRelConnections.get(connectionKey);
if (connectionList != null) {
if (!connectionList.contains(conn)) {
selfRelConnections.put(connectionKey, conn);
connectionList = selfRelConnections.get(connectionKey);
}
nIndex = connectionList.indexOf(conn);
Assert.isTrue(nIndex >= 0);
} else {
selfRelConnections.put(connectionKey, conn);
}
Dimension selfrelsizeincr = new Dimension(SELFRELSIZEINCR, 0);
if (!RouterHelper.getInstance().isFeedback(conn))
selfrelsizeincr = (Dimension)MapModeUtil.getMapMode(conn).DPtoLP(selfrelsizeincr);
IFigure owner = conn.getSourceAnchor().getOwner();
Rectangle bBox = owner.getClientArea();
owner.translateToAbsolute(bBox);
conn.translateToRelative(bBox);
nSelfIncr = selfrelsizeincr.width * (nIndex / 8);
newLine.removeAllPoints();
switch (nIndex % 8) {
case 0 :
getCornerSelfRelVertices(conn, bBox, newLine, nSelfIncr, 1, 1, bBox.getBottomRight());
break;
case 1 :
getVerticalSelfRelVertices(conn, bBox, newLine, nSelfIncr, 1, bBox.getBottom());
break;
case 2 :
getCornerSelfRelVertices(conn, bBox, newLine, nSelfIncr, -1, 1, bBox.getBottomLeft());
break;
case 3 :
getHorizontalSelfRelVertices(conn, bBox, newLine, nSelfIncr, -1, bBox.getLeft());
break;
case 4 :
getCornerSelfRelVertices(conn, bBox, newLine, nSelfIncr, -1, -1, bBox.getTopLeft());
break;
case 5 :
getVerticalSelfRelVertices(conn, bBox, newLine, nSelfIncr, -1, bBox.getTop());
break;
case 6 :
getCornerSelfRelVertices(conn, bBox, newLine, nSelfIncr, 1, -1, bBox.getTopRight());
break;
case 7 :
getHorizontalSelfRelVertices(conn, bBox, newLine, nSelfIncr, 1, bBox.getRight());
break;
}
// ensure that the end points are anchored properly to the shape.
Point ptS2 = newLine.getPoint(0);
Point ptS1 = conn.getSourceAnchor().getReferencePoint();
conn.translateToRelative(ptS1);
Point ptAbsS2 = new Point(ptS2);
conn.translateToAbsolute(ptAbsS2);
Point ptEdge = conn.getSourceAnchor().getLocation(ptAbsS2);
conn.translateToRelative(ptEdge);
ptS1 = getStraightEdgePoint(ptEdge, ptS1, ptS2);
Point ptE2 = newLine.getPoint(newLine.size() - 1);
Point ptE1 = conn.getTargetAnchor().getReferencePoint();
conn.translateToRelative(ptE1);
Point ptAbsE2 = new Point(ptE2);
conn.translateToAbsolute(ptAbsE2);
ptEdge = conn.getTargetAnchor().getLocation(ptAbsE2);
conn.translateToRelative(ptEdge);
ptE1 = getStraightEdgePoint(ptEdge, ptE1, ptE2);
newLine.setPoint(ptS1, 0);
newLine.setPoint(ptE1, newLine.size() - 1);
}
/**
* Method getCornerSelfRelVertices.
* Retrieves the relation points for the self relation given a corner point and direction factors.
* @param bBox Rectangle representing the shape extents to create the self relation around.
* @param newLine PointList of the line to receive the new points
* @param nOffset Incremental offset of the self relation to prevent overlapping relations.
* @param nXDir int Direction (either -1, 1) indicating the horizontal growth direction
* @param nYDir int Direction (either -1, 1) indicating the vertical growth direction
* @param ptOrient Point which represents the starting location for the self relation to
* grow from.
*/
private void getCornerSelfRelVertices(
Connection conn,
Rectangle bBox,
PointList newLine,
int nOffset,
int nXDir,
int nYDir,
Point ptOrient) {
int x = ptOrient.x;
int y = bBox.getCenter().y + (nYDir * bBox.height / 4 );
Point p1 = new Point(x, y);
newLine.addPoint(p1);
int xNew, yNew;
Dimension selfrelsizeinit = new Dimension(SELFRELSIZEINIT, 0);
if (!RouterHelper.getInstance().isFeedback(conn))
selfrelsizeinit = (Dimension)MapModeUtil.getMapMode(conn).DPtoLP(selfrelsizeinit);
xNew = x + (nXDir * (selfrelsizeinit.width + nOffset));
Point p2 = new Point(xNew, y);
newLine.addPoint(p2);
yNew = ptOrient.y + (nYDir * (selfrelsizeinit.width + nOffset));
Point p3 = new Point(xNew, yNew);
newLine.addPoint(p3);
xNew = ptOrient.x - (nXDir * bBox.width / 4);
Point p4 = new Point(xNew, yNew);
newLine.addPoint(p4);
yNew = ptOrient.y;
Point p5 = new Point(xNew, yNew);
newLine.addPoint(p5);
}
/**
* Method getVerticalSelfRelVertices.
* @param bBox Rectangle representing the shape extents to create the self relation around.
* @param newLine PointList of the line to receive the new points
* @param nOffset Incremental offset of the self relation to prevent overlapping relations.
* @param nDir int Direction (either -1, 1) indicating the vertical growth direction
* @param ptOrient Point which represents the starting location for the self relation to
* grow from.
*/
private void getVerticalSelfRelVertices(
Connection conn,
Rectangle bBox,
PointList newLine,
int nOffset,
int nDir,
Point ptOrient) {
int nWidth = bBox.width / 4;
int x = ptOrient.x - nWidth / 2;
int y = ptOrient.y;
Point p1 = new Point(x, y);
newLine.addPoint(p1);
int xNew, yNew;
Dimension selfrelsizeinit = new Dimension(SELFRELSIZEINIT, 0);
if (!RouterHelper.getInstance().isFeedback(conn))
selfrelsizeinit = (Dimension)MapModeUtil.getMapMode(conn).DPtoLP(selfrelsizeinit);
yNew = y + (nDir * (selfrelsizeinit.width + nOffset));
Point p2 = new Point(x, yNew);
newLine.addPoint(p2);
xNew = ptOrient.x + nWidth / 2;
Point p3 = new Point(xNew, yNew);
newLine.addPoint(p3);
yNew = ptOrient.y;
Point p4 = new Point(xNew, yNew);
newLine.addPoint(p4);
}
/**
* Method getHorizontalSelfRelVertices.
* @param bBox Rectangle representing the shape extents to create the self relation around.
* @param newLine PointList of the line to receive the new points
* @param nOffset Incremental offset of the self relation to prevent overlapping relations.
* @param nDir int Direction (either -1, 1) indicating the horizontal growth direction
* @param ptOrient Point which represents the starting location for the self relation to
* grow from.
*/
private void getHorizontalSelfRelVertices(
Connection conn,
Rectangle bBox,
PointList newLine,
int nOffset,
int nDir,
Point ptOrient) {
int nHeight = bBox.height / 4;
int y = ptOrient.y - nHeight / 2;
int x = ptOrient.x;
Point p1 = new Point(x, y);
newLine.addPoint(p1);
int xNew, yNew;
Dimension selfrelsizeinit = new Dimension(SELFRELSIZEINIT, 0);
if (!RouterHelper.getInstance().isFeedback(conn))
selfrelsizeinit = (Dimension)MapModeUtil.getMapMode(conn).DPtoLP(selfrelsizeinit);
xNew = x + (nDir * (selfrelsizeinit.width + nOffset));
Point p2 = new Point(xNew, y);
newLine.addPoint(p2);
yNew = ptOrient.y + nHeight / 2;
Point p3 = new Point(xNew, yNew);
newLine.addPoint(p3);
xNew = ptOrient.x;
Point p4 = new Point(xNew, yNew);
newLine.addPoint(p4);
}
/**
* @see org.eclipse.draw2d.BendpointConnectionRouter#remove(Connection)
*/
public void remove(Connection connection) {
super.remove(connection);
RouterHelper.getInstance().remove(connection);
removeSelfRelConnection(connection);
}
/*
* Added to support GEF's shortest path routing
*/
public void invalidate(Connection connection) {
super.invalidate(connection);
RouterHelper.getInstance().invalidate(connection);
}
/**
* Sets the constraint for the given {@link Connection}.
*
* @param connection The connection whose constraint we are setting
* @param constraint The constraint
*/
public void setConstraint(Connection connection, Object constraint) {
super.setConstraint(connection, constraint);
RouterHelper.getInstance().setConstraint(connection, constraint);
}
}