]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/multileveldiagram/TransitionDiagram.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / multileveldiagram / TransitionDiagram.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.multileveldiagram;\r
13 \r
14 import java.awt.BasicStroke;\r
15 import java.awt.Color;\r
16 import java.awt.Stroke;\r
17 import java.awt.geom.AffineTransform;\r
18 import java.awt.geom.Path2D;\r
19 import java.awt.geom.Point2D;\r
20 import java.awt.geom.Rectangle2D;\r
21 import java.util.ArrayList;\r
22 import java.util.Collection;\r
23 import java.util.Collections;\r
24 import java.util.List;\r
25 import java.util.Map;\r
26 import java.util.WeakHashMap;\r
27 \r
28 import org.simantics.g2d.diagram.DiagramClass;\r
29 import org.simantics.g2d.diagram.IDiagram;\r
30 import org.simantics.g2d.diagram.handler.DataElementMap;\r
31 import org.simantics.g2d.diagram.handler.LifeCycle;\r
32 import org.simantics.g2d.diagram.handler.Topology;\r
33 import org.simantics.g2d.diagram.handler.impl.DataElementMapImpl;\r
34 import org.simantics.g2d.diagram.handler.impl.PickContextImpl;\r
35 import org.simantics.g2d.diagram.handler.impl.TransactionContextImpl;\r
36 import org.simantics.g2d.diagram.impl.AbstractDiagram;\r
37 import org.simantics.g2d.element.ElementClass;\r
38 import org.simantics.g2d.element.ElementHints;\r
39 import org.simantics.g2d.element.ElementUtils;\r
40 import org.simantics.g2d.element.IElement;\r
41 import org.simantics.g2d.element.handler.AdditionalColor;\r
42 import org.simantics.g2d.element.handler.BendsHandler;\r
43 import org.simantics.g2d.element.handler.BorderColor;\r
44 import org.simantics.g2d.element.handler.EdgeVisuals;\r
45 import org.simantics.g2d.element.handler.ElementHandler;\r
46 import org.simantics.g2d.element.handler.FillColor;\r
47 import org.simantics.g2d.element.handler.InternalSize;\r
48 import org.simantics.g2d.element.handler.SceneGraph;\r
49 import org.simantics.g2d.element.handler.TerminalTopology;\r
50 import org.simantics.g2d.element.handler.Text;\r
51 import org.simantics.g2d.element.handler.TextColor;\r
52 import org.simantics.g2d.element.handler.Transform;\r
53 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
54 import org.simantics.g2d.element.handler.impl.MoveImpl;\r
55 import org.simantics.g2d.element.handler.impl.proxy.IProxyProvider;\r
56 import org.simantics.g2d.element.handler.impl.proxy.ProxyHandler;\r
57 import org.simantics.g2d.element.handler.impl.proxy.ProxyLifeCycle;\r
58 import org.simantics.g2d.element.impl.Element;\r
59 import org.simantics.g2d.multileveldiagram.TransitionDiagram.ProxyFadePaint.FadeDir;\r
60 import org.simantics.g2d.utils.GeometryUtils;\r
61 import org.simantics.g2d.utils.PathUtils2;\r
62 import org.simantics.scenegraph.g2d.G2DParentNode;\r
63 import org.simantics.utils.ObjectUtils;\r
64 import org.simantics.utils.datastructures.hints.HintContext;\r
65 \r
66 /**\r
67  * Transition diagram is a diagram that is a transition between two diagrams.\r
68  * Diagram in transition cannot be edited.\r
69  * There is always upper and lower diagram.\r
70  * There are different trasition effects : Morph and blend\r
71  * <p>\r
72  * TODO monitor upper and lower diagrams for element changes\r
73  * TODO monitor upper and lower elements for KEY_OBJECT changes\r
74  * \r
75  * @author Toni Kalajainen\r
76  */\r
77 public class TransitionDiagram extends AbstractDiagram {\r
78 \r
79     TransitionDiagram() {\r
80         super(TRANSITION_DIAGRAM_CLASS, new HintContext());\r
81     }\r
82 \r
83     public static final Key KEY_UPPER_DIAGRAM = new KeyOf(IDiagram.class);\r
84     public static final Key KEY_LOWER_DIAGRAM = new KeyOf(IDiagram.class);\r
85     public static final Key KEY_TRANSITION_CLASS = new KeyOf(ElementClass.class);\r
86 \r
87     /** Element keys refering to elements of source diagram */\r
88     public static final Key KEY_UPPER_ELEMENT = new KeyOf(IElement.class);\r
89     public static final Key KEY_LOWER_ELEMENT = new KeyOf(IElement.class);\r
90     public static final Key KEY_SOURCE_ELEMENT = new KeyOf(IElement.class);\r
91 \r
92     /** Diagram hint, transition phase, value between 0..1, 0=upper, 1=lower */\r
93     public static final Key KEY_PHASE = new KeyOf(Double.class);\r
94 \r
95     public static final ElementClass MORPH_ELEMENT_CLASS = ElementClass.compile(MorphElementHandler.INSTANCE, MoveImpl.HANDLER);\r
96 \r
97     public static final DiagramClass TRANSITION_DIAGRAM_CLASS = DiagramClass.compile(\r
98             new PickContextImpl(),\r
99             new TransactionContextImpl(),\r
100             new MorphTopologyImpl(),\r
101             new TransitionDiagramHandler(),\r
102             new DataElementMapImpl()\r
103     );\r
104 \r
105     public static final IDiagram createTransitionDiagram(IDiagram upperDiagram, IDiagram lowerDiagram, ElementClass transitionClass)\r
106     {\r
107         assert(upperDiagram!=null && lowerDiagram!=null && transitionClass!=null);\r
108         TransitionDiagram d = new TransitionDiagram();\r
109         d.setHint(KEY_TRANSITION_CLASS, transitionClass);\r
110         d.setHint(KEY_UPPER_DIAGRAM, upperDiagram);\r
111         d.setHint(KEY_LOWER_DIAGRAM, lowerDiagram);\r
112         d.fireCreated();\r
113         return d;\r
114     }\r
115 \r
116     IDiagram upperDiagram, lowerDiagram;\r
117 \r
118     /**\r
119      * Set a new element class to an element\r
120      * @param d\r
121      * @param e\r
122      * @param clazz\r
123      * @return new element\r
124      */\r
125     private static IElement setElementClass(IElement e, ElementClass clazz)\r
126     {\r
127         IDiagram d = e.getDiagram();\r
128         if (e.getElementClass().equals(clazz)) return e;\r
129         Map<Key, Object> hints = e.getHints();\r
130         int index = d.getElements().indexOf(e);\r
131         d.removeElement(e);\r
132         e.destroy();\r
133         e = Element.spawnNew(clazz);\r
134         e.setHints(hints);\r
135         d.addElement(e);\r
136         d.moveTo(e, index);\r
137         return e;\r
138     }\r
139 \r
140     static class TransitionDiagramHandler implements LifeCycle {\r
141 \r
142         private static final Key KEY_COMPOSITION_LISTENER =\r
143             new KeyOf(CompositionListener.class);\r
144 \r
145         @Override\r
146         public void onDiagramCreated(final IDiagram transitionDiagram) {\r
147             // 1. listen to\r
148             CompositionListener cl = new CompositionListener() {\r
149                 @Override\r
150                 public void onElementAdded(IDiagram diagram, IElement element) {\r
151                     IDiagram upperDiagram = transitionDiagram.getHint(KEY_UPPER_DIAGRAM);\r
152                     IDiagram lowerDiagram = transitionDiagram.getHint(KEY_LOWER_DIAGRAM);\r
153                     boolean isUpper = diagram == upperDiagram;\r
154                     IDiagram oppositeDiagram = isUpper ? lowerDiagram : upperDiagram;\r
155                     // Element has been added to either upper or lower diagram\r
156                     DataElementMap m = diagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
157                     Object data = m.getData(diagram, element);\r
158 \r
159                     // Check if the opposite diagram has a valid counter part\r
160                     m = oppositeDiagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
161                     IElement counterPart = data==null?null:m.getElement(oppositeDiagram, data);\r
162 \r
163 \r
164                     ElementClass clazz;\r
165                     // Element has a counter-part, therefore it can be morphed\r
166                     if (counterPart!=null) {\r
167                         clazz = MORPH_ELEMENT_CLASS;\r
168                     } else\r
169                         // There is no counterpart, therefore it is a fading element\r
170                     {\r
171                         FadeDir dir = isUpper ? FadeDir.Out : FadeDir.In;\r
172                         clazz = getFadeElementClass(element.getElementClass(), dir);\r
173                     }\r
174 \r
175                     // Check if transition element already exists\r
176                     m = transitionDiagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
177                     IElement transitionElement = m.getElement(transitionDiagram, data);\r
178                     if (transitionElement!=null) {\r
179                         setElementClass(transitionElement, clazz);\r
180                         return;\r
181                     }\r
182 \r
183                     transitionElement = Element.instantiate(clazz, null);\r
184 \r
185                     // Element has a counter-part, therefore it can be morphed\r
186                     if (counterPart!=null) {\r
187                         transitionElement.setHint(KEY_UPPER_ELEMENT, isUpper?element:counterPart);\r
188                         transitionElement.setHint(KEY_LOWER_ELEMENT, isUpper?counterPart:element);\r
189                     } else\r
190                         // There is no counterpart, therefore it is a fading element\r
191                     {\r
192                         transitionElement.setHint(KEY_SOURCE_ELEMENT, element);\r
193                     }\r
194 \r
195                     if (data!=null)\r
196                         transitionElement.setHint(ElementHints.KEY_OBJECT, data);\r
197                     Element.fireCreated(transitionElement);\r
198                     transitionDiagram.addElement(transitionElement);\r
199 \r
200                 }\r
201                 @Override\r
202                 public void onElementRemoved(IDiagram diagram, IElement element) {\r
203                     IDiagram upperDiagram = transitionDiagram.getHint(KEY_UPPER_DIAGRAM);\r
204                     IDiagram lowerDiagram = transitionDiagram.getHint(KEY_LOWER_DIAGRAM);\r
205                     boolean isUpper = diagram == upperDiagram;\r
206                     IDiagram oppositeDiagram = isUpper ? lowerDiagram : upperDiagram;\r
207                     // Element has been added to either upper or lower diagram\r
208                     DataElementMap m = diagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
209                     Object data = m.getData(diagram, element);\r
210 \r
211                     // Check if the opposite diagram has a valid counter part\r
212                     m = oppositeDiagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
213                     IElement counterPart = data==null?null:m.getElement(oppositeDiagram, data);\r
214 \r
215                     // Check if transition element already exists\r
216                     m = transitionDiagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
217                     IElement transitionElement = m.getElement(transitionDiagram, data);\r
218                     if (transitionElement==null) {\r
219                         for (IElement e : transitionDiagram.getElements())\r
220                             if (e.getHint(KEY_SOURCE_ELEMENT) == element)\r
221                             {\r
222                                 transitionElement = e;\r
223                                 break;\r
224                             }\r
225                     }\r
226                     // There is a mix-up .. not too serious\r
227                     if (transitionElement==null)\r
228                         return;\r
229 \r
230                     // if class is morph and the counter part remains, transform transition element to fade element\r
231                     if (transitionElement.getElementClass().equals(MORPH_ELEMENT_CLASS) &&\r
232                             counterPart != null)\r
233                     {\r
234                         FadeDir dir = isUpper ? FadeDir.Out : FadeDir.In;\r
235                         ElementClass clazz = getFadeElementClass(element.getElementClass(), dir);\r
236                         transitionElement.removeHint(KEY_UPPER_ELEMENT);\r
237                         transitionElement.removeHint(KEY_LOWER_ELEMENT);\r
238                         setElementClass(transitionElement, clazz);\r
239                         transitionElement.setHint(KEY_SOURCE_ELEMENT, counterPart);\r
240                     } else {\r
241                         transitionDiagram.removeElement(transitionElement);\r
242                         transitionElement.destroy();\r
243                     }\r
244                 }\r
245             };\r
246             IDiagram upperDiagram = transitionDiagram.getHint(KEY_UPPER_DIAGRAM);\r
247             IDiagram lowerDiagram = transitionDiagram.getHint(KEY_LOWER_DIAGRAM);\r
248             upperDiagram.addCompositionListener(cl);\r
249             lowerDiagram.addCompositionListener(cl);\r
250             transitionDiagram.setHint(KEY_COMPOSITION_LISTENER, cl);\r
251 \r
252             // Add elements\r
253             for (IElement e : upperDiagram.getElements())\r
254                 cl.onElementAdded(upperDiagram, e);\r
255             for (IElement e : lowerDiagram.getElements())\r
256                 cl.onElementAdded(lowerDiagram, e);\r
257         }\r
258 \r
259         @Override\r
260         public void onDiagramDisposed(IDiagram diagram) {\r
261             // Remove listener\r
262             CompositionListener cl = diagram.getHint(KEY_COMPOSITION_LISTENER);\r
263             IDiagram upperDiagram = diagram.getHint(KEY_UPPER_DIAGRAM);\r
264             IDiagram lowerDiagram = diagram.getHint(KEY_LOWER_DIAGRAM);\r
265             upperDiagram.removeCompositionListener(cl);\r
266             lowerDiagram.removeCompositionListener(cl);\r
267             diagram.removeHint(KEY_COMPOSITION_LISTENER);\r
268         }\r
269 \r
270         @Override\r
271         public void onDiagramDestroyed(IDiagram diagram) {}\r
272         @Override\r
273         public void onDiagramLoaded(IDiagram diagram, Collection<IElement> initialElements) {}\r
274 \r
275     }\r
276 \r
277     // TODO REMOVE REDUNDANCY == Transitions to void\r
278     // In morph element handler there is always 2 counter parts\r
279     static class MorphElementHandler implements SceneGraph, FillColor, BorderColor, AdditionalColor, TextColor, Transform, InternalSize, EdgeVisuals, BendsHandler, Text {\r
280 \r
281         private static final long serialVersionUID = 1907473087657477787L;\r
282 \r
283         public static final MorphElementHandler INSTANCE = new MorphElementHandler();\r
284 \r
285         /** key for sub-diagram -> element Map */\r
286         public final static Key KEY_ELEMENT_MAP = new KeyOf(Map.class);\r
287 \r
288         static IElement getUpperSourceElement(IElement e)\r
289         {\r
290             return e.getHint(KEY_UPPER_ELEMENT);\r
291         }\r
292 \r
293         static IElement getLowerSourceElement(IElement e)\r
294         {\r
295             return e.getHint(KEY_LOWER_ELEMENT);\r
296         }\r
297 \r
298         /** Returns elements between when diagram is in transition */\r
299         static Transition getTransition(IElement e)\r
300         {\r
301             Transition t = new Transition();\r
302             Double phase = e.getDiagram().getHint(KEY_PHASE);\r
303             t.le = getLowerSourceElement(e);\r
304             t.ue = getUpperSourceElement(e);\r
305             t.phase = phase;\r
306             return t;\r
307         }\r
308 \r
309         static private class Transition {\r
310             // Upper Element and Lower Element\r
311             IElement ue, le;\r
312             double phase;\r
313         }\r
314 \r
315 //              @Override\r
316 //              public void paint(IElement e, ICanvasContext ctx,\r
317 //                              GraphicsContext elementGC, GraphicsContext controlGC) {\r
318 //                      Transition t = getTransition(e);\r
319 //                      List<Paint> empty = Collections.EMPTY_LIST;\r
320 //                      List<Paint> ups = t.ue==null?empty:t.ue.getElementClass().getItemsByClass(Paint.class);\r
321 //                      List<Paint> lps = t.le==null?empty:t.le.getElementClass().getItemsByClass(Paint.class);\r
322 //\r
323 //                      if (ObjectUtils.equals(ups, lps))\r
324 //                      {\r
325 //                              for (Paint lp : lps)\r
326 //                                      lp.paint(e, ctx, elementGC, controlGC);\r
327 //                              return;\r
328 //                      }\r
329 //\r
330 //                      if (!lps.isEmpty()) {\r
331 ////                            Graphics2D g = elementGC.createClone();\r
332 ////                            Graphics2D g2 = controlGC.createClone();\r
333 ////                            Composite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)t.phase);\r
334 ////                            g.setComposite(c);\r
335 ////                            g2.setComposite(c);\r
336 //                              GraphicsContextImpl newCtx = new GraphicsContextImpl(elementGC.getBounds(), elementGC.getNode());\r
337 //                              GraphicsContextImpl newCtx2 = new GraphicsContextImpl(controlGC.getBounds(), controlGC.getNode());\r
338 //                              for (Paint lp : lps)\r
339 //                              {\r
340 //                                      lp.paint(t.le, ctx, newCtx, newCtx2);\r
341 //                              }\r
342 //                              newCtx.dispose();\r
343 //                              newCtx2.dispose();\r
344 //                      }\r
345 //\r
346 //                      if (!ups.isEmpty()) {\r
347 ////                            Graphics2D g = elementGC.createClone();\r
348 ////                            Graphics2D g2 = controlGC.createClone();\r
349 ////                            Composite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1-(float)t.phase);\r
350 ////                            g.setComposite(c);\r
351 ////                            g2.setComposite(c);\r
352 //                              GraphicsContextImpl newCtx = new GraphicsContextImpl(elementGC.getBounds(), elementGC.getNode());\r
353 //                              GraphicsContextImpl newCtx2 = new GraphicsContextImpl(controlGC.getBounds(), controlGC.getNode());\r
354 //                              for (Paint up : ups)\r
355 //                              {\r
356 //                                      up.paint(t.ue, ctx, newCtx, newCtx2);\r
357 //                              }\r
358 //                              newCtx.dispose();\r
359 //                              newCtx2.dispose();\r
360 //                      }\r
361 //              }\r
362 \r
363         @Override\r
364         public AffineTransform getTransform(IElement e) {\r
365             Transition t = getTransition(e);\r
366             Transform ut = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(Transform.class);\r
367             Transform lt = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(Transform.class);\r
368             AffineTransform uat = ut==null?null:ut.getTransform(t.ue);\r
369             AffineTransform lat = lt==null?null:lt.getTransform(t.le);\r
370             if (uat==null) return lat;\r
371             if (lat==null) return uat;\r
372             // interpolate\r
373             double um[] = new double[6];\r
374             uat.getMatrix(um);\r
375             double lm[] = new double[6];\r
376             lat.getMatrix(lm);\r
377             double rm[] = new double[6];\r
378             for (int i=0; i<6; i++)\r
379                 rm[i] = um[i]*(1-t.phase) + lm[i]*(t.phase);\r
380             return new AffineTransform(rm);\r
381         }\r
382 \r
383         @Override\r
384         public void setTransform(IElement e, AffineTransform at) {\r
385         }\r
386 \r
387         // Bounds\r
388 \r
389         @Override\r
390         public Rectangle2D getBounds(IElement e, Rectangle2D size) {\r
391             Transition t = getTransition(e);\r
392             InternalSize ub = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(InternalSize.class);\r
393             InternalSize lb = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(InternalSize.class);\r
394             Rectangle2D us = ub==null?null:ub.getBounds(t.ue, null);\r
395             Rectangle2D ls = lb==null?null:lb.getBounds(t.le, null);\r
396             if (ls==null && us==null) return null;\r
397             if (size==null) size = new Rectangle2D.Double();\r
398             if (ls==null && us!=null) {\r
399                 size.setRect(us);\r
400                 return size;\r
401             }\r
402             if (us==null && ls!=null) {\r
403                 size.setRect(ls);\r
404                 return size;\r
405             }\r
406             // interpolate\r
407             double minX = us.getMinX() * (1-t.phase) + ls.getMinX() * (t.phase);\r
408             double minY = us.getMinY() * (1-t.phase) + ls.getMinY() * (t.phase);\r
409             double maxX = us.getMaxX() * (1-t.phase) + ls.getMaxX() * (t.phase);\r
410             double maxY = us.getMaxY() * (1-t.phase) + ls.getMaxY() * (t.phase);\r
411 \r
412             size.setRect(minX, minY, maxX-minX, maxY-minY);\r
413             return size;\r
414         }\r
415 \r
416 \r
417         @Override\r
418         public double getArrowSize(IElement e, EdgeEnd end) {\r
419             Transition t = getTransition(e);\r
420             EdgeVisuals uev = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
421             EdgeVisuals lev = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
422             double us = uev==null?0:uev.getArrowSize(t.ue, end);\r
423             double ls = lev==null?0:lev.getArrowSize(t.le, end);\r
424             return us*(1-t.phase) + ls*(t.phase);\r
425         }\r
426 \r
427         @Override\r
428         public StrokeType getStrokeType(IElement e) {\r
429             Transition t = getTransition(e);\r
430             EdgeVisuals uev = t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
431             EdgeVisuals lev = t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
432             StrokeType ust = uev==null?null:uev.getStrokeType(t.ue);\r
433             StrokeType lst = lev==null?null:lev.getStrokeType(t.le);\r
434             return ust==null?lst:ust;\r
435         }\r
436 \r
437         @Override\r
438         public ArrowType getArrowType(IElement e, EdgeEnd end) {\r
439             Transition t = getTransition(e);\r
440             EdgeVisuals uev = t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
441             EdgeVisuals lev = t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
442             ArrowType uat = uev==null?null:uev.getArrowType(t.ue, end);\r
443             ArrowType lat = lev==null?null:lev.getArrowType(t.le, end);\r
444             return uat==null?lat:uat;\r
445         }\r
446 \r
447         @Override\r
448         public Stroke getStroke(IElement e) {\r
449             Transition t = getTransition(e);\r
450             EdgeVisuals uev = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
451             EdgeVisuals lev = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
452             Stroke us = uev==null?null:uev.getStroke(t.ue);\r
453             Stroke ls = lev==null?null:lev.getStroke(t.le);\r
454             if (us==null) return ls;\r
455             if (ls==null) return us;\r
456             // interpolate width\r
457             if (!(us instanceof BasicStroke) || !(ls instanceof BasicStroke))\r
458                 return us;\r
459             BasicStroke bsu = (BasicStroke) us;\r
460             BasicStroke bsl = (BasicStroke) ls;\r
461             double width = bsu.getLineWidth() * (1-t.phase) + bsl.getLineWidth() * (t.phase);\r
462 \r
463             return new BasicStroke(\r
464                     (float)width,\r
465                     bsu.getEndCap(),\r
466                     bsu.getLineJoin(),\r
467                     bsu.getMiterLimit(),\r
468                     bsu.getDashArray(),\r
469                     bsu.getDashPhase());\r
470         }\r
471 \r
472         @Override\r
473         public void setArrowSize(IElement e, EdgeEnd end, double size) {\r
474         }\r
475 \r
476         @Override\r
477         public void setStrokeType(IElement e, StrokeType arrowType) {\r
478         }\r
479 \r
480         @Override\r
481         public void setArrowType(IElement e, EdgeEnd end, ArrowType arrowType) {\r
482         }\r
483 \r
484         @Override\r
485         public void setStroke(IElement e, Stroke s) {\r
486         }\r
487 \r
488 \r
489 \r
490 \r
491         @Override\r
492         public String getText(IElement e) {\r
493             Transition t = getTransition(e);\r
494             Text tu = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(Text.class);\r
495             Text tl = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(Text.class);\r
496             String su = tu==null?null:tu.getText(t.ue);\r
497             String sl = tl==null?null:tl.getText(t.le);\r
498 \r
499             return su==null?sl:su;\r
500         }\r
501 \r
502         @Override\r
503         public void setText(IElement e, String text) {\r
504         }\r
505 \r
506         @Override\r
507         public Color getFillColor(IElement e) {\r
508             Transition t = getTransition(e);\r
509             Color uc = ElementUtils.getFillColor(t.ue);\r
510             Color lc = ElementUtils.getFillColor(t.le);\r
511             if (uc==null) return lc;\r
512             if (lc==null) return uc;\r
513             return GeometryUtils.interpolate(uc, lc, t.phase);\r
514         }\r
515         @Override\r
516         public void setFillColor(IElement e, Color c) {\r
517         }\r
518         @Override\r
519         public Color getBorderColor(IElement e) {\r
520             Transition t = getTransition(e);\r
521             Color uc = ElementUtils.getBorderColor(t.ue);\r
522             Color lc = ElementUtils.getBorderColor(t.le);\r
523             if (uc==null) return lc;\r
524             if (lc==null) return uc;\r
525             return GeometryUtils.interpolate(uc, lc, t.phase);\r
526         }\r
527 \r
528         @Override\r
529         public void setBorderColor(IElement e, Color c) {\r
530         }\r
531 \r
532         @Override\r
533         public Color getAdditionalColor(IElement e) {\r
534             Transition t = getTransition(e);\r
535             Color uc = ElementUtils.getAdditionalColor(t.ue);\r
536             Color lc = ElementUtils.getAdditionalColor(t.le);\r
537             if (uc==null) return lc;\r
538             if (lc==null) return uc;\r
539             return GeometryUtils.interpolate(uc, lc, t.phase);\r
540         }\r
541 \r
542         @Override\r
543         public void setAdditionalColor(IElement e, Color c) {\r
544         }\r
545 \r
546         @Override\r
547         public Color getTextColor(IElement e) {\r
548             Transition t = getTransition(e);\r
549             Color uc = ElementUtils.getTextColor(t.ue);\r
550             Color lc = ElementUtils.getTextColor(t.le);\r
551             if (uc==null) return lc;\r
552             if (lc==null) return uc;\r
553             return GeometryUtils.interpolate(uc, lc, t.phase);\r
554         }\r
555 \r
556         @Override\r
557         public void setTextColor(IElement e, Color c) {\r
558         }\r
559 \r
560         @Override\r
561         public AngleType getAngleType(IElement e) {\r
562             Transition t = getTransition(e);\r
563             BendsHandler        ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
564             BendsHandler        leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
565             if (ueb!=null) return ueb.getAngleType(t.ue);\r
566             if (leb!=null) return leb.getAngleType(t.le);\r
567             return null;\r
568         }\r
569         @Override\r
570         public void setAngleType(IElement e, AngleType angleType) {\r
571         }\r
572         @Override\r
573         public Bend addBend(IElement e, int index, Point2D pos) {\r
574             return null;\r
575         }\r
576         @Override\r
577         public void getBendPosition(IElement e, Bend b, Point2D pos) {\r
578             // TODO make better later\r
579             Transition t = getTransition(e);\r
580             BendsHandler        ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
581             BendsHandler        leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
582             if (ueb!=null) ueb.getBendPosition(e, b, pos);\r
583             if (leb!=null) leb.getBendPosition(e, b, pos);\r
584         }\r
585         @Override\r
586         public void getBends(IElement e, List<Bend> bends) {\r
587             // TODO make better later\r
588             Transition t = getTransition(e);\r
589             BendsHandler        ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
590             BendsHandler        leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
591             if (ueb!=null) ueb.getBends(e, bends);\r
592             if (leb!=null) leb.getBends(e, bends);\r
593         }\r
594         @Override\r
595         public boolean removeBend(IElement e, Bend b) {\r
596             return false;\r
597         }\r
598 \r
599         @Override\r
600         public Path2D getPath(IElement e) {\r
601             Transition t = getTransition(e);\r
602             BendsHandler        ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
603             BendsHandler        leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
604             if (ueb==null && leb==null) return null;\r
605             if (ueb==null) return leb.getPath(t.le);\r
606             if (leb==null) return ueb.getPath(t.ue);\r
607             Path2D up = ueb.getPath(t.ue);\r
608             Path2D lp = leb.getPath(t.le);\r
609             // interpolate two paths\r
610             return PathUtils2.interpolatePaths(up, lp, t.phase);\r
611         }\r
612 \r
613         @Override\r
614         public void setPath(IElement e, Path2D p) {\r
615         }\r
616 \r
617         @Override\r
618         public int getBendsCount(IElement e) {\r
619             Transition t = getTransition(e);\r
620             BendsHandler        ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
621             BendsHandler        leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
622             if (leb!=null) return leb.getBendsCount(t.le);\r
623             if (ueb!=null) return ueb.getBendsCount(t.ue);\r
624             return 0;\r
625         }\r
626 \r
627         @Override\r
628         public void moveBend(IElement e, Bend b, Point2D pos) {\r
629             // TODO Auto-generated method stub\r
630 \r
631         }\r
632 \r
633         @Override\r
634         public void cleanup(IElement e) {\r
635             // TODO Auto-generated method stub\r
636 \r
637         }\r
638 \r
639         @Override\r
640         public void init(IElement e, G2DParentNode parent) {\r
641             Transition t = getTransition(e);\r
642             List<SceneGraph> empty = Collections.EMPTY_LIST;\r
643             List<SceneGraph> ups = t.ue==null?empty:t.ue.getElementClass().getItemsByClass(SceneGraph.class);\r
644             List<SceneGraph> lps = t.le==null?empty:t.le.getElementClass().getItemsByClass(SceneGraph.class);\r
645 \r
646             if (ObjectUtils.equals(ups, lps))\r
647             {\r
648                 for (SceneGraph lp : lps)\r
649                     lp.init(e, parent);\r
650                 return;\r
651             }\r
652 \r
653             if (!lps.isEmpty()) {\r
654                 for (SceneGraph lp : lps)\r
655                 {\r
656                     lp.init(t.le, parent);\r
657                 }\r
658             }\r
659 \r
660             if (!ups.isEmpty()) {\r
661                 for (SceneGraph up : ups)\r
662                 {\r
663                     up.init(t.ue, parent);\r
664                 }\r
665             }\r
666         }\r
667 \r
668     }\r
669 \r
670 \r
671     private static WeakHashMap<ElementClass, ElementClass> FADEIN_CLASSES =\r
672         new WeakHashMap<ElementClass, ElementClass>();\r
673     private static WeakHashMap<ElementClass, ElementClass> FADEOUT_CLASSES =\r
674         new WeakHashMap<ElementClass, ElementClass>();\r
675     public synchronized static ElementClass getFadeElementClass(ElementClass origClass, FadeDir dir)\r
676     {\r
677         if (dir==FadeDir.In)\r
678         {\r
679             ElementClass proxyClass = FADEIN_CLASSES.get(origClass);\r
680             if (proxyClass==null) {\r
681                 proxyClass = createFadeElementClass(origClass, dir);\r
682                 FADEIN_CLASSES.put(origClass, proxyClass);\r
683             }\r
684             return proxyClass;\r
685         } else {\r
686             ElementClass proxyClass = FADEOUT_CLASSES.get(origClass);\r
687             if (proxyClass==null) {\r
688                 proxyClass = createFadeElementClass(origClass, dir);\r
689                 FADEOUT_CLASSES.put(origClass, proxyClass);\r
690             }\r
691             return proxyClass;\r
692         }\r
693     }\r
694 \r
695     private static final IProxyProvider PROXY_PROVIDER =\r
696         new IProxyProvider() {\r
697         @Override\r
698         public IElement provide(IElement src) {\r
699             return src.getHint(KEY_SOURCE_ELEMENT);\r
700         }\r
701     };\r
702 \r
703     static ElementClass createFadeElementClass(ElementClass clazz, FadeDir dir)\r
704     {\r
705         List<ElementHandler> result = new ArrayList<ElementHandler>();\r
706         List<ElementHandler> lst = new ArrayList<ElementHandler>();\r
707         for (ElementHandler eh : clazz.getAll())\r
708         {\r
709             lst.clear();\r
710             ProxyHandler.addProxyElementHandlers(eh, PROXY_PROVIDER, lst);\r
711             for (ElementHandler eh2 : lst)\r
712             {\r
713                 if (eh2 instanceof ProxyLifeCycle) continue;\r
714                 if (eh2 instanceof TerminalTopology) continue;\r
715 \r
716                 if (eh2 instanceof SceneGraph) {\r
717                     result.add( new ProxyFadePaint(dir, (SceneGraph)eh) );\r
718                 } else {\r
719                     result.add(eh2);\r
720                 }\r
721             }\r
722         }\r
723         return ElementClass.compile(result);\r
724     }\r
725 \r
726     static class ProxyFadePaint implements SceneGraph {\r
727         /**\r
728          * \r
729          */\r
730         private static final long serialVersionUID = 3559624682436513231L;\r
731         static enum FadeDir {In, Out};\r
732         FadeDir dir;\r
733         SceneGraph orig;\r
734         public ProxyFadePaint(FadeDir dir, SceneGraph orig) {\r
735             this.dir = dir;\r
736             this.orig = orig;\r
737             assert(orig!=null);\r
738         }\r
739         public static IElement getSource(IElement element)\r
740         {\r
741             return element.getHint(KEY_SOURCE_ELEMENT);\r
742         }\r
743         public double getPhase(IElement element)\r
744         {\r
745             Double phase = element.getDiagram().getHint(KEY_PHASE);\r
746             if (phase==null) phase = 0.0;\r
747             if (dir == FadeDir.Out) phase = 1-phase;\r
748             return phase;\r
749         }\r
750         @Override\r
751         public void cleanup(IElement e) {\r
752             IElement sourceElement = getSource(e);\r
753             orig.cleanup(sourceElement);\r
754         }\r
755         @Override\r
756         public void init(IElement e, G2DParentNode parent) {\r
757             IElement sourceElement = getSource(e);\r
758             orig.init(sourceElement, parent);\r
759         }\r
760     }\r
761 \r
762     static class MorphTopologyImpl implements Topology {\r
763 \r
764         @Override\r
765         public void connect(IElement edge, EdgeEnd end, IElement node, Terminal terminal) {\r
766         }\r
767 \r
768         @Override\r
769         public void disconnect(IElement edge, EdgeEnd end, IElement node, Terminal terminal) {\r
770         }\r
771 \r
772         @Override\r
773         public Connection getConnection(IElement edge, EdgeEnd end) {\r
774             return null;\r
775         }\r
776 \r
777         @Override\r
778         public void getConnections(IElement node, Terminal terminal, Collection<Connection> connections) {\r
779         }\r
780 \r
781     }\r
782 \r
783 }\r