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
14 import gnu.trove.map.hash.THashMap;
\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
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
25 public class RouteTerminal extends RoutePoint implements RouteNode, Serializable {
\r
27 private static final long serialVersionUID = -8839093950347737029L;
\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
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
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
54 this.allowedDirections = allowedDirections;
\r
55 this.routeToBounds = routeToBounds;
\r
60 public void setData(Object data) {
\r
65 public Object getData() {
\r
69 public int getAllowedDirections() {
\r
70 return allowedDirections;
\r
73 public double getMinX() {
\r
77 public double getMinY() {
\r
81 public double getMaxX() {
\r
85 public double getMaxY() {
\r
89 public Rectangle2D getBounds() {
\r
90 return new Rectangle2D.Double(minX, minY, maxX-minX, maxY-minY);
\r
94 * Routes connection from the terminal to route line
\r
95 * adding necessary transient route lines.
\r
98 void route(ArrayList<RouteLine> lines, IntervalCache cache, boolean boundingBoxesIntersect) {
\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
109 lineDir = linePosition < x ? 2 : 0;
\r
110 routeLineDoesNotIntersectTerminal =
\r
111 linePosition <= minX || linePosition >= maxX;
\r
114 if(routeLineDoesNotIntersectTerminal) {
\r
115 RouteLine line0 = createLine0(lineDir);
\r
116 new RouteLink(line0, line);
\r
121 y = 0.5*(minY+maxY);
\r
124 x = 0.5*(minX+maxX);
\r
129 y = 0.5*(minY+maxY);
\r
132 x = 0.5*(minX+maxX);
\r
139 line.addPoint(this);
\r
140 Interval interval = cache.get(line);
\r
141 if(line.isHorizontal) {
\r
142 if(interval.min < minX)
\r
150 if(interval.min < minY)
\r
158 // In which direction the route line is?
\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
168 lineDir = linePosition < x ? 2 : 0;
\r
169 routeLineDoesNotIntersectTerminal = linePosition <= minX || linePosition >= maxX
\r
170 || boundingBoxesIntersect /* we ignore intersection in this case */;
\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
182 // We must make one bend
\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
189 if(interval.max <= maxX)
\r
192 else /* dir == 1 */ {
\r
193 if(interval.max <= maxY)
\r
202 if(Directions.isAllowed(allowedDirections, dir+2)) {
\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
216 line0.nextTransient = line1;
\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
232 line0.nextTransient = line1;
\r
233 line1.nextTransient = line2;
\r
237 // Only allowed direction is to completely wrong direction:
\r
238 // we must make two bends
\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
250 line0.nextTransient = line1;
\r
251 line1.nextTransient = line2;
\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
262 line0.addPoint(this);
\r
263 line0.terminal = this;
\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
272 line1.terminal = this;
\r
276 private RouteLine createLine2(int dir, IntervalCache cache) {
\r
277 Interval interval = cache.get(line);
\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
290 if(maxY > interval.max) {
\r
294 position = maxY-y < y-minY ? maxY : minY;
\r
297 line2 = new RouteLine(true, 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
310 if(maxX > interval.max) {
\r
314 position = maxX-x < x-minX ? maxX : minX;
\r
317 line2 = new RouteLine(false, position);
\r
319 line2.terminal = this;
\r
323 public boolean isNear(double x2, double y2) {
\r
324 return minX <= x2 && x2 <= maxX && minY <= y2 && y2 <= maxY;
\r
327 void setLocation(double x2, double y2) {
\r
328 double dx = x2 - x;
\r
329 double dy = y2 - y;
\r
338 void rotate(int amount) {
\r
343 int temp = (allowedDirections&15) << amount;
\r
344 allowedDirections = (temp&15) | (temp >> 4) | (allowedDirections&16);
\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
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
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
362 case 0: return maxX;
\r
363 case 1: return maxY;
\r
364 case 2: return minX;
\r
365 case 3: return minY;
\r
369 // Only allowed direction is to completely wrong direction:
\r
370 // we must make two bends
\r
373 return line.isHorizontal ? x : y;
\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
385 copy.line = line == null ? null : line.copy(map);
\r
390 public void print(PrintStream out) {
\r
391 out.print(" (" + x + "," + y + ") " + allowedDirections + " -> ");
\r
395 out.print("NO LINE");
\r
396 out.print(" (data=" + data + ")");
\r
400 public ILineEndStyle getStyle() {
\r
404 public ILineEndStyle getRenderStyle() {
\r
405 if (dynamicStyle != null)
\r
406 return dynamicStyle;
\r
410 public boolean hasDirectConnection() {
\r
411 return (allowedDirections&16) == 16;
\r
414 public RouteLine getLine() {
\r
418 public void setMinX(double minX) {
\r
421 public void setMinY(double minY) {
\r
425 public void setMaxX(double maxX) {
\r
429 public void setMaxY(double maxY) {
\r
433 public void toggleDirectLines() {
\r
434 this.allowedDirections ^= 16;
\r
437 public boolean isRouteToBounds() {
\r
438 return routeToBounds;
\r
441 public void setStyle(ILineEndStyle style) {
\r
442 this.style = style;
\r
445 public ILineEndStyle getDynamicStyle() {
\r
446 return dynamicStyle;
\r
449 public void setDynamicStyle(ILineEndStyle dynamicStyle) {
\r
450 this.dynamicStyle = dynamicStyle;
\r