1 /*******************************************************************************
2 * Copyright (c) 2011 Association for Decentralized Information Management in
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.diagram.connection;
14 import gnu.trove.map.hash.THashMap;
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;
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;
26 public class RouteTerminal extends RoutePoint implements RouteNode, Serializable {
28 private static final long serialVersionUID = -8839093950347737029L;
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);
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;
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) {
56 this.allowedDirections = allowedDirections;
57 this.routeToBounds = routeToBounds;
59 this.dynamicPosition = dynamicPosition;
63 public void setData(Object data) {
68 public Object getData() {
72 public int getAllowedDirections() {
73 return allowedDirections;
76 public double getMinX() {
80 public double getMinY() {
84 public double getMaxX() {
88 public double getMaxY() {
92 public Rectangle2D getBounds() {
93 return new Rectangle2D.Double(minX, minY, maxX-minX, maxY-minY);
97 * Routes connection from the terminal to route line
98 * adding necessary transient route lines.
101 protected void route(ArrayList<RouteLine> lines, IntervalCache cache, boolean boundingBoxesIntersect) {
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;
112 lineDir = linePosition < x ? 2 : 0;
113 routeLineDoesNotIntersectTerminal =
114 linePosition <= minX || linePosition >= maxX;
117 if(routeLineDoesNotIntersectTerminal) {
118 RouteLine line0 = createLine0(lineDir);
119 new RouteLink(line0, line);
142 if (!line.getPoints().contains(this))
144 Interval interval = cache.get(line);
145 if(line.isHorizontal) {
146 if(interval.min < minX)
154 if(interval.min < minY)
162 // In which direction the route line is?
164 boolean routeLineDoesNotIntersectTerminal;
165 double linePosition = line.position;
166 if(line.isHorizontal) {
167 if (linePosition == y) {
168 // direct route to terminal
172 lineDir = linePosition < y ? 3 : 1;
173 routeLineDoesNotIntersectTerminal = linePosition <= minY || linePosition >= maxY
174 || boundingBoxesIntersect /* we ignore intersection in this case */;
177 if (linePosition == x) {
178 // direct route to terminal
182 lineDir = linePosition < x ? 2 : 0;
183 routeLineDoesNotIntersectTerminal = linePosition <= minX || linePosition >= maxX
184 || boundingBoxesIntersect /* we ignore intersection in this case */;
187 // We can route the connection directly to the right direction
188 if((routeLineDoesNotIntersectTerminal ||
189 (line.isHorizontal && (x == minX || x == maxX)) || // already on the top/bottom edge
190 (!line.isHorizontal && (y == minY || y == maxY)) // already on the left/right edge
192 Directions.isAllowed(allowedDirections, lineDir)) {
193 RouteLine line0 = createLine0(lineDir);
194 new RouteLink(line0, line);
199 // We must make one bend
201 int dir = 1-(lineDir&1);
202 if(Directions.isAllowed(allowedDirections, dir)) {
203 if(Directions.isAllowed(allowedDirections, dir+2)) {
204 Interval interval = cache.get(line);
206 if(interval.max <= maxX)
209 else /* dir == 1 */ {
210 if(interval.max <= maxY)
219 if(Directions.isAllowed(allowedDirections, dir+2)) {
227 RouteLine line0 = createLine0(dir);
228 RouteLine line1 = createLine1(dir);
229 new RouteLink(line0, line1);
230 new RouteLink(line1, line);
233 line0.nextTransient = line1;
237 // We can begin to the right direction but do two bends
238 if(!routeLineDoesNotIntersectTerminal &&
239 Directions.isAllowed(allowedDirections, lineDir)) {
240 RouteLine line0 = createLine0(lineDir);
241 RouteLine line1 = createLine1(lineDir);
242 RouteLine line2 = createLine2(lineDir, cache);
243 new RouteLink(line0, line1);
244 new RouteLink(line1, line2);
245 new RouteLink(line2, line);
249 line0.nextTransient = line1;
250 line1.nextTransient = line2;
254 // Only allowed direction is to completely wrong direction:
255 // we must make two bends
258 RouteLine line0 = createLine0(dir);
259 RouteLine line1 = createLine1(dir);
260 RouteLine line2 = createLine2(dir, cache);
261 new RouteLink(line0, line1);
262 new RouteLink(line1, line2);
263 new RouteLink(line2, line);
267 line0.nextTransient = line1;
268 line1.nextTransient = line2;
274 protected RouteLine createLine0(int dir) {
275 RouteLine line0 = (dir&1) == 0
276 ? new RouteLine(true, y)
277 : new RouteLine(false, x)
279 line0.addPoint(this);
280 line0.terminal = this;
284 private RouteLine createLine1(int dir) {
285 RouteLine line1 = (dir&1) == 0
286 ? new RouteLine(false, (dir&2) == 0 ? maxX : minX)
287 : new RouteLine(true, (dir&2) == 0 ? maxY : minY)
289 line1.terminal = this;
293 private RouteLine createLine2(int dir, IntervalCache cache) {
294 Interval interval = cache.get(line);
298 if(minY < interval.min) {
299 if(maxY > interval.max) {
300 position = 2*maxY-y-interval.max < interval.min+y-2*minY ? maxY : minY;
307 if(maxY > interval.max) {
311 position = maxY-y < y-minY ? maxY : minY;
314 line2 = new RouteLine(true, position);
318 if(minX < interval.min) {
319 if(maxX > interval.max) {
320 position = 2*maxX-x-interval.max < interval.min+x-2*minX ? maxX : minX;
327 if(maxX > interval.max) {
331 position = maxX-x < x-minX ? maxX : minX;
334 line2 = new RouteLine(false, position);
336 line2.terminal = this;
340 public boolean isNear(double x2, double y2) {
341 return minX <= x2 && x2 <= maxX && minY <= y2 && y2 <= maxY;
344 void setLocation(double x2, double y2) {
355 void rotate(int amount) {
360 int temp = (allowedDirections&15) << amount;
361 allowedDirections = (temp&15) | (temp >> 4) | (allowedDirections&16);
364 public double approximatePositionToLine() {
365 // In which direction the route line is?
366 int lineDir = line.isHorizontal
367 ? (line.position < y ? 3 : 1)
368 : (line.position < x ? 2 : 0)
371 // We can route the connection directly to the right direction
372 if(Directions.isAllowed(allowedDirections, lineDir))
373 return line.isHorizontal ? x : y;
375 // We must make one bend
376 for(int dir = 0;dir < 4;++dir) {
377 if(Directions.isAllowed(allowedDirections, dir) && ((dir^lineDir)&1) == 1) {
386 // Only allowed direction is to completely wrong direction:
387 // we must make two bends
390 return line.isHorizontal ? x : y;
394 public RouteTerminal copy(THashMap<Object, Object> map) {
395 RouteTerminal copy = (RouteTerminal)map.get(this);
397 copy = new RouteTerminal(x, y, minX, minY, maxX, maxY,
398 allowedDirections, routeToBounds, style, dynamicPosition);
399 copy.setDynamicStyle(dynamicStyle);
402 copy.line = line == null ? null : line.copy(map);
407 public void print(PrintStream out) {
408 out.print(" (" + x + "," + y + ") " + allowedDirections + " -> ");
412 out.print("NO LINE");
413 out.print(" (data=" + data + ")");
417 public ILineEndStyle getStyle() {
421 public ILineEndStyle getRenderStyle() {
422 if (dynamicStyle != null)
427 public boolean hasDirectConnection() {
428 return (allowedDirections&16) == 16;
431 public RouteLine getLine() {
435 public void setLine(RouteLine line) {
439 public void setMinX(double minX) {
442 public void setMinY(double minY) {
446 public void setMaxX(double maxX) {
450 public void setMaxY(double maxY) {
454 public void toggleDirectLines() {
455 this.allowedDirections ^= 16;
458 public boolean isRouteToBounds() {
459 return routeToBounds;
462 public void setStyle(ILineEndStyle style) {
466 public ILineEndStyle getDynamicStyle() {
470 public void setDynamicStyle(ILineEndStyle dynamicStyle) {
471 this.dynamicStyle = dynamicStyle;
474 public RouteTerminalPosition getDynamicPosition() {
475 return dynamicPosition;
480 public boolean updateDynamicPosition() {
481 boolean changed = false;
482 if (dynamicPosition != null) {
483 AffineTransform tr = dynamicPosition.getTransform();
485 double nx = tr.getTranslateX();
488 double ny = tr.getTranslateY();