/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.g2d.routing.algorithm2; import java.awt.geom.Path2D; import org.simantics.g2d.routing.Constants; public class LocalRouter { static final double CORNER_SIZE = 1.0; boolean roundCorners; double aMinX; double aMinY; double aMaxX; double aMaxY; double bMinX; double bMinY; double bMaxX; double bMaxY; double sx; double sy; double tx; double ty; int sourceDirection; int targetDirection; Path2D path; public LocalRouter(boolean roundCorners) { this.roundCorners = roundCorners; } /** * Case where both source and target connection directions * are to east. */ void routeEast() { if(bMinX >= aMaxX || tx >= 0 && !(bMaxY < aMinY || aMaxY < bMinY)) { if(ty != 0.0) { /* ______ ______ * | | | | * | |----\ | | * | | \--->| | * |______| |______| */ double mx = 0.5 * (aMaxX + bMinX); point(mx, 0.0); point(mx, ty); } else ; // Just a straight line } else { double x0 = bMinX; double x1 = aMaxX; double my; /* ______ * | | * | | * /->| | * | |______| * | * \-------------\ * ______ | * | | | * | |-/ * | | * |______| * * If the elements are separated in Y-direction, * route between the elements (this is always the shortest path). */ if(bMaxY < aMinY) my = 0.5 * (aMinY + bMaxY); else if(aMaxY < bMinY) my = 0.5 * (aMaxY + bMinY); else { /* * /------------------------\ * | ______ ______ | * | | | | | | * | | | | |--+ * +->| | | | | * | |______| |______| | * | | * \------------------------/ * * or * * /-----------\ * | ______ | * | | | | * | | | | * /--+->| | | * | ___|______| | * | | | | * | | |-+---/ * | | | | * | |______| | * | | * \----------/ * * We may choose either lower or upper path. */ double upperX0 = bMinX; double upperX1 = aMaxX; double lowerX0 = bMinX; double lowerX1 = aMaxX; double upperY = Math.min(aMinY, bMinY); double lowerY = Math.max(aMaxY, bMaxY); if(aMinX < bMinX) { if(ty < 0.5 * (aMinY + aMaxY)) lowerX0 = aMinX; else upperX0 = aMinX; } if(bMaxX > aMaxX) { if(ty < 0.5 * (aMinY + aMaxY)) upperX1 = bMaxX; else lowerX1 = bMaxX; } double upperLength = upperX1 - upperY + (upperX1 - upperX0) + (ty - upperY) + (tx - upperX0); double lowerLength = lowerX1 + lowerY + (lowerX1 - lowerX0) + (lowerY - ty) + (tx - lowerX0); if(upperLength < lowerLength) { x0 = upperX0; x1 = upperX1; my = upperY; } else { x0 = lowerX0; x1 = lowerX1; my = lowerY; } } point(x1, 0.0); point(x1, my); point(x0, my); point(x0, ty); } } void routeWest() { if(tx >= 0.0) { double fx = Math.max(aMaxX, bMaxX); double mx = 0.5 * (aMaxX + bMinX); if(bMinY >= 0.0 || bMaxY <= 0.0 || mx < 0.0) { /* ______ * | | * | | * | |<-\ * ______ |______| | * | | | * | |-------------------/ * | | * |______| */ point(fx, 0.0); } else { /* /-------------\ * | ______ | * | | | | * ______ | | | | * | | | | |<-+ * | |----+ |______| | * | | | | * |______| \-------------/ * * We may choose either upper or lower path * by the path length. */ double my = Math.abs(bMinY) + Math.abs(ty - bMinY) < Math.abs(bMaxY) + Math.abs(ty - bMaxY) ? bMinY : bMaxY; point(mx, 0.0); point(mx, my); point(fx, my); } point(fx, ty); } else { double fx = Math.max(aMaxX, bMaxX); double mx = 0.5 * (aMinX + bMaxX); point(fx, 0.0); if(ty <= aMinY || ty >= aMaxY || (tx >= mx && ty >= aMinY && ty <= aMaxY)) { /* ______ * | | * | | * | |--\ * ______ |______| | * | | | * | |<------------------/ * | | * |______| */ point(fx, ty); } else { /* /-------------\ * | ______ | * | | | | * ______ | | | | * | | | | |--+ * | |<---+ |______| | * | | | | * |______| \-------------/ * * We may choose either upper or lower path * by the path length. */ double my = Math.abs(aMinY) + Math.abs(ty - aMinY) < Math.abs(aMaxY) + Math.abs(ty - aMaxY) ? aMinY : aMaxY; point(fx, my); point(mx, my); point(mx, ty); } } } void routeSouth() { if(tx > 0.0 && (bMinY >= 0.0 || (ty > 0.0 && bMinX <= aMaxX))) point(tx, 0.0); else if(bMinX > aMaxX) { double mx = 0.5 * (aMaxX + bMinX); point(mx, 0.0); point(mx, bMinY); point(tx, bMinY); } else { double fx = aMaxX; double my = 0.5 * (aMaxY + bMinY); if(my < aMaxY && (tx < aMinX || ty < aMinY)) { my = Math.min(aMinY, bMinY); if(bMaxX > aMaxX) fx = bMaxX; } point(fx, 0.0); point(fx, my); point(tx, my); } } double xx, xy, yx, yy; void point(double x, double y) { if (roundCorners) { continuePath(x*xx + y*yx + sx, x*xy + y*yy + sy); } else { path.lineTo(x*xx + y*yx + sx, x*xy + y*yy + sy); } } double prevX, prevY, curX, curY, nextX, nextY; boolean first; void beginPath(double x, double y) { path.moveTo(x, y); nextX = x; nextY = y; first = true; } static double dd(double d) { if(Math.abs(d) < CORNER_SIZE*2.0) return d*0.5; else return Math.signum(d)*CORNER_SIZE; } void continuePath(double x, double y) { prevX = curX; prevY = curY; curX = nextX; curY = nextY; nextX = x; nextY = y; if(first) first = false; else { double prevDeltaX = dd(prevX - curX); double prevDeltaY = dd(prevY - curY); double nextDeltaX = dd(nextX - curX); double nextDeltaY = dd(nextY - curY); path.lineTo(curX+prevDeltaX, curY+prevDeltaY); path.quadTo( curX, curY, curX+nextDeltaX, curY+nextDeltaY); //path.lineTo(curX+nextDeltaX, curY+nextDeltaY); //path.lineTo(curX, curY); } } void endPath() { path.lineTo(nextX, nextY); } void rotate() { double temp; temp = tx; tx = ty; ty = -temp; temp = aMinX; aMinX = aMinY; aMinY = -aMaxX; aMaxX = aMaxY; aMaxY = -temp; temp = bMinX; bMinX = bMinY; bMinY = -bMaxX; bMaxX = bMaxY; bMaxY = -temp; temp = xx; xx = -xy; xy = temp; temp = yx; yx = -yy; yy = temp; --targetDirection; if(targetDirection < 0) targetDirection += 4; --sourceDirection; } void flip() { double temp; ty = -ty; temp = aMinY; aMinY = -aMaxY; aMaxY = -temp; temp = bMinY; bMinY = -bMaxY; bMaxY = -temp; yx = -yx; yy = -yy; targetDirection = (targetDirection + 2) % 4; } /* * Puts source terminal to origo and rotates the situation * so that the connection leaves to east. Finally, the * case where target direction is to south is eliminated * by optionally flipping the situation. */ void canonicalize() { aMinX -= sx; aMinY -= sy; aMaxX -= sx; aMaxY -= sy; bMinX -= sx; bMinY -= sy; bMaxX -= sx; bMaxY -= sy; tx -= sx; ty -= sy; xx = yy = 1.0; xy = yx = 0.0; while(sourceDirection > 0) rotate(); if(targetDirection == Constants.SOUTH) flip(); } public void route() { //System.out.println("route"); //System.out.println(sourceDirection + " " + targetDirection); path = new Path2D.Double(); if (roundCorners) { beginPath(sx, sy); if(bMinX < sx && sx < bMaxX && bMinY < sy && sy < bMaxY) targetDirection = (targetDirection+2)&3; if(aMinX < tx && tx < aMaxX && aMinY < ty && ty < aMaxY) sourceDirection = (sourceDirection+2)&3; // // Vertical and horizontal cases // if(Math.abs(sx - tx) < 1e-5 || Math.abs(sy - ty) < 1e-5) { // path.lineTo(tx, ty); // return; // } } else { path.moveTo(sx, sy); // Vertical and horizontal cases // TODO Removed this seemingly incorrect code (they were already disabled in roundCorners case /*if(Math.abs(sx - tx) < 1e-5 || Math.abs(sy - ty) < 1e-5) { path.lineTo(tx, ty); return; }*/ } canonicalize(); switch(targetDirection) { case Constants.EAST: routeWest(); break; case Constants.WEST: routeEast(); break; case Constants.NORTH: routeSouth(); break; } point(tx, ty); if (roundCorners) { endPath(); } //System.out.println(sourceDirection + " " + targetDirection); } }