--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2011 Association for Decentralized Information Management in\r
+ * 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.diagram.connection;\r
+\r
+/**\r
+ * An internal utility class for routing simple connections \r
+ * (a connection with two terminals without any route lines).\r
+ * \r
+ * @author Hannu Niemistö\r
+ */\r
+class SimpleConnectionUtility {\r
+\r
+ public static boolean allowsDirection(RouteTerminal a, int dir) {\r
+ return Directions.isAllowed(a.getAllowedDirections(), dir);\r
+ }\r
+ \r
+ public static final int DIRECT_HORIZONTAL_CONNECTION = 0;\r
+ public static final int DIRECT_VERTICAL_CONNECTION = 1;\r
+ \r
+ public static final int ONE_BEND_HORIZONTAL_VERTICAL = 2;\r
+ public static final int ONE_BEND_VERTICAL_HORIZONTAL = 3;\r
+\r
+ public static final int MORE_BENDS_BBS_DONT_INTERSECT = 4;\r
+ public static final int MORE_BENDS_BBS_INTERSECT = 5;\r
+ \r
+ public static final int COMPLEX_CONNECTION = 6;\r
+ \r
+ public static int simpleConnectionCase(RouteTerminal a, RouteTerminal b) {\r
+ if(a.isRouteToBounds() && b.isRouteToBounds())\r
+ return simpleConnectionCaseRouteToBounds(a, b);\r
+ \r
+ // Can connect terminals by one straight line?\r
+ if(a.y == b.y) {\r
+ if(a.x < b.x) {\r
+ if(allowsDirection(a, 0) && allowsDirection(b, 2))\r
+ return DIRECT_HORIZONTAL_CONNECTION;\r
+ }\r
+ else {\r
+ if(allowsDirection(a, 2) && allowsDirection(b, 0))\r
+ return DIRECT_HORIZONTAL_CONNECTION;\r
+ }\r
+ }\r
+ else if(a.x == b.x) {\r
+ if(a.y < b.y) {\r
+ if(allowsDirection(a, 1) && allowsDirection(b, 3))\r
+ return DIRECT_VERTICAL_CONNECTION;\r
+ }\r
+ else {\r
+ if(allowsDirection(a, 3) && allowsDirection(b, 1))\r
+ return DIRECT_VERTICAL_CONNECTION;\r
+ }\r
+ }\r
+ \r
+ // Can connect terminals by two lines?\r
+ if(a.x < b.x) {\r
+ if(a.y < b.y) {\r
+ if(allowsDirection(a, 0) && allowsDirection(b, 3)\r
+ /*&& b.x >= a.getMaxX() && a.y <= b.getMinY()*/)\r
+ return ONE_BEND_HORIZONTAL_VERTICAL;\r
+ else if(allowsDirection(a, 1) && allowsDirection(b, 2)\r
+ /*&& b.y >= a.getMaxY() && a.x <= b.getMinX()*/)\r
+ return ONE_BEND_VERTICAL_HORIZONTAL;\r
+ }\r
+ else {\r
+ if(allowsDirection(a, 0) && allowsDirection(b, 1)\r
+ /*&& b.x >= a.getMaxX() && a.y >= b.getMaxY()*/)\r
+ return ONE_BEND_HORIZONTAL_VERTICAL;\r
+ else if(allowsDirection(a, 3) && allowsDirection(b, 2)\r
+ /*&& b.y <= a.getMinY() && a.x <= b.getMinX()*/)\r
+ return ONE_BEND_VERTICAL_HORIZONTAL;\r
+ }\r
+ }\r
+ else {\r
+ if(a.y < b.y) {\r
+ if(allowsDirection(a, 2) && allowsDirection(b, 3)\r
+ /*&& b.x <= a.getMinX() && a.y <= b.getMinY()*/)\r
+ return ONE_BEND_HORIZONTAL_VERTICAL;\r
+ else if(allowsDirection(a, 1) && allowsDirection(b, 0)\r
+ /*&& b.y >= a.getMaxY() && a.x >= b.getMaxX()*/)\r
+ return ONE_BEND_VERTICAL_HORIZONTAL;\r
+ }\r
+ else {\r
+ if(allowsDirection(a, 2) && allowsDirection(b, 1)\r
+ /*&& b.x <= a.getMinX() && a.y >= b.getMaxY()*/)\r
+ return ONE_BEND_HORIZONTAL_VERTICAL;\r
+ else if(allowsDirection(a, 3) && allowsDirection(b, 0)\r
+ /*&& b.y <= a.getMinY() && a.x >= b.getMaxX()*/)\r
+ return ONE_BEND_VERTICAL_HORIZONTAL;\r
+ }\r
+ }\r
+ \r
+ // Do bounding boxes intersect each other?\r
+ boolean boundingBoxesIntersect = !(\r
+ a.getMaxX() < b.getMinX() ||\r
+ a.getMinX() > b.getMaxX() ||\r
+ a.getMaxY() < b.getMinY() ||\r
+ a.getMinY() > b.getMaxY() \r
+ );\r
+ \r
+ if(boundingBoxesIntersect) {\r
+ // Can connect terminals by two lines if we ignore bounding boxes?\r
+ if(a.x < b.x) {\r
+ if(a.y < b.y) {\r
+ if(allowsDirection(a, 0) && allowsDirection(b, 3))\r
+ return ONE_BEND_HORIZONTAL_VERTICAL;\r
+ else if(allowsDirection(a, 1) && allowsDirection(b, 2))\r
+ return ONE_BEND_VERTICAL_HORIZONTAL;\r
+ }\r
+ else {\r
+ if(allowsDirection(a, 0) && allowsDirection(b, 1))\r
+ return ONE_BEND_HORIZONTAL_VERTICAL;\r
+ else if(allowsDirection(a, 3) && allowsDirection(b, 2))\r
+ return ONE_BEND_VERTICAL_HORIZONTAL;\r
+ }\r
+ }\r
+ else {\r
+ if(a.y < b.y) {\r
+ if(allowsDirection(a, 2) && allowsDirection(b, 3))\r
+ return ONE_BEND_HORIZONTAL_VERTICAL;\r
+ else if(allowsDirection(a, 1) && allowsDirection(b, 0))\r
+ return ONE_BEND_VERTICAL_HORIZONTAL;\r
+ }\r
+ else {\r
+ if(allowsDirection(a, 2) && allowsDirection(b, 1))\r
+ return ONE_BEND_HORIZONTAL_VERTICAL;\r
+ else if(allowsDirection(a, 3) && allowsDirection(b, 0))\r
+ return ONE_BEND_VERTICAL_HORIZONTAL;\r
+ }\r
+ }\r
+ \r
+ // Otherwise\r
+ return MORE_BENDS_BBS_INTERSECT;\r
+ } \r
+ \r
+ // Otherwise\r
+ return MORE_BENDS_BBS_DONT_INTERSECT;\r
+ }\r
+ \r
+ private static int simpleConnectionCaseRouteToBounds(RouteTerminal a,\r
+ RouteTerminal b) {\r
+ double aX = 0.5*(a.getMinX() + a.getMaxX());\r
+ double aY = 0.5*(a.getMinY() + a.getMaxY());\r
+ double bX = 0.5*(b.getMinX() + b.getMaxX());\r
+ double bY = 0.5*(b.getMinY() + b.getMaxY());\r
+ \r
+ double minY = Math.max(a.getMinY(), b.getMinY());\r
+ double maxY = Math.min(a.getMaxY(), b.getMaxY());\r
+ \r
+ if(minY <= maxY) {\r
+ double cY = 0.5*(minY+maxY);\r
+ a.setY(cY);\r
+ b.setY(cY);\r
+ if(aX < bX) {\r
+ a.setX(a.getMaxX());\r
+ b.setX(b.getMinX());\r
+ }\r
+ else {\r
+ a.setX(a.getMinX());\r
+ b.setX(b.getMaxX());\r
+ }\r
+ return DIRECT_HORIZONTAL_CONNECTION;\r
+ }\r
+ \r
+ double minX = Math.max(a.getMinX(), b.getMinX());\r
+ double maxX = Math.min(a.getMaxX(), b.getMaxX());\r
+ \r
+ if(minX <= maxX) {\r
+ double cX = 0.5*(minX+maxX);\r
+ a.setX(cX);\r
+ b.setX(cX);\r
+ if(aY < bY) {\r
+ a.setY(a.getMaxY());\r
+ b.setY(b.getMinY());\r
+ }\r
+ else {\r
+ a.setY(a.getMinY());\r
+ b.setY(b.getMaxY());\r
+ }\r
+ return DIRECT_VERTICAL_CONNECTION;\r
+ }\r
+ \r
+ {\r
+ a.setY(aY);\r
+ b.setX(bX);\r
+ if(aX < bX) {\r
+ a.setX(a.getMaxX());\r
+ }\r
+ else {\r
+ a.setX(a.getMinX());\r
+ }\r
+ if(aY < bY) {\r
+ b.setY(b.getMinY());\r
+ }\r
+ else {\r
+ b.setY(b.getMaxY());\r
+ }\r
+ return ONE_BEND_HORIZONTAL_VERTICAL;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Finds a route line for two route terminals.\r
+ */\r
+ public static RouteLine findRouteLine(RouteTerminal a, RouteTerminal b, boolean terminalsIntersect) {\r
+ if(terminalsIntersect) {\r
+ if(a.x < b.x) {\r
+ if((a.getAllowedDirections() & RouteTerminal.DIR_RIGHT) != 0 \r
+ && (b.getAllowedDirections() & RouteTerminal.DIR_LEFT) != 0) {\r
+ return new RouteLine(false, 0.5 * (a.x + b.x));\r
+ }\r
+ }\r
+ else {\r
+ if((a.getAllowedDirections() & RouteTerminal.DIR_LEFT) != 0 \r
+ && (b.getAllowedDirections() & RouteTerminal.DIR_RIGHT) != 0) {\r
+ return new RouteLine(false, 0.5 * (a.x + b.x));\r
+ }\r
+ }\r
+ if(a.y < b.y) {\r
+ if((a.getAllowedDirections() & RouteTerminal.DIR_DOWN) != 0 \r
+ && (b.getAllowedDirections() & RouteTerminal.DIR_UP) != 0) {\r
+ return new RouteLine(true, 0.5 * (a.y + b.y));\r
+ }\r
+ }\r
+ else {\r
+ if((a.getAllowedDirections() & RouteTerminal.DIR_UP) != 0 \r
+ && (b.getAllowedDirections() & RouteTerminal.DIR_DOWN) != 0) {\r
+ return new RouteLine(true, 0.5 * (a.y + b.y));\r
+ }\r
+ }\r
+ }\r
+ \r
+ //int aDir = Directions.firstAllowedDirection(a.getAllowedDirections());\r
+ //int bDir = Directions.firstAllowedDirection(b.getAllowedDirections());\r
+ \r
+ boolean isHorizontal = true;\r
+ double position = 0.0;\r
+ \r
+ loop:\r
+ for(int aDir=0;aDir<4;++aDir)\r
+ if(Directions.isAllowed(a.getAllowedDirections(), aDir))\r
+ for(int bDir=0;bDir<4;++bDir)\r
+ if(Directions.isAllowed(b.getAllowedDirections(), bDir)) {\r
+ // Connection starts to the same direction from the both terminals\r
+ if(aDir == bDir) {\r
+ isHorizontal = !isHorizontal(aDir);\r
+ if(!terminalsIntersect) {\r
+ if(dist(aDir, a, b) > 0 && isIn(aDir+1, a.x, a.y, b)) {\r
+ position = middle(aDir, a, b);\r
+ break loop;\r
+ }\r
+ else if(dist(aDir, b, a) > 0 && isIn(aDir+1, b.x, b.y, a)) {\r
+ position = middle(aDir, b, a);\r
+ break loop;\r
+ }\r
+ }\r
+ position = boundary(aDir, a, b);\r
+ }\r
+ // Connection starts horizontally from one terminal and\r
+ // vertically from another terminal\r
+ else if(((aDir ^ bDir)&1) == 1) {\r
+ if(dist(aDir, a, b) >= 0) {\r
+ isHorizontal = !isHorizontal(aDir);\r
+ position = middle(aDir, a, b);\r
+ break loop;\r
+ }\r
+ else if(dist(bDir, b, a) >= 0) {\r
+ isHorizontal = isHorizontal(aDir);;\r
+ position = middle(bDir, b, a);\r
+ break loop;\r
+ }\r
+ else if(firstIsBoundary(bDir, a, b)) {\r
+ isHorizontal = isHorizontal(aDir);\r
+ position = boundary(bDir, b, a);\r
+ }\r
+ else {\r
+ isHorizontal = !isHorizontal(aDir);\r
+ position = boundary(aDir, a, b);\r
+ }\r
+ }\r
+ // Connection starts to opposite directions from the terminals\r
+ else { \r
+ if(dist(aDir, a, b) >= 0.0) {\r
+ isHorizontal = !isHorizontal(aDir);\r
+ position = middle(aDir, a, b);\r
+ break loop;\r
+ }\r
+ else if(dist(aDir+1, a, b) >= 0.0) {\r
+ isHorizontal = isHorizontal(aDir);\r
+ position = middle(aDir+1, a, b);\r
+ break loop;\r
+ }\r
+ else if(dist(aDir-1, a, b) >= 0.0) {\r
+ isHorizontal = isHorizontal(aDir);\r
+ position = middle(aDir+1, a, b);\r
+ break loop;\r
+ }\r
+ else {\r
+ isHorizontal = isHorizontal(aDir);\r
+ double b1 = boundary(aDir+1, a, b);\r
+ double b2 = boundary(aDir-1, a, b);\r
+ double cost1, cost2;\r
+ if(isHorizontal) {\r
+ double da1 = a.y - b1;\r
+ double db1 = b.y - b1;\r
+ cost1 = da1*da1 + db1*db1;\r
+ double da2 = a.y - b2;\r
+ double db2 = b.y - b2;\r
+ cost2 = da2*da2 + db1*db2;\r
+ }\r
+ else {\r
+ double da1 = a.x - b1;\r
+ double db1 = b.x - b1;\r
+ cost1 = da1*da1 + db1*db1;\r
+ double da2 = a.x - b2;\r
+ double db2 = b.x - b2;\r
+ cost2 = da2*da2 + db1*db2;\r
+ }\r
+ position = cost1 <= cost2 ? b1 : b2;\r
+ }\r
+ }\r
+ }\r
+ return new RouteLine(isHorizontal, position);\r
+ }\r
+ \r
+ /**\r
+ * Computes the difference between two points to the given direction\r
+ */\r
+ public static double diff(int dir, double x1, double y1, double x2, double y2) {\r
+ switch(dir&3) {\r
+ case 0: return x1 - x2;\r
+ case 1: return y1 - y2;\r
+ case 2: return x2 - x1;\r
+ case 3: return y2 - y1;\r
+ default: throw new Error("Should not happen.");\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Computes the distance of the bounding boxes of the two route terminals\r
+ * to the given direction.\r
+ */\r
+ public static double dist(int dir, RouteTerminal a, RouteTerminal b) {\r
+ switch(dir&3) {\r
+ case 0: return b.getMinX() - a.getMaxX();\r
+ case 1: return b.getMinY() - a.getMaxY();\r
+ case 2: return a.getMinX() - b.getMaxX();\r
+ case 3: return a.getMinY() - b.getMaxY();\r
+ default: throw new Error("Should not happen.");\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Computes the middle point between two terminals in the given direction.\r
+ */\r
+ public static double middle(int dir, RouteTerminal a, RouteTerminal b) {\r
+ switch(dir&3) {\r
+ case 0: return 0.5*(b.getMinX() + a.getMaxX());\r
+ case 1: return 0.5*(b.getMinY() + a.getMaxY());\r
+ case 2: return 0.5*(a.getMinX() + b.getMaxX());\r
+ case 3: return 0.5*(a.getMinY() + b.getMaxY());\r
+ default: throw new Error("Should not happen.");\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Tests whether the point is inside the bounding box of the terminal\r
+ * in the given direction.\r
+ */\r
+ public static boolean isIn(int dir, double x, double y, RouteTerminal a) {\r
+ if((dir&1) == 0)\r
+ return a.getMinX() < x && x < a.getMaxX();\r
+ else\r
+ return a.getMinY() < y && y < a.getMaxY();\r
+ }\r
+ \r
+ public static boolean isHorizontal(int dir) {\r
+ return (dir&1) == 0;\r
+ }\r
+ \r
+ /**\r
+ * Gives the boundary of the route terminal in the given direction.\r
+ */\r
+ public static double boundary(int dir, RouteTerminal a) {\r
+ switch(dir&3) {\r
+ case 0: return a.getMaxX();\r
+ case 1: return a.getMaxY();\r
+ case 2: return a.getMinX();\r
+ case 3: return a.getMinY();\r
+ default: throw new Error("Should not happen.");\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Gives the boundary of two route terminals in the given direction.\r
+ */\r
+ public static double boundary(int dir, RouteTerminal a, RouteTerminal b) {\r
+ switch(dir&3) {\r
+ case 0: return Math.max(a.getMaxX(), b.getMaxX());\r
+ case 1: return Math.max(a.getMaxY(), b.getMaxY());\r
+ case 2: return Math.min(a.getMinX(), b.getMinX());\r
+ case 3: return Math.min(a.getMinY(), b.getMinY());\r
+ default: throw new Error("Should not happen.");\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Returns true if the first terminal is farther away in the given direction.\r
+ */\r
+ public static boolean firstIsBoundary(int dir, RouteTerminal a, RouteTerminal b) {\r
+ switch(dir&3) {\r
+ case 0: return a.getMaxX() >= b.getMaxX();\r
+ case 1: return a.getMaxY() >= b.getMaxY();\r
+ case 2: return a.getMinX() <= b.getMinX();\r
+ case 3: return a.getMinY() <= b.getMinY();\r
+ default: throw new Error("Should not happen.");\r
+ }\r
+ }\r
+ \r
+}\r