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