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