]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/DiagramUtils.java
Merge "Databoard and SCL enchancements."
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / diagram / DiagramUtils.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.g2d.diagram;\r
13 \r
14 import java.awt.geom.AffineTransform;\r
15 import java.awt.geom.Path2D;\r
16 import java.awt.geom.Point2D;\r
17 import java.awt.geom.Rectangle2D;\r
18 import java.util.ArrayList;\r
19 import java.util.Arrays;\r
20 import java.util.Collection;\r
21 import java.util.Collections;\r
22 import java.util.Iterator;\r
23 import java.util.List;\r
24 import java.util.Map;\r
25 import java.util.Set;\r
26 import java.util.function.Consumer;\r
27 \r
28 import org.simantics.g2d.canvas.Hints;\r
29 import org.simantics.g2d.canvas.ICanvasContext;\r
30 import org.simantics.g2d.connection.ConnectionEntity;\r
31 import org.simantics.g2d.connection.EndKeyOf;\r
32 import org.simantics.g2d.connection.TerminalKeyOf;\r
33 import org.simantics.g2d.connection.handler.ConnectionHandler;\r
34 import org.simantics.g2d.diagram.handler.PickContext;\r
35 import org.simantics.g2d.diagram.handler.PickRequest;\r
36 import org.simantics.g2d.diagram.handler.Topology;\r
37 import org.simantics.g2d.diagram.handler.Topology.Connection;\r
38 import org.simantics.g2d.diagram.handler.TransactionContext;\r
39 import org.simantics.g2d.diagram.handler.TransactionContext.Transaction;\r
40 import org.simantics.g2d.diagram.handler.TransactionContext.TransactionType;\r
41 import org.simantics.g2d.diagram.impl.Diagram;\r
42 import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil;\r
43 import org.simantics.g2d.element.ElementHints;\r
44 import org.simantics.g2d.element.ElementUtils;\r
45 import org.simantics.g2d.element.IElement;\r
46 import org.simantics.g2d.element.handler.BendsHandler;\r
47 import org.simantics.g2d.element.handler.BendsHandler.Bend;\r
48 import org.simantics.g2d.element.handler.Children;\r
49 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
50 import org.simantics.g2d.element.handler.InternalSize;\r
51 import org.simantics.g2d.element.handler.Transform;\r
52 import org.simantics.g2d.element.impl.Element;\r
53 import org.simantics.g2d.elementclass.BranchPoint;\r
54 import org.simantics.g2d.elementclass.BranchPoint.Direction;\r
55 import org.simantics.g2d.routing.ConnectionDirectionUtil;\r
56 import org.simantics.g2d.routing.Constants;\r
57 import org.simantics.g2d.routing.IConnection;\r
58 import org.simantics.g2d.routing.IRouter2;\r
59 import org.simantics.g2d.routing.TrivialRouter2;\r
60 import org.simantics.scenegraph.utils.GeometryUtils;\r
61 \r
62 import gnu.trove.map.hash.THashMap;\r
63 \r
64 /**\r
65  * @author Toni Kalajainen\r
66  * @author Antti Villberg\r
67  * @author Tuukka Lehtonen\r
68  */\r
69 public class DiagramUtils {\r
70 \r
71     /**\r
72      * Get rectangle that contains all elements or null if there are no elements.\r
73      * @param d\r
74      * @return rectangle or null\r
75      */\r
76     public static Rectangle2D getContentRect(IDiagram d)\r
77     {\r
78         return getContentRect(d.getElements());\r
79     }\r
80 \r
81     /**\r
82      * Get rectangle that contains all elements or null if there are no elements.\r
83      * @param d\r
84      * @return rectangle or null\r
85      */\r
86     public static Rectangle2D getContentRect(Collection<IElement> elements)\r
87     {\r
88         Rectangle2D diagramRect = null;\r
89         Rectangle2D elementRect = new Rectangle2D.Double();\r
90         for (IElement el : elements) {\r
91             if (ElementUtils.isHidden(el))\r
92                 continue;\r
93 \r
94             InternalSize size = el.getElementClass().getSingleItem(InternalSize.class);\r
95             elementRect.setRect(Double.NaN, Double.NaN, Double.NaN, Double.NaN);\r
96             size.getBounds(el, elementRect);\r
97                         if (!Double.isFinite(elementRect.getWidth()) || !Double.isFinite(elementRect.getHeight())\r
98                                         || !Double.isFinite(elementRect.getX()) || !Double.isFinite(elementRect.getY()))\r
99                 continue;\r
100 \r
101             Transform t = el.getElementClass().getSingleItem(Transform.class);\r
102             AffineTransform at = t.getTransform(el);\r
103             Rectangle2D transformedRect = GeometryUtils.transformRectangle(at, elementRect);\r
104             if (diagramRect==null)\r
105                 diagramRect = new Rectangle2D.Double( transformedRect.getX(), transformedRect.getY(), transformedRect.getWidth(), transformedRect.getHeight() );\r
106             else\r
107                 diagramRect.add(transformedRect);\r
108         }\r
109         return diagramRect;\r
110     }\r
111 \r
112     public static void pick(\r
113             IDiagram d,\r
114             PickRequest request,\r
115             Collection<IElement> result)\r
116     {\r
117         PickContext pc = d.getDiagramClass().getSingleItem(PickContext.class);\r
118         pc.pick(d, request, result);\r
119     }\r
120 \r
121     public static void invalidate(IDiagram d) {\r
122         //Task task = ThreadLog.BEGIN("DiagramUtils.invalidate");\r
123         d.setHint(Hints.KEY_DIRTY, Hints.VALUE_SG_DIRTY);\r
124         //task.end();\r
125     }\r
126 \r
127     private static final ThreadLocal<List<IElement>> elements = new ThreadLocal<List<IElement>>() {\r
128         @Override\r
129         protected java.util.List<IElement> initialValue() {\r
130             return new ArrayList<IElement>();\r
131         }\r
132     };\r
133 \r
134     /**\r
135      * @param d\r
136      * @param context\r
137      */\r
138     public static void validateAndFix(final IDiagram d, ICanvasContext context) {\r
139         //Task task = ThreadLog.BEGIN("DU.validateAndFix");\r
140         validateAndFix(d, d.getElements());\r
141         //task.end();\r
142     }\r
143 \r
144     /**\r
145      * @param d\r
146      * @param elementsToFix\r
147      */\r
148     public static void validateAndFix(final IDiagram d, Collection<IElement> elementsToFix) {\r
149         //Task task = ThreadLog.BEGIN("DU.validateAndFix(IDiagram, Set<IElement>)");\r
150 \r
151         IRouter2 defaultRouter = ElementUtils.getHintOrDefault(d, DiagramHints.ROUTE_ALGORITHM, TrivialRouter2.INSTANCE);\r
152         final Topology topology = d.getDiagramClass().getSingleItem(Topology.class);\r
153 \r
154         // Validate-and-fix is single-threaded.\r
155         List<IElement> segments = elements.get();\r
156         final Collection<IElement> unmodifiableSegments = Collections.unmodifiableList(segments);\r
157 \r
158         for (final IElement element : elementsToFix) {\r
159             if (!d.containsElement(element)) {\r
160                 System.err.println("Fixing element not contained by diagram " + d + ": " + element);\r
161                 continue;\r
162             }\r
163 \r
164             ConnectionHandler ch = element.getElementClass().getAtMostOneItemOfClass(ConnectionHandler.class);\r
165             if (ch == null)\r
166                 continue;\r
167 \r
168             segments.clear();\r
169             ch.getSegments(element, segments);\r
170             if (segments.isEmpty())\r
171                 continue;\r
172 \r
173             // Get connection-specific router or use diagram default.\r
174             IRouter2 router = ElementUtils.getHintOrDefault(element, DiagramHints.ROUTE_ALGORITHM, defaultRouter);\r
175 \r
176             for (final IElement e : unmodifiableSegments) {\r
177                 if (e.getElementClass().containsClass(BendsHandler.class)) {\r
178                     router.route(new IConnection() {\r
179 \r
180                         THashMap<IElement, Connector> branchPoints = new THashMap<IElement, Connector>();\r
181 \r
182                         @Override\r
183                         public Connector getBegin(Object seg) {\r
184                             IElement e = (IElement)seg;\r
185                             Connection begin = topology.getConnection(e, EdgeEnd.Begin);\r
186                             Connector connector = begin == null ? null : branchPoints.get(begin.node);\r
187                             if(connector != null)\r
188                                 return connector;\r
189                             connector = new Connector();\r
190                             if(begin==null) {\r
191                                 BendsHandler bends =\r
192                                     e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
193                                 List<Bend> bs = new ArrayList<Bend>();\r
194                                 bends.getBends(e, bs);\r
195                                 Point2D p = new Point2D.Double();\r
196                                 if(bs.size() > 0)\r
197                                     bends.getBendPosition(e, bs.get(0), p);\r
198                                 else\r
199                                     p.setLocation(0.0, 0.0);\r
200                                 AffineTransform elementTransform = ElementUtils.getTransform(e);\r
201                                 elementTransform.transform(p, p);\r
202                                 connector.x = p.getX();\r
203                                 connector.y = p.getY();\r
204                                 connector.allowedDirections = 0xf;\r
205                             }\r
206                             else {\r
207                                 AffineTransform at =\r
208                                     TerminalUtil.getTerminalPosOnDiagram(begin.node, begin.terminal);\r
209                                 connector.x = at.getTranslateX();\r
210                                 connector.y = at.getTranslateY();\r
211                                 connector.parentObstacle = getObstacleShape(begin.node);\r
212                                 BranchPoint bph = begin.node.getElementClass().getAtMostOneItemOfClass(BranchPoint.class);\r
213                                 if(bph != null) {\r
214                                     branchPoints.put(begin.node, connector);\r
215                                     connector.allowedDirections = toAllowedDirections( bph.getDirectionPreference(begin.node, Direction.Any) );\r
216                                 }\r
217                                 else\r
218                                     ConnectionDirectionUtil.determineAllowedDirections(connector);\r
219                             }\r
220                             return connector;\r
221                         }\r
222 \r
223                         private int toAllowedDirections(BranchPoint.Direction direction) {\r
224                             switch (direction) {\r
225                                 case Any:\r
226                                     return 0xf;\r
227                                 case Horizontal:\r
228                                     return Constants.EAST_FLAG | Constants.WEST_FLAG;\r
229                                 case Vertical:\r
230                                     return Constants.NORTH_FLAG | Constants.SOUTH_FLAG;\r
231                                 default:\r
232                                     throw new IllegalArgumentException("unrecognized direction: " + direction);\r
233                             }\r
234                         }\r
235 \r
236                         @Override\r
237                         public Connector getEnd(Object seg) {\r
238                             IElement e = (IElement)seg;\r
239                             Connection end = topology.getConnection(e, EdgeEnd.End);\r
240                             Connector connector = end == null ? null : branchPoints.get(end.node);\r
241                             if(connector != null)\r
242                                 return connector;\r
243                             connector = new Connector();\r
244                             if(end==null) {\r
245                                 BendsHandler bends =\r
246                                     e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
247                                 List<Bend> bs = new ArrayList<Bend>();\r
248                                 bends.getBends(e, bs);\r
249                                 Point2D p = new Point2D.Double();\r
250                                 if(bs.size() > 0)\r
251                                     bends.getBendPosition(e, bs.get(bs.size()-1), p);\r
252                                 else\r
253                                     p.setLocation(0.0, 0.0);\r
254                                 AffineTransform elementTransform = ElementUtils.getTransform(e);\r
255                                 elementTransform.transform(p, p);\r
256                                 connector.x = p.getX();\r
257                                 connector.y = p.getY();\r
258                                 connector.allowedDirections = 0xf;\r
259                             }\r
260                             else {\r
261 \r
262                                 AffineTransform at =\r
263                                     TerminalUtil.getTerminalPosOnDiagram(end.node, end.terminal);\r
264                                 connector.x = at.getTranslateX();\r
265                                 connector.y = at.getTranslateY();\r
266                                 connector.parentObstacle = getObstacleShape(end.node);\r
267                                 BranchPoint bph = end.node.getElementClass().getAtMostOneItemOfClass(BranchPoint.class);\r
268                                 if(bph != null) {\r
269                                     branchPoints.put(end.node, connector);\r
270                                     connector.allowedDirections = toAllowedDirections( bph.getDirectionPreference(end.node, Direction.Any) );\r
271                                 }\r
272                                 else\r
273                                     ConnectionDirectionUtil.determineAllowedDirections(connector);\r
274                             }\r
275                             return connector;\r
276                         }\r
277 \r
278                         @Override\r
279                         public Collection<? extends Object> getSegments() {\r
280                             return unmodifiableSegments;\r
281                         }\r
282 \r
283                         @Override\r
284                         public void setPath(Object seg, Path2D path) {\r
285                             IElement e = (IElement)seg;\r
286                             BendsHandler bends =\r
287                                 e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
288                             AffineTransform elementTransform = ElementUtils.getInvTransform(e);\r
289                             path = (Path2D)path.clone();\r
290                             path.transform(elementTransform);\r
291                             bends.setPath(e, path);\r
292                         }\r
293                     });\r
294                     //task2.end();\r
295                 }\r
296             }\r
297         }\r
298 \r
299         // Don't leave dangling references behind.\r
300         segments.clear();\r
301 \r
302         //task.end();\r
303     }\r
304 \r
305     /**\r
306      * Execute the specified {@link Runnable} with in a diagram transaction\r
307      * using the {@link TransactionContext} handler available in the\r
308      * {@link DiagramClass} of the specified {@link Diagram}.\r
309      * \r
310      * @param diagram the diagram to execute the transaction for\r
311      * @param type read or write (exclusive)\r
312      * @param r the runnable to execute\r
313      * \r
314      * @throws IllegalArgumentException if the specified diagram does not have a\r
315      *         {@link TransactionContext} handler\r
316      */\r
317     public static void inDiagramTransaction(IDiagram diagram, TransactionType type, Runnable r) {\r
318         TransactionContext ctx = diagram.getDiagramClass().getAtMostOneItemOfClass(TransactionContext.class);\r
319         if (ctx == null)\r
320             throw new IllegalArgumentException("Diagram does not have a TransactionContext handler: " + diagram\r
321                     + ". Cannot execute runnable " + r);\r
322 \r
323         Transaction txn = ctx.startTransaction(diagram, type);\r
324         try {\r
325             r.run();\r
326         } finally {\r
327             ctx.finishTransaction(diagram, txn);\r
328         }\r
329     }\r
330 \r
331     /**\r
332      * Execute the specified {@link Callback} within a diagram write transaction\r
333      * using the {@link TransactionContext} handler available in the\r
334      * {@link DiagramClass} of the specified {@link Diagram}. The diagram must\r
335      * contain a valid value for the {@link DiagramHints#KEY_MUTATOR} hint which\r
336      * is passed to the specified callback as an argument. This utility takes\r
337      * care of clearing the diagram mutator before callback invocation and\r
338      * clearing/committing its modifications after callback invocation depending\r
339      * on its success.\r
340      * \r
341      * @param diagram the diagram to execute the transaction for\r
342      * @param callback the runnable to execute\r
343      * \r
344      * @throws IllegalArgumentException if the specified diagram does not have a\r
345      *         {@link TransactionContext} handler or if the diagram does not\r
346      *         have a valid value for the {@link DiagramHints#KEY_MUTATOR} hint\r
347      */\r
348     public static void mutateDiagram(IDiagram diagram, Consumer<DiagramMutator> callback) {\r
349         DiagramMutator mutator = diagram.getHint(DiagramHints.KEY_MUTATOR);\r
350         if (mutator == null)\r
351             throw new IllegalArgumentException("Diagram does not have an associated DiagramMutator (see DiagramHints.KEY_MUTATOR).");\r
352 \r
353         TransactionContext ctx = diagram.getDiagramClass().getAtMostOneItemOfClass(TransactionContext.class);\r
354         if (ctx == null)\r
355             throw new IllegalArgumentException("Diagram does not have a TransactionContext handler: " + diagram\r
356                     + ". Cannot execute callback " + callback);\r
357 \r
358         Transaction txn = ctx.startTransaction(diagram, TransactionType.WRITE);\r
359         boolean committed = false;\r
360         try {\r
361             mutator.clear();\r
362             callback.accept(mutator);\r
363             mutator.commit();\r
364             committed = true;\r
365         } finally {\r
366             if (!committed)\r
367                 mutator.clear();\r
368             ctx.finishTransaction(diagram, txn);\r
369         }\r
370     }\r
371 \r
372     /**\r
373      * Invokes a diagram mutation that synchronizes the hints of all the\r
374      * specified elements into the back-end.\r
375      * \r
376      * @param diagram the diagram to mutate\r
377      * @param elements the elements to synchronize to the back-end\r
378      */\r
379     public static void synchronizeHintsToBackend(IDiagram diagram, final IElement... elements) {\r
380         synchronizeHintsToBackend(diagram, Arrays.asList(elements));\r
381     }\r
382 \r
383     /**\r
384      * Invokes a diagram mutation that synchronizes the hints of all the\r
385      * specified elements into the back-end.\r
386      * \r
387      * @param diagram the diagram to mutate\r
388      * @param elements the elements to synchronize to the back-end\r
389      */\r
390     public static void synchronizeHintsToBackend(IDiagram diagram, final Collection<IElement> elements) {\r
391         mutateDiagram(diagram, m -> {\r
392             for (IElement e : elements)\r
393                 m.synchronizeHintsToBackend(e);\r
394         });\r
395     }\r
396 \r
397     /**\r
398      * @param elements\r
399      * @return\r
400      */\r
401     public static Collection<IElement> withChildren(Collection<IElement> elements) {\r
402         ArrayList<IElement> result = new ArrayList<IElement>(elements.size()*2);\r
403         result.addAll(elements);\r
404         for (int pos = 0; pos < result.size(); ++pos) {\r
405             IElement element = result.get(pos);\r
406             Children children = element.getElementClass().getAtMostOneItemOfClass(Children.class);\r
407             if (children != null) {\r
408                 children.getChildren(element, result);\r
409             }\r
410         }\r
411         return result;\r
412     }\r
413 \r
414     /**\r
415      * @param elements\r
416      * @return\r
417      */\r
418     public static Collection<IElement> withDirectChildren(Collection<IElement> elements) {\r
419         ArrayList<IElement> result = new ArrayList<IElement>(elements);\r
420         return getDirectChildren(elements, result);\r
421     }\r
422 \r
423     /**\r
424      * @param elements\r
425      * @param\r
426      * @return\r
427      */\r
428     public static Collection<IElement> getDirectChildren(Collection<IElement> elements, Collection<IElement> result) {\r
429         for (IElement element : elements) {\r
430             Children children = element.getElementClass().getAtMostOneItemOfClass(Children.class);\r
431             if (children != null)\r
432                 children.getChildren(element, result);\r
433         }\r
434         return result;\r
435     }\r
436 \r
437     /**\r
438      * @param diagram\r
439      * @param e\r
440      */\r
441     public static void testInclusion(IDiagram diagram, IElement e) {\r
442         BendsHandler bh = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
443         BranchPoint bp = e.getElementClass().getAtMostOneItemOfClass(BranchPoint.class);\r
444 \r
445         assertAndPrint(e,e instanceof Element);\r
446 \r
447         if(bh == null && bp == null) {\r
448             assertAndPrint(e,diagram == e.peekDiagram());\r
449         } else {\r
450             assertAndPrint(e,e.peekDiagram() == null);\r
451             ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
452             assertAndPrint(e,ce != null);\r
453             assertAndPrint(e,diagram == ce.getConnection().getDiagram());\r
454         }\r
455     }\r
456 \r
457     /**\r
458      * @param diagram\r
459      */\r
460     public static void testDiagram(IDiagram diagram) {\r
461         if (!(diagram instanceof Diagram))\r
462             return;\r
463 \r
464         Collection<IElement> es = withChildren(diagram.getSnapshot());\r
465 \r
466         for (IElement e : es) {\r
467             System.out.println("test element " + e + " " + e.getElementClass());\r
468 \r
469             testInclusion(diagram, e);\r
470 \r
471             Set<Map.Entry<TerminalKeyOf, Object>> entrySet = e.getHintsOfClass(TerminalKeyOf.class).entrySet();\r
472 \r
473             for (Map.Entry<TerminalKeyOf, Object> entry : entrySet) {\r
474                 Connection c = (Connection) entry.getValue();\r
475                 testInclusion(diagram, c.node);\r
476                 testInclusion(diagram, c.edge);\r
477             }\r
478 \r
479             BendsHandler bh = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
480 \r
481             if (bh != null) {\r
482                 Collection<Object> values = e.getHintsOfClass(EndKeyOf.class).values();\r
483                 assertAndPrint(e, values.size() == 2);\r
484                 Iterator<Object> it = values.iterator();\r
485                 Connection e1 = (Connection)it.next();\r
486                 Connection e2 = (Connection)it.next();\r
487                 testInclusion(diagram, e1.node);\r
488                 testInclusion(diagram, e1.edge);\r
489                 testInclusion(diagram, e2.node);\r
490                 testInclusion(diagram, e2.edge);\r
491                 assertAndPrint(e, e1.end.equals(e2.end.other()));\r
492             }\r
493         }\r
494     }\r
495 \r
496     public static void pruneDiagram(IDiagram diagram) {\r
497         if (!(diagram instanceof Diagram))\r
498             return;\r
499 \r
500         Collection<IElement> es = withChildren(diagram.getSnapshot());\r
501 \r
502         for (IElement e : es) {\r
503             BendsHandler bh = e.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
504 \r
505             if (bh != null) {\r
506                 Set<Map.Entry<EndKeyOf, Object>> values = e.getHintsOfClass(EndKeyOf.class).entrySet();\r
507                 if (values.size() == 2) {\r
508                     Iterator<Map.Entry<EndKeyOf, Object>> it = values.iterator();\r
509                     Map.Entry<EndKeyOf, Object> e1 = it.next();\r
510                     Map.Entry<EndKeyOf, Object> e2 = it.next();\r
511                     if (!(((Connection) e1.getValue()).node instanceof Element)) {\r
512                         e.removeHint(e1.getKey());\r
513                         System.out.println("###################### PRUNED: " /*+ ((Connection)e1.getValue()).node*/);\r
514                     }\r
515                     if (!(((Connection) e2.getValue()).node instanceof Element)) {\r
516                         e.removeHint(e2.getKey());\r
517                         System.out.println("###################### PRUNED: " /*+ ((Connection)e2.getValue()).node*/);\r
518                     }\r
519                 }\r
520             }\r
521         }\r
522     }\r
523 \r
524     private static void assertAndPrint(IElement element, boolean condition) {\r
525         if(!condition) {\r
526             System.out.println("ASSERTION FAILED FOR");\r
527             System.out.println("-" + element);\r
528             System.out.println("-" + element.getElementClass());\r
529             assert(condition);\r
530         }\r
531     }\r
532 \r
533     public static Rectangle2D getObstacleShape(IElement e) {\r
534         Rectangle2D rect = ElementUtils.getElementBounds(e);\r
535         AffineTransform at = ElementUtils.getTransform(e);\r
536 \r
537         Point2D p1 = new Point2D.Double();\r
538         Point2D p2 = new Point2D.Double();\r
539 \r
540         p1.setLocation(rect.getMinX(), rect.getMinY());\r
541         at.transform(p1, p1);\r
542 \r
543         p2.setLocation(rect.getMaxX(), rect.getMaxY());\r
544         at.transform(p2, p2);\r
545 \r
546         double x0 = p1.getX();\r
547         double y0 = p1.getY();\r
548         double x1 = p2.getX();\r
549         double y1 = p2.getY();\r
550         if(x0 > x1) {\r
551             double temp = x0;\r
552             x0 = x1;\r
553             x1 = temp;\r
554         }\r
555         if(y0 > y1) {\r
556             double temp = y0;\r
557             y0 = y1;\r
558             y1 = temp;\r
559         }\r
560 \r
561         double OBSTACLE_MARGINAL = 1.0;\r
562         return new Rectangle2D.Double(\r
563                 x0-OBSTACLE_MARGINAL,\r
564                 y0-OBSTACLE_MARGINAL,\r
565                 (x1-x0)+OBSTACLE_MARGINAL*2,\r
566                 (y1-y0)+OBSTACLE_MARGINAL*2\r
567         );\r
568     }\r
569 \r
570 }\r