1
|
/*******************************************************************************
|
2
|
* Copyright (c) 2000, 2010 IBM Corporation and others.
|
3
|
* All rights reserved. This program and the accompanying materials
|
4
|
* are made available under the terms of the Eclipse Public License v1.0
|
5
|
* which accompanies this distribution, and is available at
|
6
|
* http://www.eclipse.org/legal/epl-v10.html
|
7
|
*
|
8
|
* Contributors:
|
9
|
* IBM Corporation - initial API and implementation
|
10
|
*******************************************************************************/
|
11
|
package org.eclipse.draw2d;
|
12
|
|
13
|
import java.util.ArrayList;
|
14
|
import java.util.HashMap;
|
15
|
import java.util.List;
|
16
|
import java.util.Map;
|
17
|
|
18
|
import org.eclipse.draw2d.geometry.Point;
|
19
|
import org.eclipse.draw2d.geometry.PointList;
|
20
|
import org.eclipse.draw2d.geometry.Ray;
|
21
|
import org.eclipse.draw2d.geometry.Rectangle;
|
22
|
|
23
|
/**
|
24
|
* Provides a {@link Connection} with an orthogonal route between the
|
25
|
* Connection's source and target anchors.
|
26
|
*/
|
27
|
public final class ManhattanConnectionRouter extends AbstractRouter {
|
28
|
|
29
|
private Map rowsUsed = new HashMap();
|
30
|
private Map colsUsed = new HashMap();
|
31
|
// private Hashtable offsets = new Hashtable(7);
|
32
|
|
33
|
private Map reservedInfo = new HashMap();
|
34
|
|
35
|
private class ReservedInfo {
|
36
|
public List reservedRows = new ArrayList(2);
|
37
|
public List reservedCols = new ArrayList(2);
|
38
|
}
|
39
|
|
40
|
private static Ray UP = new Ray(0, -1), DOWN = new Ray(0, 1),
|
41
|
LEFT = new Ray(-1, 0), RIGHT = new Ray(1, 0);
|
42
|
|
43
|
/**
|
44
|
* @see ConnectionRouter#invalidate(Connection)
|
45
|
*/
|
46
|
public void invalidate(Connection connection) {
|
47
|
removeReservedLines(connection);
|
48
|
}
|
49
|
|
50
|
private int getColumnNear(Connection connection, int r, int n, int x) {
|
51
|
int min = Math.min(n, x), max = Math.max(n, x);
|
52
|
if (min > r) {
|
53
|
max = min;
|
54
|
min = r - (min - r);
|
55
|
}
|
56
|
if (max < r) {
|
57
|
min = max;
|
58
|
max = r + (r - max);
|
59
|
}
|
60
|
int proximity = 0;
|
61
|
int direction = -1;
|
62
|
if (r % 2 == 1)
|
63
|
r--;
|
64
|
Integer i;
|
65
|
while (proximity < r) {
|
66
|
i = new Integer(r + proximity * direction);
|
67
|
if (!colsUsed.containsKey(i)) {
|
68
|
colsUsed.put(i, i);
|
69
|
reserveColumn(connection, i);
|
70
|
return i.intValue();
|
71
|
}
|
72
|
int j = i.intValue();
|
73
|
if (j <= min)
|
74
|
return j + 2;
|
75
|
if (j >= max)
|
76
|
return j - 2;
|
77
|
if (direction == 1)
|
78
|
direction = -1;
|
79
|
else {
|
80
|
direction = 1;
|
81
|
proximity += 2;
|
82
|
}
|
83
|
}
|
84
|
return r;
|
85
|
}
|
86
|
|
87
|
/**
|
88
|
* Returns the direction the point <i>p</i> is in relation to the given
|
89
|
* rectangle. Possible values are LEFT (-1,0), RIGHT (1,0), UP (0,-1) and
|
90
|
* DOWN (0,1).
|
91
|
*
|
92
|
* @param r
|
93
|
* the rectangle
|
94
|
* @param p
|
95
|
* the point
|
96
|
* @return the direction from <i>r</i> to <i>p</i>
|
97
|
*/
|
98
|
protected Ray getDirection(Rectangle r, Point p) {
|
99
|
int i, distance = Math.abs(r.x - p.x);
|
100
|
Ray direction;
|
101
|
|
102
|
direction = LEFT;
|
103
|
|
104
|
i = Math.abs(r.y - p.y);
|
105
|
if (i <= distance) {
|
106
|
distance = i;
|
107
|
direction = UP;
|
108
|
}
|
109
|
|
110
|
i = Math.abs(r.bottom() - p.y);
|
111
|
if (i <= distance) {
|
112
|
distance = i;
|
113
|
direction = DOWN;
|
114
|
}
|
115
|
|
116
|
i = Math.abs(r.right() - p.x);
|
117
|
if (i < distance) {
|
118
|
distance = i;
|
119
|
direction = RIGHT;
|
120
|
}
|
121
|
|
122
|
return direction;
|
123
|
}
|
124
|
|
125
|
protected Ray getEndDirection(Connection conn) {
|
126
|
ConnectionAnchor anchor = conn.getTargetAnchor();
|
127
|
Point p = getEndPoint(conn);
|
128
|
Rectangle rect;
|
129
|
if (anchor.getOwner() == null)
|
130
|
rect = new Rectangle(p.x - 1, p.y - 1, 2, 2);
|
131
|
else {
|
132
|
rect = conn.getTargetAnchor().getOwner().getBounds().getCopy();
|
133
|
conn.getTargetAnchor().getOwner().translateToAbsolute(rect);
|
134
|
}
|
135
|
return getDirection(rect, p);
|
136
|
}
|
137
|
|
138
|
protected int getRowNear(Connection connection, int r, int n, int x) {
|
139
|
int min = Math.min(n, x), max = Math.max(n, x);
|
140
|
if (min > r) {
|
141
|
max = min;
|
142
|
min = r - (min - r);
|
143
|
}
|
144
|
if (max < r) {
|
145
|
min = max;
|
146
|
max = r + (r - max);
|
147
|
}
|
148
|
|
149
|
int proximity = 0;
|
150
|
int direction = -1;
|
151
|
if (r % 2 == 1)
|
152
|
r--;
|
153
|
Integer i;
|
154
|
while (proximity < r) {
|
155
|
i = new Integer(r + proximity * direction);
|
156
|
if (!rowsUsed.containsKey(i)) {
|
157
|
rowsUsed.put(i, i);
|
158
|
reserveRow(connection, i);
|
159
|
return i.intValue();
|
160
|
}
|
161
|
int j = i.intValue();
|
162
|
if (j <= min)
|
163
|
return j + 2;
|
164
|
if (j >= max)
|
165
|
return j - 2;
|
166
|
if (direction == 1)
|
167
|
direction = -1;
|
168
|
else {
|
169
|
direction = 1;
|
170
|
proximity += 2;
|
171
|
}
|
172
|
}
|
173
|
return r;
|
174
|
}
|
175
|
|
176
|
protected Ray getStartDirection(Connection conn) {
|
177
|
ConnectionAnchor anchor = conn.getSourceAnchor();
|
178
|
Point p = getStartPoint(conn);
|
179
|
Rectangle rect;
|
180
|
if (anchor.getOwner() == null)
|
181
|
rect = new Rectangle(p.x - 1, p.y - 1, 2, 2);
|
182
|
else {
|
183
|
rect = conn.getSourceAnchor().getOwner().getBounds().getCopy();
|
184
|
conn.getSourceAnchor().getOwner().translateToAbsolute(rect);
|
185
|
}
|
186
|
return getDirection(rect, p);
|
187
|
}
|
188
|
|
189
|
protected void processPositions(Ray start, Ray end, List positions,
|
190
|
boolean horizontal, Connection conn) {
|
191
|
removeReservedLines(conn);
|
192
|
|
193
|
int pos[] = new int[positions.size() + 2];
|
194
|
if (horizontal)
|
195
|
pos[0] = start.x;
|
196
|
else
|
197
|
pos[0] = start.y;
|
198
|
int i;
|
199
|
for (i = 0; i < positions.size(); i++) {
|
200
|
pos[i + 1] = ((Integer) positions.get(i)).intValue();
|
201
|
}
|
202
|
if (horizontal == (positions.size() % 2 == 1))
|
203
|
pos[++i] = end.x;
|
204
|
else
|
205
|
pos[++i] = end.y;
|
206
|
|
207
|
PointList points = new PointList();
|
208
|
points.addPoint(new Point(start.x, start.y));
|
209
|
Point p;
|
210
|
int current, prev, min, max;
|
211
|
boolean adjust;
|
212
|
for (i = 2; i < pos.length - 1; i++) {
|
213
|
horizontal = !horizontal;
|
214
|
prev = pos[i - 1];
|
215
|
current = pos[i];
|
216
|
|
217
|
adjust = (i != pos.length - 2);
|
218
|
if (horizontal) {
|
219
|
if (adjust) {
|
220
|
min = pos[i - 2];
|
221
|
max = pos[i + 2];
|
222
|
pos[i] = current = getRowNear(conn, current, min, max);
|
223
|
}
|
224
|
p = new Point(prev, current);
|
225
|
} else {
|
226
|
if (adjust) {
|
227
|
min = pos[i - 2];
|
228
|
max = pos[i + 2];
|
229
|
pos[i] = current = getColumnNear(conn, current, min, max);
|
230
|
}
|
231
|
p = new Point(current, prev);
|
232
|
}
|
233
|
points.addPoint(p);
|
234
|
}
|
235
|
points.addPoint(new Point(end.x, end.y));
|
236
|
conn.setPoints(points);
|
237
|
}
|
238
|
|
239
|
/**
|
240
|
* @see ConnectionRouter#remove(Connection)
|
241
|
*/
|
242
|
public void remove(Connection connection) {
|
243
|
removeReservedLines(connection);
|
244
|
}
|
245
|
|
246
|
protected void removeReservedLines(Connection connection) {
|
247
|
ReservedInfo rInfo = (ReservedInfo) reservedInfo.get(connection);
|
248
|
if (rInfo == null)
|
249
|
return;
|
250
|
|
251
|
for (int i = 0; i < rInfo.reservedRows.size(); i++) {
|
252
|
rowsUsed.remove(rInfo.reservedRows.get(i));
|
253
|
}
|
254
|
for (int i = 0; i < rInfo.reservedCols.size(); i++) {
|
255
|
colsUsed.remove(rInfo.reservedCols.get(i));
|
256
|
}
|
257
|
reservedInfo.remove(connection);
|
258
|
}
|
259
|
|
260
|
protected void reserveColumn(Connection connection, Integer column) {
|
261
|
ReservedInfo info = (ReservedInfo) reservedInfo.get(connection);
|
262
|
if (info == null) {
|
263
|
info = new ReservedInfo();
|
264
|
reservedInfo.put(connection, info);
|
265
|
}
|
266
|
info.reservedCols.add(column);
|
267
|
}
|
268
|
|
269
|
protected void reserveRow(Connection connection, Integer row) {
|
270
|
ReservedInfo info = (ReservedInfo) reservedInfo.get(connection);
|
271
|
if (info == null) {
|
272
|
info = new ReservedInfo();
|
273
|
reservedInfo.put(connection, info);
|
274
|
}
|
275
|
info.reservedRows.add(row);
|
276
|
}
|
277
|
|
278
|
/**
|
279
|
* @see ConnectionRouter#route(Connection)
|
280
|
*/
|
281
|
public void route(Connection conn) {
|
282
|
if ((conn.getSourceAnchor() == null)
|
283
|
|| (conn.getTargetAnchor() == null))
|
284
|
return;
|
285
|
int i;
|
286
|
Point startPoint = getStartPoint(conn);
|
287
|
conn.translateToRelative(startPoint);
|
288
|
Point endPoint = getEndPoint(conn);
|
289
|
conn.translateToRelative(endPoint);
|
290
|
|
291
|
Ray start = new Ray(startPoint);
|
292
|
Ray end = new Ray(endPoint);
|
293
|
Ray average = start.getAveraged(end);
|
294
|
|
295
|
Ray direction = new Ray(start, end);
|
296
|
Ray startNormal = getStartDirection(conn);
|
297
|
Ray endNormal = getEndDirection(conn);
|
298
|
|
299
|
List positions = new ArrayList(5);
|
300
|
boolean horizontal = startNormal.isHorizontal();
|
301
|
if (horizontal)
|
302
|
positions.add(new Integer(start.y));
|
303
|
else
|
304
|
positions.add(new Integer(start.x));
|
305
|
horizontal = !horizontal;
|
306
|
|
307
|
if (startNormal.dotProduct(endNormal) == 0) {
|
308
|
if ((startNormal.dotProduct(direction) >= 0)
|
309
|
&& (endNormal.dotProduct(direction) <= 0)) {
|
310
|
// 0
|
311
|
} else {
|
312
|
// 2
|
313
|
if (startNormal.dotProduct(direction) < 0)
|
314
|
i = startNormal.similarity(start.getAdded(startNormal
|
315
|
.getScaled(10)));
|
316
|
else {
|
317
|
if (horizontal)
|
318
|
i = average.y;
|
319
|
else
|
320
|
i = average.x;
|
321
|
}
|
322
|
positions.add(new Integer(i));
|
323
|
horizontal = !horizontal;
|
324
|
|
325
|
if (endNormal.dotProduct(direction) > 0)
|
326
|
i = endNormal.similarity(end.getAdded(endNormal
|
327
|
.getScaled(10)));
|
328
|
else {
|
329
|
if (horizontal)
|
330
|
i = average.y;
|
331
|
else
|
332
|
i = average.x;
|
333
|
}
|
334
|
positions.add(new Integer(i));
|
335
|
horizontal = !horizontal;
|
336
|
}
|
337
|
} else {
|
338
|
if (startNormal.dotProduct(endNormal) > 0) {
|
339
|
// 1
|
340
|
if (startNormal.dotProduct(direction) >= 0)
|
341
|
i = startNormal.similarity(start.getAdded(startNormal
|
342
|
.getScaled(10)));
|
343
|
else
|
344
|
i = endNormal.similarity(end.getAdded(endNormal
|
345
|
.getScaled(10)));
|
346
|
positions.add(new Integer(i));
|
347
|
horizontal = !horizontal;
|
348
|
} else {
|
349
|
// 3 or 1
|
350
|
if (startNormal.dotProduct(direction) < 0) {
|
351
|
i = startNormal.similarity(start.getAdded(startNormal
|
352
|
.getScaled(10)));
|
353
|
positions.add(new Integer(i));
|
354
|
horizontal = !horizontal;
|
355
|
}
|
356
|
|
357
|
if (horizontal)
|
358
|
i = average.y;
|
359
|
else
|
360
|
i = average.x;
|
361
|
positions.add(new Integer(i));
|
362
|
horizontal = !horizontal;
|
363
|
|
364
|
if (startNormal.dotProduct(direction) < 0) {
|
365
|
i = endNormal.similarity(end.getAdded(endNormal
|
366
|
.getScaled(10)));
|
367
|
positions.add(new Integer(i));
|
368
|
horizontal = !horizontal;
|
369
|
}
|
370
|
}
|
371
|
}
|
372
|
if (horizontal)
|
373
|
positions.add(new Integer(end.y));
|
374
|
else
|
375
|
positions.add(new Integer(end.x));
|
376
|
|
377
|
processPositions(start, end, positions, startNormal.isHorizontal(),
|
378
|
conn);
|
379
|
}
|
380
|
|
381
|
}
|