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