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