1 /*******************************************************************************
\r
2 * Copyright (c) 2011 Association for Decentralized Information Management in
\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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.diagram.connection;
\r
15 * An internal utility class for routing simple connections
\r
16 * (a connection with two terminals without any route lines).
\r
18 * @author Hannu Niemistö
\r
20 class SimpleConnectionUtility {
\r
22 public static boolean allowsDirection(RouteTerminal a, int dir) {
\r
23 return Directions.isAllowed(a.getAllowedDirections(), dir);
\r
26 public static final int DIRECT_HORIZONTAL_CONNECTION = 0;
\r
27 public static final int DIRECT_VERTICAL_CONNECTION = 1;
\r
29 public static final int ONE_BEND_HORIZONTAL_VERTICAL = 2;
\r
30 public static final int ONE_BEND_VERTICAL_HORIZONTAL = 3;
\r
32 public static final int MORE_BENDS_BBS_DONT_INTERSECT = 4;
\r
33 public static final int MORE_BENDS_BBS_INTERSECT = 5;
\r
35 public static final int COMPLEX_CONNECTION = 6;
\r
37 public static int simpleConnectionCase(RouteTerminal a, RouteTerminal b) {
\r
38 if(a.isRouteToBounds() && b.isRouteToBounds())
\r
39 return simpleConnectionCaseRouteToBounds(a, b);
\r
41 // Can connect terminals by one straight line?
\r
44 if(allowsDirection(a, 0) && allowsDirection(b, 2))
\r
45 return DIRECT_HORIZONTAL_CONNECTION;
\r
48 if(allowsDirection(a, 2) && allowsDirection(b, 0))
\r
49 return DIRECT_HORIZONTAL_CONNECTION;
\r
52 else if(a.x == b.x) {
\r
54 if(allowsDirection(a, 1) && allowsDirection(b, 3))
\r
55 return DIRECT_VERTICAL_CONNECTION;
\r
58 if(allowsDirection(a, 3) && allowsDirection(b, 1))
\r
59 return DIRECT_VERTICAL_CONNECTION;
\r
63 // Can connect terminals by two lines?
\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
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
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
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
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
109 if(boundingBoxesIntersect) {
\r
110 // Can connect terminals by two lines if we ignore bounding boxes?
\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
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
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
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
141 return MORE_BENDS_BBS_INTERSECT;
\r
145 return MORE_BENDS_BBS_DONT_INTERSECT;
\r
148 private static int simpleConnectionCaseRouteToBounds(RouteTerminal a,
\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
155 double minY = Math.max(a.getMinY(), b.getMinY());
\r
156 double maxY = Math.min(a.getMaxY(), b.getMaxY());
\r
159 double cY = 0.5*(minY+maxY);
\r
163 a.setX(a.getMaxX());
\r
164 b.setX(b.getMinX());
\r
167 a.setX(a.getMinX());
\r
168 b.setX(b.getMaxX());
\r
170 return DIRECT_HORIZONTAL_CONNECTION;
\r
173 double minX = Math.max(a.getMinX(), b.getMinX());
\r
174 double maxX = Math.min(a.getMaxX(), b.getMaxX());
\r
177 double cX = 0.5*(minX+maxX);
\r
181 a.setY(a.getMaxY());
\r
182 b.setY(b.getMinY());
\r
185 a.setY(a.getMinY());
\r
186 b.setY(b.getMaxY());
\r
188 return DIRECT_VERTICAL_CONNECTION;
\r
195 a.setX(a.getMaxX());
\r
198 a.setX(a.getMinX());
\r
201 b.setY(b.getMinY());
\r
204 b.setY(b.getMaxY());
\r
206 return ONE_BEND_HORIZONTAL_VERTICAL;
\r
211 * Finds a route line for two route terminals.
\r
213 public static RouteLine findRouteLine(RouteTerminal a, RouteTerminal b, boolean terminalsIntersect) {
\r
214 if(terminalsIntersect) {
\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
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
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
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
241 //int aDir = Directions.firstAllowedDirection(a.getAllowedDirections());
\r
242 //int bDir = Directions.firstAllowedDirection(b.getAllowedDirections());
\r
244 boolean isHorizontal = true;
\r
245 double position = 0.0;
\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
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
260 else if(dist(aDir, b, a) > 0 && isIn(aDir+1, b.x, b.y, a)) {
\r
261 position = middle(aDir, b, a);
\r
265 position = boundary(aDir, a, b);
\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
275 else if(dist(bDir, b, a) >= 0) {
\r
276 isHorizontal = isHorizontal(aDir);;
\r
277 position = middle(bDir, b, a);
\r
280 else if(firstIsBoundary(bDir, a, b)) {
\r
281 isHorizontal = isHorizontal(aDir);
\r
282 position = boundary(bDir, b, a);
\r
285 isHorizontal = !isHorizontal(aDir);
\r
286 position = boundary(aDir, a, b);
\r
289 // Connection starts to opposite directions from the terminals
\r
291 if(dist(aDir, a, b) >= 0.0) {
\r
292 isHorizontal = !isHorizontal(aDir);
\r
293 position = middle(aDir, a, b);
\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
301 else if(dist(aDir-1, a, b) >= 0.0) {
\r
302 isHorizontal = isHorizontal(aDir);
\r
303 position = middle(aDir+1, a, b);
\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
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
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
327 position = cost1 <= cost2 ? b1 : b2;
\r
331 return new RouteLine(isHorizontal, position);
\r
335 * Computes the difference between two points to the given direction
\r
337 public static double diff(int dir, double x1, double y1, double x2, double y2) {
\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
348 * Computes the distance of the bounding boxes of the two route terminals
\r
349 * to the given direction.
\r
351 public static double dist(int dir, RouteTerminal a, RouteTerminal b) {
\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
362 * Computes the middle point between two terminals in the given direction.
\r
364 public static double middle(int dir, RouteTerminal a, RouteTerminal b) {
\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
375 * Tests whether the point is inside the bounding box of the terminal
\r
376 * in the given direction.
\r
378 public static boolean isIn(int dir, double x, double y, RouteTerminal a) {
\r
380 return a.getMinX() < x && x < a.getMaxX();
\r
382 return a.getMinY() < y && y < a.getMaxY();
\r
385 public static boolean isHorizontal(int dir) {
\r
386 return (dir&1) == 0;
\r
390 * Gives the boundary of the route terminal in the given direction.
\r
392 public static double boundary(int dir, RouteTerminal a) {
\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
403 * Gives the boundary of two route terminals in the given direction.
\r
405 public static double boundary(int dir, RouteTerminal a, RouteTerminal b) {
\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
416 * Returns true if the first terminal is farther away in the given direction.
\r
418 public static boolean firstIsBoundary(int dir, RouteTerminal a, RouteTerminal b) {
\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