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