Mercurial > projects > dwt-addons
diff dwtx/draw2d/ManhattanConnectionRouter.d @ 98:95307ad235d9
Added Draw2d code, still work in progress
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 03 Aug 2008 00:52:14 +0200 |
parents | |
children | 2d6540440fe6 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/ManhattanConnectionRouter.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,394 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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 + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.draw2d.ManhattanConnectionRouter; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.geometry.Point; +import dwtx.draw2d.geometry.PointList; +import dwtx.draw2d.geometry.Ray; +import dwtx.draw2d.geometry.Rectangle; +import dwtx.draw2d.AbstractRouter; +import dwtx.draw2d.Connection; +import dwtx.draw2d.ConnectionAnchor; + +/** + * Provides a {@link Connection} with an orthogonal route between the Connection's source + * and target anchors. + */ +public final class ManhattanConnectionRouter + : AbstractRouter +{ + +private Map rowsUsed; +private Map colsUsed; +//private Hashtable offsets = new Hashtable(7); + +private Map reservedInfo; + +private class ReservedInfo { + public List reservedRows; + public List reservedCols; + this(){ + reservedRows = new ArrayList(2); + reservedCols = new ArrayList(2); + } +} + +private static Ray UP, DOWN, LEFT, RIGHT; + + +static this(){ + UP = new Ray(0, -1); + DOWN = new Ray(0, 1); + LEFT = new Ray(-1, 0); + RIGHT = new Ray(1, 0); +} + +public this(){ + rowsUsed = new HashMap(); + colsUsed = new HashMap(); + reservedInfo = new HashMap(); +} + +/** + * @see 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 is 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 is 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() is 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 is 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 is 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() is 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, + bool 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] = (cast(Integer)positions.get(i)).intValue(); + } + if (horizontal is (positions.size() % 2 is 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; + bool adjust; + for (i = 2; i < pos.length - 1; i++) { + horizontal = !horizontal; + prev = pos[i - 1]; + current = pos[i]; + + adjust = (i !is 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) + */ +public void remove(Connection connection) { + removeReservedLines(connection); +} + +protected void removeReservedLines(Connection connection) { + ReservedInfo rInfo = cast(ReservedInfo) reservedInfo.get(cast(Object)connection); + if (rInfo is 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(cast(Object)connection); +} + +protected void reserveColumn(Connection connection, Integer column) { + ReservedInfo info = cast(ReservedInfo) reservedInfo.get(cast(Object)connection); + if (info is null) { + info = new ReservedInfo(); + reservedInfo.put(cast(Object)connection, info); + } + info.reservedCols.add(column); +} + +protected void reserveRow(Connection connection, Integer row) { + ReservedInfo info = cast(ReservedInfo) reservedInfo.get(cast(Object)connection); + if (info is null) { + info = new ReservedInfo(); + reservedInfo.put(cast(Object)connection, info); + } + info.reservedRows.add(row); +} + +/** + * @see ConnectionRouter#route(Connection) + */ +public void route(Connection conn) { + if ((conn.getSourceAnchor() is null) || (conn.getTargetAnchor() is 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 = getStartDirection(conn); + Ray endNormal = getEndDirection(conn); + + List positions = new ArrayList(5); + bool horizontal = startNormal.isHorizontal(); + if (horizontal) + positions.add(new Integer(start.y)); + else + positions.add(new Integer(start.x)); + horizontal = !horizontal; + + if (startNormal.dotProduct(endNormal) is 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); +} + +}