blob: a1957822c7287390d3c19f261422723429501556 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 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.wst.xsd.ui.internal.design.editparts;
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.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Ray;
import org.eclipse.draw2d.geometry.Rectangle;
// TODO Manhattan connection router is final
public class XSDModelGroupRouter extends AbstractRouter
{
public XSDModelGroupRouter()
{
super();
}
private Map rowsUsed = new HashMap();
private Map colsUsed = new HashMap();
private Map reservedInfo = new HashMap();
private class ReservedInfo {
public List reservedRows = new ArrayList(2);
public List reservedCols = new ArrayList(2);
}
private static Ray UP = new Ray(0, -1),
DOWN = new Ray(0, 1),
LEFT = new Ray(-1, 0),
RIGHT = new Ray(1, 0);
/**
* @see org.eclipse.draw2d.ConnectionRouter#invalidate(Connection)
*/
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 == 1)
r--;
Integer i;
while (proximity < r) {
i = new Integer(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 == 1)
r--;
Integer i;
while (proximity < r) {
i = new Integer(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 == 1))
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 org.eclipse.draw2d.ConnectionRouter#remove(Connection)
*/
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 org.eclipse.draw2d.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 = new Ray(startPoint.x + 4, startPoint.y); // start.getAveraged(end);
Ray direction = new Ray(start, end);
Ray startNormal = getStartDirection(conn);
Ray 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(new Integer(i));
horizontal = !horizontal;
if (startNormal.dotProduct(direction) < 0) {
i = endNormal.similarity(end.getAdded(endNormal.getScaled(10)));
positions.add(new Integer(i));
horizontal = !horizontal;
}
}
}
if (horizontal)
positions.add(new Integer(end.y));
else
positions.add(new Integer(end.x));
processPositions(start, end, positions, startNormal.isHorizontal(), conn);
}
}