/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.diagram.ui.connection; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; 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; /** * Provides a customized {@link Connection} with an orthogonal route between the connection's source and target anchors. This * class provides means to pad the offset of the routed lines outside of * * @since 8.0 */ public final class BlockConnectionRouter extends AbstractRouter { private int offsetPadding = 10; private Map<Integer, Integer> usedRows = new HashMap<Integer, Integer>(); private Map<Integer, Integer> usedColumns = new HashMap<Integer, Integer>(); private Map<Connection, ReservedInfo> reservedInfo = new HashMap<Connection, ReservedInfo>(); class ReservedInfo { public List<Integer> reservedRows = new ArrayList<Integer>(2); public List<Integer> reservedColumns = new ArrayList<Integer>(2); } private static Ray DIR_UP = new Ray(0, -1), DIR_DOWN = new Ray(0, 1), DIR_LEFT = new Ray(-1, 0), DIR_RIGHT = new Ray(1, 0); /** * Create with initial offsetPadding * * @param offsetPadding */ public BlockConnectionRouter( int offsetPadding ) { super(); this.offsetPadding = offsetPadding; } /** * @see ConnectionRouter#invalidate(Connection) */ @Override public void invalidate( Connection connection ) { removeReservedLines(connection); } /** * Returns the direction of the point <i>p</i> in relation to the given rectangle. Possible values are DIR_LEFT (-1,0), * DIR_RIGHT (1,0), DIR_UP (0,-1) and DIR_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 = DIR_LEFT; i = Math.abs(r.y - p.y); if (i <= distance) { distance = i; direction = DIR_UP; } i = Math.abs(r.bottom() - p.y); if (i <= distance) { distance = i; direction = DIR_DOWN; } i = Math.abs(r.right() - p.x); if (i < distance) { distance = i; direction = DIR_RIGHT; } return direction; } private int getNearestColumn( 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 proxVal = 0; int dirVal = -1; if (r % 2 == 1) r--; Integer i; while (proxVal < r) { i = new Integer(r + proxVal * dirVal); if (!usedColumns.containsKey(i)) { usedColumns.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 (dirVal == 1) dirVal = -1; else { dirVal = 1; proxVal += 2; } } return r; } 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 getNearestRow( 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 proxVal = 0; int dirVal = -1; if (r % 2 == 1) r--; Integer i; while (proxVal < r) { i = new Integer(r + proxVal * dirVal); if (!usedRows.containsKey(i)) { usedRows.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 (dirVal == 1) dirVal = -1; else { dirVal = 1; proxVal += 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); } /** * Process all positions and reset routing points * * @param start * @param end * @param positions * @param horizontalState * @param conn */ protected void processFinalPositions( Ray start, Ray end, List positions, boolean horizontalState, Connection conn ) { removeReservedLines(conn); int pos[] = new int[positions.size() + 2]; int i; if (horizontalState) pos[0] = start.x; else pos[0] = start.y; for (i = 0; i < positions.size(); i++) { pos[i + 1] = ((Integer)positions.get(i)).intValue(); } if (horizontalState == (positions.size() % 2 == 1)) { pos[++i] = end.x; } else { pos[++i] = end.y; } PointList routerPoints = new PointList(); Point newPoint; int currentPosition, previousPosition, minPosition, maxPosition; boolean adjust; routerPoints.addPoint(new Point(start.x, start.y)); for (i = 2; i < pos.length - 1; i++) { horizontalState = !horizontalState; previousPosition = pos[i - 1]; currentPosition = pos[i]; adjust = (i != pos.length - 2); if (horizontalState) { if (adjust) { minPosition = pos[i - 2]; maxPosition = pos[i + 2]; pos[i] = currentPosition = getNearestRow(conn, currentPosition, minPosition, maxPosition); } newPoint = new Point(previousPosition, currentPosition); } else { if (adjust) { minPosition = pos[i - 2]; maxPosition = pos[i + 2]; pos[i] = currentPosition = getNearestColumn(conn, currentPosition, minPosition, maxPosition); } newPoint = new Point(currentPosition, previousPosition); } routerPoints.addPoint(newPoint); } routerPoints.addPoint(new Point(end.x, end.y)); conn.setPoints(routerPoints); } /** * @see ConnectionRouter#remove(Connection) */ @Override public void remove( Connection connection ) { removeReservedLines(connection); } protected void reserveColumn( Connection connection, Integer column ) { ReservedInfo info = reservedInfo.get(connection); if (info == null) { info = new ReservedInfo(); reservedInfo.put(connection, info); } info.reservedColumns.add(column); } protected void reserveRow( Connection connection, Integer row ) { ReservedInfo info = reservedInfo.get(connection); if (info == null) { info = new ReservedInfo(); reservedInfo.put(connection, info); } info.reservedRows.add(row); } protected void removeReservedLines( Connection connection ) { ReservedInfo rInfo = reservedInfo.get(connection); if (rInfo == null) return; for (int i = 0; i < rInfo.reservedRows.size(); i++) { usedRows.remove(rInfo.reservedRows.get(i)); } for (int i = 0; i < rInfo.reservedColumns.size(); i++) { usedColumns.remove(rInfo.reservedColumns.get(i)); } reservedInfo.remove(connection); } /** * Create all route points. * * @see ConnectionRouter#route(Connection) */ @Override public void route( Connection conn ) { if ((conn.getTargetAnchor() == null) || (conn.getSourceAnchor() == null)) { return; } int i; Point startingPt = getStartPoint(conn); conn.translateToRelative(startingPt); Point endPt = getEndPoint(conn); conn.translateToRelative(endPt); Ray startRay = new Ray(startingPt); Ray endRay = new Ray(endPt); Ray averageRay = startRay.getAveraged(endRay); Ray direction = new Ray(startRay, endRay); Ray startingNormal = getStartDirection(conn); Ray endingNormal = getEndDirection(conn); Vector<Integer> positions = new Vector<Integer>(5); boolean horizontalState = startingNormal.isHorizontal(); if (horizontalState) { positions.add(new Integer(startRay.y)); } else { positions.add(new Integer(startRay.x)); } horizontalState = !horizontalState; if (startingNormal.dotProduct(endingNormal) == 0) { if ((startingNormal.dotProduct(direction) >= 0) && (endingNormal.dotProduct(direction) <= 0)) { // 0 only // Do nothing } else { // 2 only if (startingNormal.dotProduct(direction) < 0) i = startingNormal.similarity(startRay.getAdded(startingNormal.getScaled(offsetPadding))); else { if (horizontalState) i = averageRay.y; else i = averageRay.x; } positions.add(new Integer(i)); horizontalState = !horizontalState; if (endingNormal.dotProduct(direction) > 0) { i = endingNormal.similarity(endRay.getAdded(endingNormal.getScaled(offsetPadding))); } else { if (horizontalState) { i = averageRay.y; } else { i = averageRay.x; } } positions.add(new Integer(i)); horizontalState = !horizontalState; } } else { if (startingNormal.dotProduct(endingNormal) > 0) { // 1 only if (startingNormal.dotProduct(direction) >= 0) { i = startingNormal.similarity(startRay.getAdded(startingNormal.getScaled(offsetPadding))); } else { i = endingNormal.similarity(endRay.getAdded(endingNormal.getScaled(offsetPadding))); } positions.add(new Integer(i)); horizontalState = !horizontalState; } else { // 3 or 1 if (startingNormal.dotProduct(direction) < 0) { i = startingNormal.similarity(startRay.getAdded(startingNormal.getScaled(offsetPadding))); positions.add(new Integer(i)); horizontalState = !horizontalState; } if (horizontalState) { i = averageRay.y; } else { i = averageRay.x; } positions.add(new Integer(i)); horizontalState = !horizontalState; if (startingNormal.dotProduct(direction) < 0) { i = endingNormal.similarity(endRay.getAdded(endingNormal.getScaled(offsetPadding))); positions.add(new Integer(i)); horizontalState = !horizontalState; } } } if (horizontalState) { positions.add(new Integer(endRay.y)); } else { positions.add(new Integer(endRay.x)); } processFinalPositions(startRay, endRay, positions, startingNormal.isHorizontal(), conn); } }