]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/SimpleConnectionUtility.java
4f9b94f77e452df5e0756a4ed1874ccd75f04ecc
[simantics/platform.git] / bundles / org.simantics.diagram.connection / src / org / simantics / diagram / connection / SimpleConnectionUtility.java
1 /*******************************************************************************\r
2  * Copyright (c) 2011 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.diagram.connection;\r
13 \r
14 /**\r
15  * An internal utility class for routing simple connections \r
16  * (a connection with two terminals without any route lines).\r
17  * \r
18  * @author Hannu Niemistö\r
19  */\r
20 class SimpleConnectionUtility {\r
21 \r
22     public static boolean allowsDirection(RouteTerminal a, int dir) {\r
23         return Directions.isAllowed(a.getAllowedDirections(), dir);\r
24     }\r
25     \r
26     public static final int DIRECT_HORIZONTAL_CONNECTION = 0;\r
27     public static final int DIRECT_VERTICAL_CONNECTION = 1;\r
28     \r
29     public static final int ONE_BEND_HORIZONTAL_VERTICAL = 2;\r
30     public static final int ONE_BEND_VERTICAL_HORIZONTAL = 3;\r
31 \r
32     public static final int MORE_BENDS_BBS_DONT_INTERSECT = 4;\r
33     public static final int MORE_BENDS_BBS_INTERSECT = 5;\r
34     \r
35     public static final int COMPLEX_CONNECTION = 6;\r
36     \r
37     public static int simpleConnectionCase(RouteTerminal a, RouteTerminal b) {\r
38         if(a.isRouteToBounds() && b.isRouteToBounds())\r
39             return simpleConnectionCaseRouteToBounds(a, b);\r
40                     \r
41         // Can connect terminals by one straight line?\r
42         if(a.y == b.y) {\r
43             if(a.x < b.x) {\r
44                 if(allowsDirection(a, 0) && allowsDirection(b, 2))\r
45                     return DIRECT_HORIZONTAL_CONNECTION;\r
46             }\r
47             else {\r
48                 if(allowsDirection(a, 2) && allowsDirection(b, 0))\r
49                     return DIRECT_HORIZONTAL_CONNECTION;\r
50             }\r
51         }\r
52         else if(a.x == b.x) {\r
53             if(a.y < b.y) {\r
54                 if(allowsDirection(a, 1) && allowsDirection(b, 3))\r
55                     return DIRECT_VERTICAL_CONNECTION;\r
56             }\r
57             else {\r
58                 if(allowsDirection(a, 3) && allowsDirection(b, 1))\r
59                     return DIRECT_VERTICAL_CONNECTION;\r
60             }\r
61         }\r
62         \r
63         // Can connect terminals by two lines?\r
64         if(a.x < b.x) {\r
65             if(a.y < b.y) {\r
66                 if(allowsDirection(a, 0) && allowsDirection(b, 3)\r
67                         /*&& b.x >= a.getMaxX() && a.y <= b.getMinY()*/)\r
68                     return ONE_BEND_HORIZONTAL_VERTICAL;\r
69                 else if(allowsDirection(a, 1) && allowsDirection(b, 2)\r
70                         /*&& b.y >= a.getMaxY() && a.x <= b.getMinX()*/)\r
71                     return ONE_BEND_VERTICAL_HORIZONTAL;\r
72             }\r
73             else {\r
74                 if(allowsDirection(a, 0) && allowsDirection(b, 1)\r
75                         /*&& b.x >= a.getMaxX() && a.y >= b.getMaxY()*/)\r
76                     return ONE_BEND_HORIZONTAL_VERTICAL;\r
77                 else if(allowsDirection(a, 3) && allowsDirection(b, 2)\r
78                         /*&& b.y <= a.getMinY() && a.x <= b.getMinX()*/)\r
79                     return ONE_BEND_VERTICAL_HORIZONTAL;\r
80             }\r
81         }\r
82         else {\r
83             if(a.y < b.y) {\r
84                 if(allowsDirection(a, 2) && allowsDirection(b, 3)\r
85                         /*&& b.x <= a.getMinX() && a.y <= b.getMinY()*/)\r
86                     return ONE_BEND_HORIZONTAL_VERTICAL;\r
87                 else if(allowsDirection(a, 1) && allowsDirection(b, 0)\r
88                         /*&& b.y >= a.getMaxY() && a.x >= b.getMaxX()*/)\r
89                     return ONE_BEND_VERTICAL_HORIZONTAL;\r
90             }\r
91             else {\r
92                 if(allowsDirection(a, 2) && allowsDirection(b, 1)\r
93                         /*&& b.x <= a.getMinX() && a.y >= b.getMaxY()*/)\r
94                     return ONE_BEND_HORIZONTAL_VERTICAL;\r
95                 else if(allowsDirection(a, 3) && allowsDirection(b, 0)\r
96                         /*&& b.y <= a.getMinY() && a.x >= b.getMaxX()*/)\r
97                     return ONE_BEND_VERTICAL_HORIZONTAL;\r
98             }\r
99         }\r
100         \r
101         // Do bounding boxes intersect each other?\r
102         boolean boundingBoxesIntersect = !(\r
103                 a.getMaxX() < b.getMinX() ||\r
104                 a.getMinX() > b.getMaxX() ||\r
105                 a.getMaxY() < b.getMinY() ||\r
106                 a.getMinY() > b.getMaxY() \r
107                 );\r
108         \r
109         if(boundingBoxesIntersect) {\r
110             // Can connect terminals by two lines if we ignore bounding boxes?\r
111             if(a.x < b.x) {\r
112                 if(a.y < b.y) {\r
113                     if(allowsDirection(a, 0) && allowsDirection(b, 3))\r
114                         return ONE_BEND_HORIZONTAL_VERTICAL;\r
115                     else if(allowsDirection(a, 1) && allowsDirection(b, 2))\r
116                         return ONE_BEND_VERTICAL_HORIZONTAL;\r
117                 }\r
118                 else {\r
119                     if(allowsDirection(a, 0) && allowsDirection(b, 1))\r
120                         return ONE_BEND_HORIZONTAL_VERTICAL;\r
121                     else if(allowsDirection(a, 3) && allowsDirection(b, 2))\r
122                         return ONE_BEND_VERTICAL_HORIZONTAL;\r
123                 }\r
124             }\r
125             else {\r
126                 if(a.y < b.y) {\r
127                     if(allowsDirection(a, 2) && allowsDirection(b, 3))\r
128                         return ONE_BEND_HORIZONTAL_VERTICAL;\r
129                     else if(allowsDirection(a, 1) && allowsDirection(b, 0))\r
130                         return ONE_BEND_VERTICAL_HORIZONTAL;\r
131                 }\r
132                 else {\r
133                     if(allowsDirection(a, 2) && allowsDirection(b, 1))\r
134                         return ONE_BEND_HORIZONTAL_VERTICAL;\r
135                     else if(allowsDirection(a, 3) && allowsDirection(b, 0))\r
136                         return ONE_BEND_VERTICAL_HORIZONTAL;\r
137                 }\r
138             }\r
139             \r
140             // Otherwise\r
141             return MORE_BENDS_BBS_INTERSECT;\r
142         }        \r
143         \r
144         // Otherwise\r
145         return MORE_BENDS_BBS_DONT_INTERSECT;\r
146     }\r
147     \r
148     private static int simpleConnectionCaseRouteToBounds(RouteTerminal a,\r
149             RouteTerminal b) {\r
150         double aX = 0.5*(a.getMinX() + a.getMaxX());\r
151         double aY = 0.5*(a.getMinY() + a.getMaxY());\r
152         double bX = 0.5*(b.getMinX() + b.getMaxX());\r
153         double bY = 0.5*(b.getMinY() + b.getMaxY());\r
154         \r
155         double minY = Math.max(a.getMinY(), b.getMinY());\r
156         double maxY = Math.min(a.getMaxY(), b.getMaxY());\r
157         \r
158         if(minY <= maxY) {\r
159             double cY = 0.5*(minY+maxY);\r
160             a.setY(cY);\r
161             b.setY(cY);\r
162             if(aX < bX) {\r
163                 a.setX(a.getMaxX());\r
164                 b.setX(b.getMinX());\r
165             }\r
166             else {\r
167                 a.setX(a.getMinX());\r
168                 b.setX(b.getMaxX());\r
169             }\r
170             return DIRECT_HORIZONTAL_CONNECTION;\r
171         }\r
172         \r
173         double minX = Math.max(a.getMinX(), b.getMinX());\r
174         double maxX = Math.min(a.getMaxX(), b.getMaxX());\r
175         \r
176         if(minX <= maxX) {\r
177             double cX = 0.5*(minX+maxX);\r
178             a.setX(cX);\r
179             b.setX(cX);\r
180             if(aY < bY) {\r
181                 a.setY(a.getMaxY());\r
182                 b.setY(b.getMinY());\r
183             }\r
184             else {\r
185                 a.setY(a.getMinY());\r
186                 b.setY(b.getMaxY());\r
187             }\r
188             return DIRECT_VERTICAL_CONNECTION;\r
189         }\r
190         \r
191         {\r
192             a.setY(aY);\r
193             b.setX(bX);\r
194             if(aX < bX) {\r
195                 a.setX(a.getMaxX());\r
196             }\r
197             else {\r
198                 a.setX(a.getMinX());\r
199             }\r
200             if(aY < bY) {\r
201                 b.setY(b.getMinY());\r
202             }\r
203             else {\r
204                 b.setY(b.getMaxY());\r
205             }\r
206             return ONE_BEND_HORIZONTAL_VERTICAL;\r
207         }\r
208     }\r
209 \r
210     /**\r
211      * Finds a route line for two route terminals.\r
212      */\r
213     public static RouteLine findRouteLine(RouteTerminal a, RouteTerminal b, boolean terminalsIntersect) {\r
214         if(terminalsIntersect) {\r
215             if(a.x < b.x) {\r
216                 if((a.getAllowedDirections() & RouteTerminal.DIR_RIGHT) != 0 \r
217                         && (b.getAllowedDirections() & RouteTerminal.DIR_LEFT) != 0) {\r
218                     return new RouteLine(false, 0.5 * (a.x + b.x));\r
219                 }\r
220             }\r
221             else {\r
222                 if((a.getAllowedDirections() & RouteTerminal.DIR_LEFT) != 0 \r
223                         && (b.getAllowedDirections() & RouteTerminal.DIR_RIGHT) != 0) {\r
224                     return new RouteLine(false, 0.5 * (a.x + b.x));\r
225                 }\r
226             }\r
227             if(a.y < b.y) {\r
228                 if((a.getAllowedDirections() & RouteTerminal.DIR_DOWN) != 0 \r
229                         && (b.getAllowedDirections() & RouteTerminal.DIR_UP) != 0) {\r
230                     return new RouteLine(true, 0.5 * (a.y + b.y));\r
231                 }\r
232             }\r
233             else {\r
234                 if((a.getAllowedDirections() & RouteTerminal.DIR_UP) != 0 \r
235                         && (b.getAllowedDirections() & RouteTerminal.DIR_DOWN) != 0) {\r
236                     return new RouteLine(true, 0.5 * (a.y + b.y));\r
237                 }\r
238             }\r
239         }\r
240         \r
241         //int aDir = Directions.firstAllowedDirection(a.getAllowedDirections());\r
242         //int bDir = Directions.firstAllowedDirection(b.getAllowedDirections());\r
243         \r
244         boolean isHorizontal = true;\r
245         double position = 0.0;\r
246         \r
247         loop:\r
248         for(int aDir=0;aDir<4;++aDir)\r
249             if(Directions.isAllowed(a.getAllowedDirections(), aDir))\r
250                 for(int bDir=0;bDir<4;++bDir)\r
251                     if(Directions.isAllowed(b.getAllowedDirections(), bDir)) {\r
252                         // Connection starts to the same direction from the both terminals\r
253                         if(aDir == bDir) {\r
254                             isHorizontal = !isHorizontal(aDir);\r
255                             if(!terminalsIntersect) {\r
256                                 if(dist(aDir, a, b) > 0 && isIn(aDir+1, a.x, a.y, b)) {\r
257                                     position = middle(aDir, a, b);\r
258                                     break loop;\r
259                                 }\r
260                                 else if(dist(aDir, b, a) > 0 && isIn(aDir+1, b.x, b.y, a)) {\r
261                                     position = middle(aDir, b, a);\r
262                                     break loop;\r
263                                 }\r
264                             }\r
265                             position = boundary(aDir, a, b);\r
266                         }\r
267                         // Connection starts horizontally from one terminal and\r
268                         // vertically from another terminal\r
269                         else if(((aDir ^ bDir)&1) == 1) {\r
270                             if(dist(aDir, a, b) >= 0) {\r
271                                 isHorizontal = !isHorizontal(aDir);\r
272                                 position = middle(aDir, a, b);\r
273                                 break loop;\r
274                             }\r
275                             else if(dist(bDir, b, a) >= 0) {\r
276                                 isHorizontal = isHorizontal(aDir);;\r
277                                 position = middle(bDir, b, a);\r
278                                 break loop;\r
279                             }\r
280                             else if(firstIsBoundary(bDir, a, b)) {\r
281                                 isHorizontal = isHorizontal(aDir);\r
282                                 position = boundary(bDir, b, a);\r
283                             }\r
284                             else {\r
285                                 isHorizontal = !isHorizontal(aDir);\r
286                                 position = boundary(aDir, a, b);\r
287                             }\r
288                         }\r
289                         // Connection starts to opposite directions from the terminals\r
290                         else {           \r
291                             if(dist(aDir, a, b) >= 0.0) {\r
292                                 isHorizontal = !isHorizontal(aDir);\r
293                                 position = middle(aDir, a, b);\r
294                                 break loop;\r
295                             }\r
296                             else if(dist(aDir+1, a, b) >= 0.0) {\r
297                                 isHorizontal = isHorizontal(aDir);\r
298                                 position = middle(aDir+1, a, b);\r
299                                 break loop;\r
300                             }\r
301                             else if(dist(aDir-1, a, b) >= 0.0) {\r
302                                 isHorizontal = isHorizontal(aDir);\r
303                                 position = middle(aDir+1, a, b);\r
304                                 break loop;\r
305                             }\r
306                             else {\r
307                                 isHorizontal = isHorizontal(aDir);\r
308                                 double b1 = boundary(aDir+1, a, b);\r
309                                 double b2 = boundary(aDir-1, a, b);\r
310                                 double cost1, cost2;\r
311                                 if(isHorizontal) {\r
312                                     double da1 = a.y - b1;\r
313                                     double db1 = b.y - b1;\r
314                                     cost1 = da1*da1 + db1*db1;\r
315                                     double da2 = a.y - b2;\r
316                                     double db2 = b.y - b2;\r
317                                     cost2 = da2*da2 + db1*db2;\r
318                                 }\r
319                                 else {\r
320                                     double da1 = a.x - b1;\r
321                                     double db1 = b.x - b1;\r
322                                     cost1 = da1*da1 + db1*db1;\r
323                                     double da2 = a.x - b2;\r
324                                     double db2 = b.x - b2;\r
325                                     cost2 = da2*da2 + db1*db2;\r
326                                 }\r
327                                 position = cost1 <= cost2 ? b1 : b2;\r
328                             }\r
329                         }\r
330                     }\r
331         return new RouteLine(isHorizontal, position);\r
332     }\r
333     \r
334     /**\r
335      * Computes the difference between two points to the given direction\r
336      */\r
337     public static double diff(int dir, double x1, double y1, double x2, double y2) {\r
338         switch(dir&3) {\r
339         case 0: return x1 - x2;\r
340         case 1: return y1 - y2;\r
341         case 2: return x2 - x1;\r
342         case 3: return y2 - y1;\r
343         default: throw new Error("Should not happen.");\r
344         }\r
345     }\r
346     \r
347     /**\r
348      * Computes the distance of the bounding boxes of the two route terminals\r
349      * to the given direction.\r
350      */\r
351     public static double dist(int dir, RouteTerminal a, RouteTerminal b) {\r
352         switch(dir&3) {\r
353         case 0: return b.getMinX() - a.getMaxX();\r
354         case 1: return b.getMinY() - a.getMaxY();\r
355         case 2: return a.getMinX() - b.getMaxX();\r
356         case 3: return a.getMinY() - b.getMaxY();\r
357         default: throw new Error("Should not happen.");\r
358         }\r
359     }\r
360     \r
361     /**\r
362      * Computes the middle point between two terminals in the given direction.\r
363      */\r
364     public static double middle(int dir, RouteTerminal a, RouteTerminal b) {\r
365         switch(dir&3) {\r
366         case 0: return 0.5*(b.getMinX() + a.getMaxX());\r
367         case 1: return 0.5*(b.getMinY() + a.getMaxY());\r
368         case 2: return 0.5*(a.getMinX() + b.getMaxX());\r
369         case 3: return 0.5*(a.getMinY() + b.getMaxY());\r
370         default: throw new Error("Should not happen.");\r
371         }\r
372     }\r
373     \r
374     /**\r
375      * Tests whether the point is inside the bounding box of the terminal\r
376      * in the given direction.\r
377      */\r
378     public static boolean isIn(int dir, double x, double y, RouteTerminal a) {\r
379         if((dir&1) == 0)\r
380             return a.getMinX() < x && x < a.getMaxX();\r
381         else\r
382             return a.getMinY() < y && y < a.getMaxY();\r
383     }\r
384     \r
385     public static boolean isHorizontal(int dir) {\r
386         return (dir&1) == 0;\r
387     }\r
388     \r
389     /**\r
390      * Gives the boundary of the route terminal in the given direction.\r
391      */\r
392     public static double boundary(int dir, RouteTerminal a) {\r
393         switch(dir&3) {\r
394         case 0: return a.getMaxX();\r
395         case 1: return a.getMaxY();\r
396         case 2: return a.getMinX();\r
397         case 3: return a.getMinY();\r
398         default: throw new Error("Should not happen.");\r
399         }\r
400     }\r
401     \r
402     /**\r
403      * Gives the boundary of two route terminals in the given direction.\r
404      */\r
405     public static double boundary(int dir, RouteTerminal a, RouteTerminal b) {\r
406         switch(dir&3) {\r
407         case 0: return Math.max(a.getMaxX(), b.getMaxX());\r
408         case 1: return Math.max(a.getMaxY(), b.getMaxY());\r
409         case 2: return Math.min(a.getMinX(), b.getMinX());\r
410         case 3: return Math.min(a.getMinY(), b.getMinY());\r
411         default: throw new Error("Should not happen.");\r
412         }\r
413     }\r
414     \r
415     /**\r
416      * Returns true if the first terminal is farther away in the given direction.\r
417      */\r
418     public static boolean firstIsBoundary(int dir, RouteTerminal a, RouteTerminal b) {\r
419         switch(dir&3) {\r
420         case 0: return a.getMaxX() >= b.getMaxX();\r
421         case 1: return a.getMaxY() >= b.getMaxY();\r
422         case 2: return a.getMinX() <= b.getMinX();\r
423         case 3: return a.getMinY() <= b.getMinY();\r
424         default: throw new Error("Should not happen.");\r
425         }\r
426     }\r
427         \r
428 }\r