1
28 package demo.layout.withoutview;
29
30 import y.base.DataProvider;
31 import y.base.Edge;
32 import y.base.Node;
33 import y.geom.YPoint;
34 import y.layout.IntersectionCalculator;
35 import y.layout.LayoutGraph;
36 import y.layout.NodeLayout;
37 import y.util.DataProviderAdapter;
38
39 import java.util.ArrayList;
40 import java.util.Iterator;
41
42
45 public class IntersectionCalculators {
46
53 static final Object SHAPE_DPKEY = "demo.layout.withoutview.IntersectionCalculator.SHAPE_DPKEY";
54
55
58 public static final int SHAPE_RECTANGLE = 0;
59
62 public static final int SHAPE_ELLIPSE = 1;
63
66 public static final int SHAPE_DIAMOND = 2;
67
68 private IntersectionCalculators() {
69 }
70
71
81 public static void addIntersectionCalculator(
82 final LayoutGraph graph, final DataProvider shapeDp, final boolean source
83 ) {
84 if (source) {
85 final Object key = IntersectionCalculator.SOURCE_INTERSECTION_CALCULATOR_DPKEY;
86 graph.addDataProvider(key, new IntersectionCalculatorProvider(shapeDp, true));
87 } else {
88 final Object key = IntersectionCalculator.TARGET_INTERSECTION_CALCULATOR_DPKEY;
89 graph.addDataProvider(key, new IntersectionCalculatorProvider(shapeDp, false));
90 }
91 }
92
93
97 private static class IntersectionCalculatorProvider extends DataProviderAdapter {
98 private final DataProvider shapeDp;
99 private final boolean source;
100 private final DiamondCalculator diamondCalculator;
101 private final EllipseCalculator ellipseCalculator;
102
103
110 IntersectionCalculatorProvider(
111 final DataProvider shapeDp,
112 final boolean source
113 ) {
114 this.shapeDp = shapeDp;
115 this.source = source;
116 diamondCalculator = new DiamondCalculator();
117 ellipseCalculator = new EllipseCalculator();
118 }
119
120
129 public Object get( final Object dataHolder ) {
130 final Edge edge = (Edge) dataHolder;
131 switch (getShape(source ? edge.source() : edge.target())) {
132 case SHAPE_DIAMOND:
133 return diamondCalculator;
134 case SHAPE_ELLIPSE:
135 return ellipseCalculator;
136 default:
137 return null;
138 }
139 }
140
141
149 private int getShape( final Node node ) {
150 return shapeDp == null ? SHAPE_RECTANGLE : shapeDp.getInt(node);
151 }
152 }
153
154
159 private static final class DiamondCalculator implements IntersectionCalculator {
160 private static final double EPS = 1e-10;
161
162
167 public YPoint calculateIntersectionPoint(
168 final NodeLayout nl,
169 final double xOffset, final double yOffset,
170 final double dx, final double dy
171 ) {
172 final double w = nl.getWidth();
173 final double w2 = w * 0.5;
174 final double minX = nl.getX();
175 final double maxX = minX + w;
176 final double cx = minX + w2;
177
178 final double h = nl.getHeight();
179 final double h2 = h * 0.5;
180 final double minY = nl.getY();
181 final double maxY = minY + h;
182 final double cy = minY + h2;
183
184 final double qx = cx + xOffset;
187 final double qy = cy + yOffset;
188
189 double px = qx - dx;
194 double py = qy - dy;
195 if (dx > 0) {
196 if (qx > minX) {
197 px = minX - 10;
198 py = qy + (px - qx)/dx * dy;
199 }
200 } else if (dx < 0) {
201 if (qx < maxX) {
202 px = maxX + 10;
203 py = qy + (px - qx)/dx * dy;
204 }
205 } else {
206 if (dy > 0) {
207 if (qy > minY) {
208 px = qx;
209 py = minY - 10;
210 }
211 } else if (dy < 0) {
212 if (qy < maxY) {
213 px = qx;
214 py = maxY + 10;
215 }
216 } else {
217 return null;
221 }
222 }
223
224 final ArrayList intersections = new ArrayList();
225
226 final double inf = -EPS;
227 final double sup = w2 + EPS;
228
229 final YPoint p1 = calcIntersection(px, py, qx, qy, cx, minY, minX, cy);
231 if (p1 != null) {
232 final double tmp = p1.getX() - minX;
235 if (inf < tmp && tmp < sup) {
237 intersections.add(p1);
238 }
239 }
240
241 final YPoint p2 = calcIntersection(px, py, qx, qy, minX, cy, cx, maxY);
243 if (p2 != null) {
244 final double tmp = p2.getX() - minX;
247 if (inf < tmp && tmp < sup) {
249 intersections.add(p2);
250 }
251 }
252
253 final YPoint p3 = calcIntersection(px, py, qx, qy, cx, maxY, maxX, cy);
255 if (p3 != null) {
256 final double tmp = maxX - p3.getX();
259 if (inf < tmp && tmp < sup) {
261 intersections.add(p3);
262 }
263 }
264
265 final YPoint p4 = calcIntersection(px, py, qx, qy, maxX, cy, cx, minY);
267 if (p4 != null) {
268 final double tmp = maxX - p4.getX();
271 if (inf < tmp && tmp < sup) {
273 intersections.add(p4);
274 }
275 }
276
277 if (intersections.isEmpty()) {
278 return null;
279 } else if (intersections.size() == 1) {
280 final YPoint p = (YPoint) intersections.get(0);
281 return new YPoint(p.getX() - cx, p.getY() - cy);
284 } else {
285 Iterator it = intersections.iterator();
289
290 YPoint result = (YPoint) it.next();
291 double distX = result.getX() - px;
292 double distY = result.getY() - py;
293 double distSqr = distX * distX + distY * distY;
294 while (it.hasNext()) {
295 final YPoint next = (YPoint) it.next();
296 distX = next.getX() - px;
297 distY = next.getY() - py;
298 final double tmp = distX * distX + distY * distY;
299 if (distSqr > tmp) {
300 distSqr = tmp;
301 result = next;
302 }
303 }
304
305 return new YPoint(result.getX() - cx, result.getY() - cy);
308 }
309 }
310
311 private static YPoint calcIntersection(
312 final double x1, final double y1,
313 final double x2, final double y2,
314 final double x3, final double y3,
315 final double x4, final double y4
316 ) {
317 final double a1 = y2 - y1;
318 final double b1 = x1 - x2;
319
320 final double a2 = y4 - y3;
321 final double b2 = x3 - x4;
322
323 final double det = a1 * b2 - a2 * b1;
324 if (det == 0) {
325 return null;
326 } else {
327 final double c1 = a1 * x1 + b1 * y1;
328 final double c2 = a2 * x3 + b2 * y3;
329 final double x = (b2 * c1 - b1 * c2) / det;
330 final double y = (a1 * c2 - a2 * c1) / det;
331 return new YPoint(x, y);
332 }
333 }
334 }
335
336
339 private static final class EllipseCalculator implements IntersectionCalculator {
340
348 public YPoint calculateIntersectionPoint(
349 final NodeLayout nl,
350 final double xOffset, final double yOffset,
351 final double dx, final double dy
352 ) {
353 final double rx = nl.getWidth() * 0.5;
354 final double ry = nl.getHeight() * 0.5;
355
356 final double v1x = dx / rx;
357 final double v1y = dy / ry;
358 final double v2x = xOffset / rx;
359 final double v2y = yOffset / ry;
360
361 final double a = v1x*v1x + v1y*v1y;
362 final double b = 2 * v1x * v2x + 2 * v1y * v2y;
363 final double c = v2x*v2x + v2y*v2y - 1;
364
365 final double det = b * b - 4 * a * c;
366 if (det < 0) {
367 return null;
368 } else if (det > 0) {
369 final double sqrt = Math.sqrt(det);
370 final double m = (-b - sqrt) / (2 * a);
371 return new YPoint(xOffset + m * dx, yOffset + m * dy);
372 } else {
373 final double m = -b / (2 * a);
374 return new YPoint(xOffset + m * dx, yOffset + m * dy);
375 }
376 }
377 }
378 }
379