]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteTerminal.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram.connection / src / org / simantics / diagram / connection / RouteTerminal.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 import gnu.trove.map.hash.THashMap;\r
15 \r
16 import java.awt.geom.Rectangle2D;\r
17 import java.io.PrintStream;\r
18 import java.io.Serializable;\r
19 import java.util.ArrayList;\r
20 \r
21 import org.simantics.diagram.connection.RouteGraph.Interval;\r
22 import org.simantics.diagram.connection.RouteGraph.IntervalCache;\r
23 import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;\r
24 \r
25 public class RouteTerminal extends RoutePoint implements RouteNode, Serializable {\r
26 \r
27     private static final long serialVersionUID = -8839093950347737029L;\r
28 \r
29     public static final int   DIR_RIGHT        = (1 << 0);\r
30     public static final int   DIR_DOWN         = (1 << 1);\r
31     public static final int   DIR_LEFT         = (1 << 2);\r
32     public static final int   DIR_UP           = (1 << 3);\r
33     public static final int   DIR_DIRECT       = (1 << 4);\r
34 \r
35     private Object data;\r
36     private double minX, minY;\r
37     private double maxX, maxY;\r
38     private int allowedDirections;\r
39     private ILineEndStyle style;\r
40     private ILineEndStyle dynamicStyle;\r
41     private boolean routeToBounds;\r
42 \r
43     RouteLine line;\r
44 \r
45     RouteTerminal(double x, double y, double minX, double minY,\r
46             double maxX, double maxY, int allowedDirections,\r
47             boolean routeToBounds,\r
48             ILineEndStyle style) {\r
49         super(x, y);\r
50         this.minX = minX;\r
51         this.minY = minY;\r
52         this.maxX = maxX;\r
53         this.maxY = maxY;\r
54         this.allowedDirections = allowedDirections;\r
55         this.routeToBounds = routeToBounds;\r
56         this.style = style;\r
57     }\r
58     \r
59     @Override\r
60     public void setData(Object data) {\r
61         this.data = data;\r
62     }\r
63     \r
64     @Override\r
65     public Object getData() {\r
66         return data;\r
67     }\r
68     \r
69     public int getAllowedDirections() {\r
70                 return allowedDirections;\r
71         }\r
72     \r
73     public double getMinX() {\r
74                 return minX;\r
75         }\r
76     \r
77     public double getMinY() {\r
78                 return minY;\r
79         }\r
80     \r
81     public double getMaxX() {\r
82                 return maxX;\r
83         }\r
84     \r
85     public double getMaxY() {\r
86                 return maxY;\r
87         }\r
88     \r
89     public Rectangle2D getBounds() {\r
90         return new Rectangle2D.Double(minX, minY, maxX-minX, maxY-minY);\r
91     }\r
92 \r
93     /**\r
94      * Routes connection from the terminal to route line\r
95      * adding necessary transient route lines.\r
96      * @param cache \r
97      */\r
98     void route(ArrayList<RouteLine> lines, IntervalCache cache, boolean boundingBoxesIntersect) {\r
99         if(routeToBounds) {\r
100             int lineDir;\r
101             boolean routeLineDoesNotIntersectTerminal;\r
102             double linePosition = line.position;\r
103             if(line.isHorizontal) { \r
104                 lineDir = linePosition < y ? 3 : 1;\r
105                 routeLineDoesNotIntersectTerminal = \r
106                         linePosition <= minY || linePosition >= maxY;\r
107             }\r
108             else {\r
109                 lineDir = linePosition < x ? 2 : 0;\r
110                 routeLineDoesNotIntersectTerminal = \r
111                         linePosition <= minX || linePosition >= maxX;\r
112             }\r
113             \r
114             if(routeLineDoesNotIntersectTerminal) {\r
115                 RouteLine line0 = createLine0(lineDir);\r
116                 new RouteLink(line0, line);\r
117                 lines.add(line0);\r
118                 switch(lineDir) {\r
119                 case 0:\r
120                     x = maxX;\r
121                     y = 0.5*(minY+maxY);\r
122                     break;\r
123                 case 1:\r
124                     x = 0.5*(minX+maxX);\r
125                     y = maxY;\r
126                     break;\r
127                 case 2:\r
128                     x = minX;\r
129                     y = 0.5*(minY+maxY);\r
130                     break;\r
131                 case 3:\r
132                     x = 0.5*(minX+maxX);\r
133                     y = minY;\r
134                     break;\r
135                 }\r
136                 return;\r
137             }\r
138             else {\r
139                 line.addPoint(this);\r
140                 Interval interval = cache.get(line);\r
141                 if(line.isHorizontal) {\r
142                     if(interval.min < minX)\r
143                         x = minX;\r
144                     else\r
145                         x = maxX;\r
146                     y = linePosition;\r
147                 }\r
148                 else {\r
149                     x = linePosition;\r
150                     if(interval.min < minY)\r
151                         y = minY;\r
152                     else\r
153                         y = maxY;\r
154                 }\r
155             }\r
156         }\r
157         else {\r
158             // In which direction the route line is?\r
159             int lineDir;\r
160             boolean routeLineDoesNotIntersectTerminal;\r
161             double linePosition = line.position;\r
162             if(line.isHorizontal) { \r
163                 lineDir = linePosition < y ? 3 : 1;\r
164                 routeLineDoesNotIntersectTerminal = linePosition <= minY || linePosition >= maxY \r
165                         || boundingBoxesIntersect /* we ignore intersection in this case */;\r
166             }\r
167             else {\r
168                 lineDir = linePosition < x ? 2 : 0;\r
169                 routeLineDoesNotIntersectTerminal = linePosition <= minX || linePosition >= maxX\r
170                         || boundingBoxesIntersect /* we ignore intersection in this case */;\r
171             }\r
172                     \r
173             // We can route the connection directly to the right direction\r
174             if(routeLineDoesNotIntersectTerminal && \r
175                     Directions.isAllowed(allowedDirections, lineDir)) {           \r
176                 RouteLine line0 = createLine0(lineDir);\r
177                 new RouteLink(line0, line);\r
178                 lines.add(line0);\r
179                 return;\r
180             }\r
181             \r
182             // We must make one bend\r
183             oneBend: {\r
184                 int dir = 1-(lineDir&1);\r
185                 if(Directions.isAllowed(allowedDirections, dir)) {\r
186                     if(Directions.isAllowed(allowedDirections, dir+2)) {\r
187                         Interval interval = cache.get(line);\r
188                         if(dir == 0) {\r
189                             if(interval.max <= maxX)\r
190                                 dir = 2;\r
191                         }\r
192                         else /* dir == 1 */ {\r
193                             if(interval.max <= maxY)\r
194                                 dir = 3;\r
195                         }\r
196                     }\r
197                     else {\r
198                         // ok\r
199                     }\r
200                 }\r
201                 else {\r
202                     if(Directions.isAllowed(allowedDirections, dir+2)) {\r
203                         dir = dir + 2;\r
204                     }\r
205                     else {\r
206                         break oneBend;\r
207                     }\r
208                 }\r
209     \r
210                 RouteLine line0 = createLine0(dir);\r
211                 RouteLine line1 = createLine1(dir);\r
212                 new RouteLink(line0, line1);\r
213                 new RouteLink(line1, line);\r
214                 lines.add(line0);\r
215                 lines.add(line1);\r
216                 line0.nextTransient = line1;\r
217                 return;\r
218             }\r
219             \r
220             // We can begin to the right direction but do two bends\r
221             if(!routeLineDoesNotIntersectTerminal && \r
222                     Directions.isAllowed(allowedDirections, lineDir)) {  \r
223                 RouteLine line0 = createLine0(lineDir);\r
224                 RouteLine line1 = createLine1(lineDir);\r
225                 RouteLine line2 = createLine2(lineDir, cache);\r
226                 new RouteLink(line0, line1);\r
227                 new RouteLink(line1, line2);\r
228                 new RouteLink(line2, line);\r
229                 lines.add(line0);\r
230                 lines.add(line1);\r
231                 lines.add(line2);\r
232                 line0.nextTransient = line1;\r
233                 line1.nextTransient = line2;\r
234                 return;\r
235             }\r
236             \r
237             // Only allowed direction is to completely wrong direction:\r
238             // we must make two bends\r
239             {\r
240                 int dir = lineDir^2;\r
241                 RouteLine line0 = createLine0(dir);\r
242                 RouteLine line1 = createLine1(dir);\r
243                 RouteLine line2 = createLine2(dir, cache);\r
244                 new RouteLink(line0, line1);\r
245                 new RouteLink(line1, line2);\r
246                 new RouteLink(line2, line);\r
247                 lines.add(line0);\r
248                 lines.add(line1);\r
249                 lines.add(line2);\r
250                 line0.nextTransient = line1;\r
251                 line1.nextTransient = line2;\r
252                 return;\r
253             }\r
254         }\r
255     }\r
256     \r
257     private RouteLine createLine0(int dir) {\r
258         RouteLine line0 = (dir&1) == 0 \r
259                 ? new RouteLine(true, y)\r
260                 : new RouteLine(false, x)\r
261                 ;\r
262         line0.addPoint(this);\r
263         line0.terminal = this;\r
264         return line0;\r
265     }\r
266     \r
267     private RouteLine createLine1(int dir) {\r
268         RouteLine line1 = (dir&1) == 0 \r
269                 ? new RouteLine(false, (dir&2) == 0 ? maxX : minX)\r
270                 : new RouteLine(true, (dir&2) == 0 ? maxY : minY)\r
271                 ;\r
272         line1.terminal = this;\r
273         return line1;\r
274     }\r
275     \r
276     private RouteLine createLine2(int dir, IntervalCache cache) {\r
277         Interval interval = cache.get(line);\r
278         RouteLine line2;\r
279         if((dir&1) == 0) {\r
280             double position;\r
281             if(minY < interval.min) {\r
282                 if(maxY > interval.max) {\r
283                     position = 2*maxY-y-interval.max < interval.min+y-2*minY ? maxY : minY;\r
284                 }\r
285                 else {\r
286                     position = maxY;\r
287                 }\r
288             }\r
289             else {\r
290                 if(maxY > interval.max) {\r
291                     position = minY;\r
292                 }\r
293                 else {\r
294                     position = maxY-y < y-minY ? maxY : minY;\r
295                 }\r
296             }\r
297             line2 = new RouteLine(true, position);\r
298         }\r
299         else {\r
300             double position;\r
301             if(minX < interval.min) {\r
302                 if(maxX > interval.max) {\r
303                     position = 2*maxX-x-interval.max < interval.min+x-2*minX ? maxX : minX;\r
304                 }\r
305                 else {\r
306                     position = maxX;\r
307                 }\r
308             }\r
309             else {\r
310                 if(maxX > interval.max) {\r
311                     position = minX;\r
312                 }\r
313                 else {\r
314                     position = maxX-x < x-minX ? maxX : minX;\r
315                 }\r
316             }\r
317             line2 = new RouteLine(false, position);\r
318         }\r
319         line2.terminal = this;                \r
320         return line2;\r
321     }    \r
322 \r
323     public boolean isNear(double x2, double y2) {\r
324         return minX <= x2 && x2 <= maxX && minY <= y2 && y2 <= maxY;\r
325     }\r
326 \r
327     void setLocation(double x2, double y2) {\r
328         double dx = x2 - x;\r
329         double dy = y2 - y;\r
330         x = x2;\r
331         y = y2;\r
332         minX += dx;\r
333         minY += dy;\r
334         maxX += dx;\r
335         maxY += dy;\r
336     }\r
337 \r
338     void rotate(int amount) {\r
339         amount %= 4;\r
340         if(amount < 0)\r
341             amount += 4;\r
342         \r
343         int temp = (allowedDirections&15) << amount;\r
344         allowedDirections = (temp&15) | (temp >> 4) | (allowedDirections&16);       \r
345     }\r
346 \r
347     public double approximatePositionToLine() {\r
348         // In which direction the route line is?\r
349         int lineDir = line.isHorizontal \r
350                 ? (line.position < y ? 3 : 1)\r
351                 : (line.position < x ? 2 : 0)\r
352                 ;\r
353                 \r
354         // We can route the connection directly to the right direction\r
355         if(Directions.isAllowed(allowedDirections, lineDir))\r
356             return line.isHorizontal ? x : y;\r
357         \r
358         // We must make one bend \r
359         for(int dir = 0;dir < 4;++dir) {\r
360             if(Directions.isAllowed(allowedDirections, dir) && ((dir^lineDir)&1) == 1) {\r
361                 switch(dir) {\r
362                 case 0: return maxX;\r
363                 case 1: return maxY;\r
364                 case 2: return minX;\r
365                 case 3: return minY;\r
366                 }\r
367             }\r
368         }\r
369         // Only allowed direction is to completely wrong direction:\r
370         // we must make two bends\r
371         {\r
372             // Approximation\r
373             return line.isHorizontal ? x : y;\r
374         }\r
375     }\r
376 \r
377     RouteTerminal copy(THashMap<Object, Object> map) {\r
378         RouteTerminal copy = (RouteTerminal)map.get(this);\r
379         if(copy == null) {      \r
380                 copy = new RouteTerminal(x,  y, minX, minY, maxX, maxY, \r
381                                 allowedDirections, routeToBounds, style);\r
382                 copy.setDynamicStyle(dynamicStyle);\r
383                 map.put(this, copy);\r
384                 copy.data = data;\r
385                 copy.line = line == null ? null : line.copy(map);\r
386         }\r
387         return copy;\r
388     }\r
389 \r
390     public void print(PrintStream out) {\r
391         out.print("     (" + x + "," + y + ") " + allowedDirections + " -> ");\r
392         if (line != null)\r
393             line.print(out);\r
394         else\r
395             out.print("NO LINE");\r
396         out.print(" (data=" + data + ")");\r
397         out.println();\r
398     }\r
399 \r
400     public ILineEndStyle getStyle() {\r
401         return style;\r
402     }\r
403     \r
404     public ILineEndStyle getRenderStyle() {\r
405         if (dynamicStyle != null)\r
406                 return dynamicStyle;\r
407         return style;\r
408     }\r
409 \r
410     public boolean hasDirectConnection() {\r
411         return (allowedDirections&16) == 16;\r
412     }\r
413     \r
414     public RouteLine getLine() {\r
415         return line;\r
416     }\r
417     \r
418     public void setMinX(double minX) {\r
419                 this.minX = minX;\r
420         }\r
421     public void setMinY(double minY) {\r
422                 this.minY = minY;\r
423         }\r
424     \r
425     public void setMaxX(double maxX) {\r
426                 this.maxX = maxX;\r
427         }\r
428     \r
429     public void setMaxY(double maxY) {\r
430                 this.maxY = maxY;\r
431         }\r
432 \r
433     public void toggleDirectLines() {\r
434         this.allowedDirections ^= 16;\r
435     }\r
436     \r
437     public boolean isRouteToBounds() {\r
438         return routeToBounds;\r
439     }\r
440     \r
441     public void setStyle(ILineEndStyle style) {\r
442                 this.style = style;\r
443         }\r
444     \r
445     public ILineEndStyle getDynamicStyle() {\r
446                 return dynamicStyle;\r
447         }\r
448     \r
449     public void setDynamicStyle(ILineEndStyle dynamicStyle) {\r
450                 this.dynamicStyle = dynamicStyle;\r
451         }\r
452 }\r