]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - 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
index 517aadbe22033e1008e25499d44ae830fcf5cb45..90186e8003efbe97ece066dc4cb38a7156924e7b 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.g2d.multileveldiagram;\r
-\r
-import java.awt.BasicStroke;\r
-import java.awt.Color;\r
-import java.awt.Stroke;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Path2D;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.WeakHashMap;\r
-\r
-import org.simantics.g2d.diagram.DiagramClass;\r
-import org.simantics.g2d.diagram.IDiagram;\r
-import org.simantics.g2d.diagram.handler.DataElementMap;\r
-import org.simantics.g2d.diagram.handler.LifeCycle;\r
-import org.simantics.g2d.diagram.handler.Topology;\r
-import org.simantics.g2d.diagram.handler.impl.DataElementMapImpl;\r
-import org.simantics.g2d.diagram.handler.impl.PickContextImpl;\r
-import org.simantics.g2d.diagram.handler.impl.TransactionContextImpl;\r
-import org.simantics.g2d.diagram.impl.AbstractDiagram;\r
-import org.simantics.g2d.element.ElementClass;\r
-import org.simantics.g2d.element.ElementHints;\r
-import org.simantics.g2d.element.ElementUtils;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.handler.AdditionalColor;\r
-import org.simantics.g2d.element.handler.BendsHandler;\r
-import org.simantics.g2d.element.handler.BorderColor;\r
-import org.simantics.g2d.element.handler.EdgeVisuals;\r
-import org.simantics.g2d.element.handler.ElementHandler;\r
-import org.simantics.g2d.element.handler.FillColor;\r
-import org.simantics.g2d.element.handler.InternalSize;\r
-import org.simantics.g2d.element.handler.SceneGraph;\r
-import org.simantics.g2d.element.handler.TerminalTopology;\r
-import org.simantics.g2d.element.handler.Text;\r
-import org.simantics.g2d.element.handler.TextColor;\r
-import org.simantics.g2d.element.handler.Transform;\r
-import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
-import org.simantics.g2d.element.handler.impl.MoveImpl;\r
-import org.simantics.g2d.element.handler.impl.proxy.IProxyProvider;\r
-import org.simantics.g2d.element.handler.impl.proxy.ProxyHandler;\r
-import org.simantics.g2d.element.handler.impl.proxy.ProxyLifeCycle;\r
-import org.simantics.g2d.element.impl.Element;\r
-import org.simantics.g2d.multileveldiagram.TransitionDiagram.ProxyFadePaint.FadeDir;\r
-import org.simantics.g2d.utils.GeometryUtils;\r
-import org.simantics.g2d.utils.PathUtils2;\r
-import org.simantics.scenegraph.g2d.G2DParentNode;\r
-import org.simantics.utils.ObjectUtils;\r
-import org.simantics.utils.datastructures.hints.HintContext;\r
-\r
-/**\r
- * Transition diagram is a diagram that is a transition between two diagrams.\r
- * Diagram in transition cannot be edited.\r
- * There is always upper and lower diagram.\r
- * There are different trasition effects : Morph and blend\r
- * <p>\r
- * TODO monitor upper and lower diagrams for element changes\r
- * TODO monitor upper and lower elements for KEY_OBJECT changes\r
- * \r
- * @author Toni Kalajainen\r
- */\r
-public class TransitionDiagram extends AbstractDiagram {\r
-\r
-    TransitionDiagram() {\r
-        super(TRANSITION_DIAGRAM_CLASS, new HintContext());\r
-    }\r
-\r
-    public static final Key KEY_UPPER_DIAGRAM = new KeyOf(IDiagram.class);\r
-    public static final Key KEY_LOWER_DIAGRAM = new KeyOf(IDiagram.class);\r
-    public static final Key KEY_TRANSITION_CLASS = new KeyOf(ElementClass.class);\r
-\r
-    /** Element keys refering to elements of source diagram */\r
-    public static final Key KEY_UPPER_ELEMENT = new KeyOf(IElement.class);\r
-    public static final Key KEY_LOWER_ELEMENT = new KeyOf(IElement.class);\r
-    public static final Key KEY_SOURCE_ELEMENT = new KeyOf(IElement.class);\r
-\r
-    /** Diagram hint, transition phase, value between 0..1, 0=upper, 1=lower */\r
-    public static final Key KEY_PHASE = new KeyOf(Double.class);\r
-\r
-    public static final ElementClass MORPH_ELEMENT_CLASS = ElementClass.compile(MorphElementHandler.INSTANCE, MoveImpl.HANDLER);\r
-\r
-    public static final DiagramClass TRANSITION_DIAGRAM_CLASS = DiagramClass.compile(\r
-            new PickContextImpl(),\r
-            new TransactionContextImpl(),\r
-            new MorphTopologyImpl(),\r
-            new TransitionDiagramHandler(),\r
-            new DataElementMapImpl()\r
-    );\r
-\r
-    public static final IDiagram createTransitionDiagram(IDiagram upperDiagram, IDiagram lowerDiagram, ElementClass transitionClass)\r
-    {\r
-        assert(upperDiagram!=null && lowerDiagram!=null && transitionClass!=null);\r
-        TransitionDiagram d = new TransitionDiagram();\r
-        d.setHint(KEY_TRANSITION_CLASS, transitionClass);\r
-        d.setHint(KEY_UPPER_DIAGRAM, upperDiagram);\r
-        d.setHint(KEY_LOWER_DIAGRAM, lowerDiagram);\r
-        d.fireCreated();\r
-        return d;\r
-    }\r
-\r
-    IDiagram upperDiagram, lowerDiagram;\r
-\r
-    /**\r
-     * Set a new element class to an element\r
-     * @param d\r
-     * @param e\r
-     * @param clazz\r
-     * @return new element\r
-     */\r
-    private static IElement setElementClass(IElement e, ElementClass clazz)\r
-    {\r
-        IDiagram d = e.getDiagram();\r
-        if (e.getElementClass().equals(clazz)) return e;\r
-        Map<Key, Object> hints = e.getHints();\r
-        int index = d.getElements().indexOf(e);\r
-        d.removeElement(e);\r
-        e.destroy();\r
-        e = Element.spawnNew(clazz);\r
-        e.setHints(hints);\r
-        d.addElement(e);\r
-        d.moveTo(e, index);\r
-        return e;\r
-    }\r
-\r
-    static class TransitionDiagramHandler implements LifeCycle {\r
-\r
-        private static final Key KEY_COMPOSITION_LISTENER =\r
-            new KeyOf(CompositionListener.class);\r
-\r
-        @Override\r
-        public void onDiagramCreated(final IDiagram transitionDiagram) {\r
-            // 1. listen to\r
-            CompositionListener cl = new CompositionListener() {\r
-                @Override\r
-                public void onElementAdded(IDiagram diagram, IElement element) {\r
-                    IDiagram upperDiagram = transitionDiagram.getHint(KEY_UPPER_DIAGRAM);\r
-                    IDiagram lowerDiagram = transitionDiagram.getHint(KEY_LOWER_DIAGRAM);\r
-                    boolean isUpper = diagram == upperDiagram;\r
-                    IDiagram oppositeDiagram = isUpper ? lowerDiagram : upperDiagram;\r
-                    // Element has been added to either upper or lower diagram\r
-                    DataElementMap m = diagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
-                    Object data = m.getData(diagram, element);\r
-\r
-                    // Check if the opposite diagram has a valid counter part\r
-                    m = oppositeDiagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
-                    IElement counterPart = data==null?null:m.getElement(oppositeDiagram, data);\r
-\r
-\r
-                    ElementClass clazz;\r
-                    // Element has a counter-part, therefore it can be morphed\r
-                    if (counterPart!=null) {\r
-                        clazz = MORPH_ELEMENT_CLASS;\r
-                    } else\r
-                        // There is no counterpart, therefore it is a fading element\r
-                    {\r
-                        FadeDir dir = isUpper ? FadeDir.Out : FadeDir.In;\r
-                        clazz = getFadeElementClass(element.getElementClass(), dir);\r
-                    }\r
-\r
-                    // Check if transition element already exists\r
-                    m = transitionDiagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
-                    IElement transitionElement = m.getElement(transitionDiagram, data);\r
-                    if (transitionElement!=null) {\r
-                        setElementClass(transitionElement, clazz);\r
-                        return;\r
-                    }\r
-\r
-                    transitionElement = Element.instantiate(clazz, null);\r
-\r
-                    // Element has a counter-part, therefore it can be morphed\r
-                    if (counterPart!=null) {\r
-                        transitionElement.setHint(KEY_UPPER_ELEMENT, isUpper?element:counterPart);\r
-                        transitionElement.setHint(KEY_LOWER_ELEMENT, isUpper?counterPart:element);\r
-                    } else\r
-                        // There is no counterpart, therefore it is a fading element\r
-                    {\r
-                        transitionElement.setHint(KEY_SOURCE_ELEMENT, element);\r
-                    }\r
-\r
-                    if (data!=null)\r
-                        transitionElement.setHint(ElementHints.KEY_OBJECT, data);\r
-                    Element.fireCreated(transitionElement);\r
-                    transitionDiagram.addElement(transitionElement);\r
-\r
-                }\r
-                @Override\r
-                public void onElementRemoved(IDiagram diagram, IElement element) {\r
-                    IDiagram upperDiagram = transitionDiagram.getHint(KEY_UPPER_DIAGRAM);\r
-                    IDiagram lowerDiagram = transitionDiagram.getHint(KEY_LOWER_DIAGRAM);\r
-                    boolean isUpper = diagram == upperDiagram;\r
-                    IDiagram oppositeDiagram = isUpper ? lowerDiagram : upperDiagram;\r
-                    // Element has been added to either upper or lower diagram\r
-                    DataElementMap m = diagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
-                    Object data = m.getData(diagram, element);\r
-\r
-                    // Check if the opposite diagram has a valid counter part\r
-                    m = oppositeDiagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
-                    IElement counterPart = data==null?null:m.getElement(oppositeDiagram, data);\r
-\r
-                    // Check if transition element already exists\r
-                    m = transitionDiagram.getDiagramClass().getSingleItem(DataElementMap.class);\r
-                    IElement transitionElement = m.getElement(transitionDiagram, data);\r
-                    if (transitionElement==null) {\r
-                        for (IElement e : transitionDiagram.getElements())\r
-                            if (e.getHint(KEY_SOURCE_ELEMENT) == element)\r
-                            {\r
-                                transitionElement = e;\r
-                                break;\r
-                            }\r
-                    }\r
-                    // There is a mix-up .. not too serious\r
-                    if (transitionElement==null)\r
-                        return;\r
-\r
-                    // if class is morph and the counter part remains, transform transition element to fade element\r
-                    if (transitionElement.getElementClass().equals(MORPH_ELEMENT_CLASS) &&\r
-                            counterPart != null)\r
-                    {\r
-                        FadeDir dir = isUpper ? FadeDir.Out : FadeDir.In;\r
-                        ElementClass clazz = getFadeElementClass(element.getElementClass(), dir);\r
-                        transitionElement.removeHint(KEY_UPPER_ELEMENT);\r
-                        transitionElement.removeHint(KEY_LOWER_ELEMENT);\r
-                        setElementClass(transitionElement, clazz);\r
-                        transitionElement.setHint(KEY_SOURCE_ELEMENT, counterPart);\r
-                    } else {\r
-                        transitionDiagram.removeElement(transitionElement);\r
-                        transitionElement.destroy();\r
-                    }\r
-                }\r
-            };\r
-            IDiagram upperDiagram = transitionDiagram.getHint(KEY_UPPER_DIAGRAM);\r
-            IDiagram lowerDiagram = transitionDiagram.getHint(KEY_LOWER_DIAGRAM);\r
-            upperDiagram.addCompositionListener(cl);\r
-            lowerDiagram.addCompositionListener(cl);\r
-            transitionDiagram.setHint(KEY_COMPOSITION_LISTENER, cl);\r
-\r
-            // Add elements\r
-            for (IElement e : upperDiagram.getElements())\r
-                cl.onElementAdded(upperDiagram, e);\r
-            for (IElement e : lowerDiagram.getElements())\r
-                cl.onElementAdded(lowerDiagram, e);\r
-        }\r
-\r
-        @Override\r
-        public void onDiagramDisposed(IDiagram diagram) {\r
-            // Remove listener\r
-            CompositionListener cl = diagram.getHint(KEY_COMPOSITION_LISTENER);\r
-            IDiagram upperDiagram = diagram.getHint(KEY_UPPER_DIAGRAM);\r
-            IDiagram lowerDiagram = diagram.getHint(KEY_LOWER_DIAGRAM);\r
-            upperDiagram.removeCompositionListener(cl);\r
-            lowerDiagram.removeCompositionListener(cl);\r
-            diagram.removeHint(KEY_COMPOSITION_LISTENER);\r
-        }\r
-\r
-        @Override\r
-        public void onDiagramDestroyed(IDiagram diagram) {}\r
-        @Override\r
-        public void onDiagramLoaded(IDiagram diagram, Collection<IElement> initialElements) {}\r
-\r
-    }\r
-\r
-    // TODO REMOVE REDUNDANCY == Transitions to void\r
-    // In morph element handler there is always 2 counter parts\r
-    static class MorphElementHandler implements SceneGraph, FillColor, BorderColor, AdditionalColor, TextColor, Transform, InternalSize, EdgeVisuals, BendsHandler, Text {\r
-\r
-        private static final long serialVersionUID = 1907473087657477787L;\r
-\r
-        public static final MorphElementHandler INSTANCE = new MorphElementHandler();\r
-\r
-        /** key for sub-diagram -> element Map */\r
-        public final static Key KEY_ELEMENT_MAP = new KeyOf(Map.class);\r
-\r
-        static IElement getUpperSourceElement(IElement e)\r
-        {\r
-            return e.getHint(KEY_UPPER_ELEMENT);\r
-        }\r
-\r
-        static IElement getLowerSourceElement(IElement e)\r
-        {\r
-            return e.getHint(KEY_LOWER_ELEMENT);\r
-        }\r
-\r
-        /** Returns elements between when diagram is in transition */\r
-        static Transition getTransition(IElement e)\r
-        {\r
-            Transition t = new Transition();\r
-            Double phase = e.getDiagram().getHint(KEY_PHASE);\r
-            t.le = getLowerSourceElement(e);\r
-            t.ue = getUpperSourceElement(e);\r
-            t.phase = phase;\r
-            return t;\r
-        }\r
-\r
-        static private class Transition {\r
-            // Upper Element and Lower Element\r
-            IElement ue, le;\r
-            double phase;\r
-        }\r
-\r
-//             @Override\r
-//             public void paint(IElement e, ICanvasContext ctx,\r
-//                             GraphicsContext elementGC, GraphicsContext controlGC) {\r
-//                     Transition t = getTransition(e);\r
-//                     List<Paint> empty = Collections.EMPTY_LIST;\r
-//                     List<Paint> ups = t.ue==null?empty:t.ue.getElementClass().getItemsByClass(Paint.class);\r
-//                     List<Paint> lps = t.le==null?empty:t.le.getElementClass().getItemsByClass(Paint.class);\r
-//\r
-//                     if (ObjectUtils.equals(ups, lps))\r
-//                     {\r
-//                             for (Paint lp : lps)\r
-//                                     lp.paint(e, ctx, elementGC, controlGC);\r
-//                             return;\r
-//                     }\r
-//\r
-//                     if (!lps.isEmpty()) {\r
-////                           Graphics2D g = elementGC.createClone();\r
-////                           Graphics2D g2 = controlGC.createClone();\r
-////                           Composite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)t.phase);\r
-////                           g.setComposite(c);\r
-////                           g2.setComposite(c);\r
-//                             GraphicsContextImpl newCtx = new GraphicsContextImpl(elementGC.getBounds(), elementGC.getNode());\r
-//                             GraphicsContextImpl newCtx2 = new GraphicsContextImpl(controlGC.getBounds(), controlGC.getNode());\r
-//                             for (Paint lp : lps)\r
-//                             {\r
-//                                     lp.paint(t.le, ctx, newCtx, newCtx2);\r
-//                             }\r
-//                             newCtx.dispose();\r
-//                             newCtx2.dispose();\r
-//                     }\r
-//\r
-//                     if (!ups.isEmpty()) {\r
-////                           Graphics2D g = elementGC.createClone();\r
-////                           Graphics2D g2 = controlGC.createClone();\r
-////                           Composite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1-(float)t.phase);\r
-////                           g.setComposite(c);\r
-////                           g2.setComposite(c);\r
-//                             GraphicsContextImpl newCtx = new GraphicsContextImpl(elementGC.getBounds(), elementGC.getNode());\r
-//                             GraphicsContextImpl newCtx2 = new GraphicsContextImpl(controlGC.getBounds(), controlGC.getNode());\r
-//                             for (Paint up : ups)\r
-//                             {\r
-//                                     up.paint(t.ue, ctx, newCtx, newCtx2);\r
-//                             }\r
-//                             newCtx.dispose();\r
-//                             newCtx2.dispose();\r
-//                     }\r
-//             }\r
-\r
-        @Override\r
-        public AffineTransform getTransform(IElement e) {\r
-            Transition t = getTransition(e);\r
-            Transform ut = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(Transform.class);\r
-            Transform lt = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(Transform.class);\r
-            AffineTransform uat = ut==null?null:ut.getTransform(t.ue);\r
-            AffineTransform lat = lt==null?null:lt.getTransform(t.le);\r
-            if (uat==null) return lat;\r
-            if (lat==null) return uat;\r
-            // interpolate\r
-            double um[] = new double[6];\r
-            uat.getMatrix(um);\r
-            double lm[] = new double[6];\r
-            lat.getMatrix(lm);\r
-            double rm[] = new double[6];\r
-            for (int i=0; i<6; i++)\r
-                rm[i] = um[i]*(1-t.phase) + lm[i]*(t.phase);\r
-            return new AffineTransform(rm);\r
-        }\r
-\r
-        @Override\r
-        public void setTransform(IElement e, AffineTransform at) {\r
-        }\r
-\r
-        // Bounds\r
-\r
-        @Override\r
-        public Rectangle2D getBounds(IElement e, Rectangle2D size) {\r
-            Transition t = getTransition(e);\r
-            InternalSize ub = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(InternalSize.class);\r
-            InternalSize lb = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(InternalSize.class);\r
-            Rectangle2D us = ub==null?null:ub.getBounds(t.ue, null);\r
-            Rectangle2D ls = lb==null?null:lb.getBounds(t.le, null);\r
-            if (ls==null && us==null) return null;\r
-            if (size==null) size = new Rectangle2D.Double();\r
-            if (ls==null && us!=null) {\r
-                size.setRect(us);\r
-                return size;\r
-            }\r
-            if (us==null && ls!=null) {\r
-                size.setRect(ls);\r
-                return size;\r
-            }\r
-            // interpolate\r
-            double minX = us.getMinX() * (1-t.phase) + ls.getMinX() * (t.phase);\r
-            double minY = us.getMinY() * (1-t.phase) + ls.getMinY() * (t.phase);\r
-            double maxX = us.getMaxX() * (1-t.phase) + ls.getMaxX() * (t.phase);\r
-            double maxY = us.getMaxY() * (1-t.phase) + ls.getMaxY() * (t.phase);\r
-\r
-            size.setRect(minX, minY, maxX-minX, maxY-minY);\r
-            return size;\r
-        }\r
-\r
-\r
-        @Override\r
-        public double getArrowSize(IElement e, EdgeEnd end) {\r
-            Transition t = getTransition(e);\r
-            EdgeVisuals uev = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
-            EdgeVisuals lev = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
-            double us = uev==null?0:uev.getArrowSize(t.ue, end);\r
-            double ls = lev==null?0:lev.getArrowSize(t.le, end);\r
-            return us*(1-t.phase) + ls*(t.phase);\r
-        }\r
-\r
-        @Override\r
-        public StrokeType getStrokeType(IElement e) {\r
-            Transition t = getTransition(e);\r
-            EdgeVisuals uev = t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
-            EdgeVisuals lev = t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
-            StrokeType ust = uev==null?null:uev.getStrokeType(t.ue);\r
-            StrokeType lst = lev==null?null:lev.getStrokeType(t.le);\r
-            return ust==null?lst:ust;\r
-        }\r
-\r
-        @Override\r
-        public ArrowType getArrowType(IElement e, EdgeEnd end) {\r
-            Transition t = getTransition(e);\r
-            EdgeVisuals uev = t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
-            EdgeVisuals lev = t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
-            ArrowType uat = uev==null?null:uev.getArrowType(t.ue, end);\r
-            ArrowType lat = lev==null?null:lev.getArrowType(t.le, end);\r
-            return uat==null?lat:uat;\r
-        }\r
-\r
-        @Override\r
-        public Stroke getStroke(IElement e) {\r
-            Transition t = getTransition(e);\r
-            EdgeVisuals uev = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
-            EdgeVisuals lev = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);\r
-            Stroke us = uev==null?null:uev.getStroke(t.ue);\r
-            Stroke ls = lev==null?null:lev.getStroke(t.le);\r
-            if (us==null) return ls;\r
-            if (ls==null) return us;\r
-            // interpolate width\r
-            if (!(us instanceof BasicStroke) || !(ls instanceof BasicStroke))\r
-                return us;\r
-            BasicStroke bsu = (BasicStroke) us;\r
-            BasicStroke bsl = (BasicStroke) ls;\r
-            double width = bsu.getLineWidth() * (1-t.phase) + bsl.getLineWidth() * (t.phase);\r
-\r
-            return new BasicStroke(\r
-                    (float)width,\r
-                    bsu.getEndCap(),\r
-                    bsu.getLineJoin(),\r
-                    bsu.getMiterLimit(),\r
-                    bsu.getDashArray(),\r
-                    bsu.getDashPhase());\r
-        }\r
-\r
-        @Override\r
-        public void setArrowSize(IElement e, EdgeEnd end, double size) {\r
-        }\r
-\r
-        @Override\r
-        public void setStrokeType(IElement e, StrokeType arrowType) {\r
-        }\r
-\r
-        @Override\r
-        public void setArrowType(IElement e, EdgeEnd end, ArrowType arrowType) {\r
-        }\r
-\r
-        @Override\r
-        public void setStroke(IElement e, Stroke s) {\r
-        }\r
-\r
-\r
-\r
-\r
-        @Override\r
-        public String getText(IElement e) {\r
-            Transition t = getTransition(e);\r
-            Text tu = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(Text.class);\r
-            Text tl = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(Text.class);\r
-            String su = tu==null?null:tu.getText(t.ue);\r
-            String sl = tl==null?null:tl.getText(t.le);\r
-\r
-            return su==null?sl:su;\r
-        }\r
-\r
-        @Override\r
-        public void setText(IElement e, String text) {\r
-        }\r
-\r
-        @Override\r
-        public Color getFillColor(IElement e) {\r
-            Transition t = getTransition(e);\r
-            Color uc = ElementUtils.getFillColor(t.ue);\r
-            Color lc = ElementUtils.getFillColor(t.le);\r
-            if (uc==null) return lc;\r
-            if (lc==null) return uc;\r
-            return GeometryUtils.interpolate(uc, lc, t.phase);\r
-        }\r
-        @Override\r
-        public void setFillColor(IElement e, Color c) {\r
-        }\r
-        @Override\r
-        public Color getBorderColor(IElement e) {\r
-            Transition t = getTransition(e);\r
-            Color uc = ElementUtils.getBorderColor(t.ue);\r
-            Color lc = ElementUtils.getBorderColor(t.le);\r
-            if (uc==null) return lc;\r
-            if (lc==null) return uc;\r
-            return GeometryUtils.interpolate(uc, lc, t.phase);\r
-        }\r
-\r
-        @Override\r
-        public void setBorderColor(IElement e, Color c) {\r
-        }\r
-\r
-        @Override\r
-        public Color getAdditionalColor(IElement e) {\r
-            Transition t = getTransition(e);\r
-            Color uc = ElementUtils.getAdditionalColor(t.ue);\r
-            Color lc = ElementUtils.getAdditionalColor(t.le);\r
-            if (uc==null) return lc;\r
-            if (lc==null) return uc;\r
-            return GeometryUtils.interpolate(uc, lc, t.phase);\r
-        }\r
-\r
-        @Override\r
-        public void setAdditionalColor(IElement e, Color c) {\r
-        }\r
-\r
-        @Override\r
-        public Color getTextColor(IElement e) {\r
-            Transition t = getTransition(e);\r
-            Color uc = ElementUtils.getTextColor(t.ue);\r
-            Color lc = ElementUtils.getTextColor(t.le);\r
-            if (uc==null) return lc;\r
-            if (lc==null) return uc;\r
-            return GeometryUtils.interpolate(uc, lc, t.phase);\r
-        }\r
-\r
-        @Override\r
-        public void setTextColor(IElement e, Color c) {\r
-        }\r
-\r
-        @Override\r
-        public AngleType getAngleType(IElement e) {\r
-            Transition t = getTransition(e);\r
-            BendsHandler       ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-            BendsHandler       leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-            if (ueb!=null) return ueb.getAngleType(t.ue);\r
-            if (leb!=null) return leb.getAngleType(t.le);\r
-            return null;\r
-        }\r
-        @Override\r
-        public void setAngleType(IElement e, AngleType angleType) {\r
-        }\r
-        @Override\r
-        public Bend addBend(IElement e, int index, Point2D pos) {\r
-            return null;\r
-        }\r
-        @Override\r
-        public void getBendPosition(IElement e, Bend b, Point2D pos) {\r
-            // TODO make better later\r
-            Transition t = getTransition(e);\r
-            BendsHandler       ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-            BendsHandler       leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-            if (ueb!=null) ueb.getBendPosition(e, b, pos);\r
-            if (leb!=null) leb.getBendPosition(e, b, pos);\r
-        }\r
-        @Override\r
-        public void getBends(IElement e, List<Bend> bends) {\r
-            // TODO make better later\r
-            Transition t = getTransition(e);\r
-            BendsHandler       ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-            BendsHandler       leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-            if (ueb!=null) ueb.getBends(e, bends);\r
-            if (leb!=null) leb.getBends(e, bends);\r
-        }\r
-        @Override\r
-        public boolean removeBend(IElement e, Bend b) {\r
-            return false;\r
-        }\r
-\r
-        @Override\r
-        public Path2D getPath(IElement e) {\r
-            Transition t = getTransition(e);\r
-            BendsHandler       ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-            BendsHandler       leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-            if (ueb==null && leb==null) return null;\r
-            if (ueb==null) return leb.getPath(t.le);\r
-            if (leb==null) return ueb.getPath(t.ue);\r
-            Path2D up = ueb.getPath(t.ue);\r
-            Path2D lp = leb.getPath(t.le);\r
-            // interpolate two paths\r
-            return PathUtils2.interpolatePaths(up, lp, t.phase);\r
-        }\r
-\r
-        @Override\r
-        public void setPath(IElement e, Path2D p) {\r
-        }\r
-\r
-        @Override\r
-        public int getBendsCount(IElement e) {\r
-            Transition t = getTransition(e);\r
-            BendsHandler       ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-            BendsHandler       leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
-            if (leb!=null) return leb.getBendsCount(t.le);\r
-            if (ueb!=null) return ueb.getBendsCount(t.ue);\r
-            return 0;\r
-        }\r
-\r
-        @Override\r
-        public void moveBend(IElement e, Bend b, Point2D pos) {\r
-            // TODO Auto-generated method stub\r
-\r
-        }\r
-\r
-        @Override\r
-        public void cleanup(IElement e) {\r
-            // TODO Auto-generated method stub\r
-\r
-        }\r
-\r
-        @Override\r
-        public void init(IElement e, G2DParentNode parent) {\r
-            Transition t = getTransition(e);\r
-            List<SceneGraph> empty = Collections.EMPTY_LIST;\r
-            List<SceneGraph> ups = t.ue==null?empty:t.ue.getElementClass().getItemsByClass(SceneGraph.class);\r
-            List<SceneGraph> lps = t.le==null?empty:t.le.getElementClass().getItemsByClass(SceneGraph.class);\r
-\r
-            if (ObjectUtils.equals(ups, lps))\r
-            {\r
-                for (SceneGraph lp : lps)\r
-                    lp.init(e, parent);\r
-                return;\r
-            }\r
-\r
-            if (!lps.isEmpty()) {\r
-                for (SceneGraph lp : lps)\r
-                {\r
-                    lp.init(t.le, parent);\r
-                }\r
-            }\r
-\r
-            if (!ups.isEmpty()) {\r
-                for (SceneGraph up : ups)\r
-                {\r
-                    up.init(t.ue, parent);\r
-                }\r
-            }\r
-        }\r
-\r
-    }\r
-\r
-\r
-    private static WeakHashMap<ElementClass, ElementClass> FADEIN_CLASSES =\r
-        new WeakHashMap<ElementClass, ElementClass>();\r
-    private static WeakHashMap<ElementClass, ElementClass> FADEOUT_CLASSES =\r
-        new WeakHashMap<ElementClass, ElementClass>();\r
-    public synchronized static ElementClass getFadeElementClass(ElementClass origClass, FadeDir dir)\r
-    {\r
-        if (dir==FadeDir.In)\r
-        {\r
-            ElementClass proxyClass = FADEIN_CLASSES.get(origClass);\r
-            if (proxyClass==null) {\r
-                proxyClass = createFadeElementClass(origClass, dir);\r
-                FADEIN_CLASSES.put(origClass, proxyClass);\r
-            }\r
-            return proxyClass;\r
-        } else {\r
-            ElementClass proxyClass = FADEOUT_CLASSES.get(origClass);\r
-            if (proxyClass==null) {\r
-                proxyClass = createFadeElementClass(origClass, dir);\r
-                FADEOUT_CLASSES.put(origClass, proxyClass);\r
-            }\r
-            return proxyClass;\r
-        }\r
-    }\r
-\r
-    private static final IProxyProvider PROXY_PROVIDER =\r
-        new IProxyProvider() {\r
-        @Override\r
-        public IElement provide(IElement src) {\r
-            return src.getHint(KEY_SOURCE_ELEMENT);\r
-        }\r
-    };\r
-\r
-    static ElementClass createFadeElementClass(ElementClass clazz, FadeDir dir)\r
-    {\r
-        List<ElementHandler> result = new ArrayList<ElementHandler>();\r
-        List<ElementHandler> lst = new ArrayList<ElementHandler>();\r
-        for (ElementHandler eh : clazz.getAll())\r
-        {\r
-            lst.clear();\r
-            ProxyHandler.addProxyElementHandlers(eh, PROXY_PROVIDER, lst);\r
-            for (ElementHandler eh2 : lst)\r
-            {\r
-                if (eh2 instanceof ProxyLifeCycle) continue;\r
-                if (eh2 instanceof TerminalTopology) continue;\r
-\r
-                if (eh2 instanceof SceneGraph) {\r
-                    result.add( new ProxyFadePaint(dir, (SceneGraph)eh) );\r
-                } else {\r
-                    result.add(eh2);\r
-                }\r
-            }\r
-        }\r
-        return ElementClass.compile(result);\r
-    }\r
-\r
-    static class ProxyFadePaint implements SceneGraph {\r
-        /**\r
-         * \r
-         */\r
-        private static final long serialVersionUID = 3559624682436513231L;\r
-        static enum FadeDir {In, Out};\r
-        FadeDir dir;\r
-        SceneGraph orig;\r
-        public ProxyFadePaint(FadeDir dir, SceneGraph orig) {\r
-            this.dir = dir;\r
-            this.orig = orig;\r
-            assert(orig!=null);\r
-        }\r
-        public static IElement getSource(IElement element)\r
-        {\r
-            return element.getHint(KEY_SOURCE_ELEMENT);\r
-        }\r
-        public double getPhase(IElement element)\r
-        {\r
-            Double phase = element.getDiagram().getHint(KEY_PHASE);\r
-            if (phase==null) phase = 0.0;\r
-            if (dir == FadeDir.Out) phase = 1-phase;\r
-            return phase;\r
-        }\r
-        @Override\r
-        public void cleanup(IElement e) {\r
-            IElement sourceElement = getSource(e);\r
-            orig.cleanup(sourceElement);\r
-        }\r
-        @Override\r
-        public void init(IElement e, G2DParentNode parent) {\r
-            IElement sourceElement = getSource(e);\r
-            orig.init(sourceElement, parent);\r
-        }\r
-    }\r
-\r
-    static class MorphTopologyImpl implements Topology {\r
-\r
-        @Override\r
-        public void connect(IElement edge, EdgeEnd end, IElement node, Terminal terminal) {\r
-        }\r
-\r
-        @Override\r
-        public void disconnect(IElement edge, EdgeEnd end, IElement node, Terminal terminal) {\r
-        }\r
-\r
-        @Override\r
-        public Connection getConnection(IElement edge, EdgeEnd end) {\r
-            return null;\r
-        }\r
-\r
-        @Override\r
-        public void getConnections(IElement node, Terminal terminal, Collection<Connection> connections) {\r
-        }\r
-\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.g2d.multileveldiagram;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Stroke;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.simantics.g2d.diagram.DiagramClass;
+import org.simantics.g2d.diagram.IDiagram;
+import org.simantics.g2d.diagram.handler.DataElementMap;
+import org.simantics.g2d.diagram.handler.LifeCycle;
+import org.simantics.g2d.diagram.handler.Topology;
+import org.simantics.g2d.diagram.handler.impl.DataElementMapImpl;
+import org.simantics.g2d.diagram.handler.impl.PickContextImpl;
+import org.simantics.g2d.diagram.handler.impl.TransactionContextImpl;
+import org.simantics.g2d.diagram.impl.AbstractDiagram;
+import org.simantics.g2d.element.ElementClass;
+import org.simantics.g2d.element.ElementHints;
+import org.simantics.g2d.element.ElementUtils;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.handler.AdditionalColor;
+import org.simantics.g2d.element.handler.BendsHandler;
+import org.simantics.g2d.element.handler.BorderColor;
+import org.simantics.g2d.element.handler.EdgeVisuals;
+import org.simantics.g2d.element.handler.ElementHandler;
+import org.simantics.g2d.element.handler.FillColor;
+import org.simantics.g2d.element.handler.InternalSize;
+import org.simantics.g2d.element.handler.SceneGraph;
+import org.simantics.g2d.element.handler.TerminalTopology;
+import org.simantics.g2d.element.handler.Text;
+import org.simantics.g2d.element.handler.TextColor;
+import org.simantics.g2d.element.handler.Transform;
+import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
+import org.simantics.g2d.element.handler.impl.MoveImpl;
+import org.simantics.g2d.element.handler.impl.proxy.IProxyProvider;
+import org.simantics.g2d.element.handler.impl.proxy.ProxyHandler;
+import org.simantics.g2d.element.handler.impl.proxy.ProxyLifeCycle;
+import org.simantics.g2d.element.impl.Element;
+import org.simantics.g2d.multileveldiagram.TransitionDiagram.ProxyFadePaint.FadeDir;
+import org.simantics.g2d.utils.GeometryUtils;
+import org.simantics.g2d.utils.PathUtils2;
+import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.utils.ObjectUtils;
+import org.simantics.utils.datastructures.hints.HintContext;
+
+/**
+ * Transition diagram is a diagram that is a transition between two diagrams.
+ * Diagram in transition cannot be edited.
+ * There is always upper and lower diagram.
+ * There are different trasition effects : Morph and blend
+ * <p>
+ * TODO monitor upper and lower diagrams for element changes
+ * TODO monitor upper and lower elements for KEY_OBJECT changes
+ * 
+ * @author Toni Kalajainen
+ */
+public class TransitionDiagram extends AbstractDiagram {
+
+    TransitionDiagram() {
+        super(TRANSITION_DIAGRAM_CLASS, new HintContext());
+    }
+
+    public static final Key KEY_UPPER_DIAGRAM = new KeyOf(IDiagram.class);
+    public static final Key KEY_LOWER_DIAGRAM = new KeyOf(IDiagram.class);
+    public static final Key KEY_TRANSITION_CLASS = new KeyOf(ElementClass.class);
+
+    /** Element keys refering to elements of source diagram */
+    public static final Key KEY_UPPER_ELEMENT = new KeyOf(IElement.class);
+    public static final Key KEY_LOWER_ELEMENT = new KeyOf(IElement.class);
+    public static final Key KEY_SOURCE_ELEMENT = new KeyOf(IElement.class);
+
+    /** Diagram hint, transition phase, value between 0..1, 0=upper, 1=lower */
+    public static final Key KEY_PHASE = new KeyOf(Double.class);
+
+    public static final ElementClass MORPH_ELEMENT_CLASS = ElementClass.compile(MorphElementHandler.INSTANCE, MoveImpl.HANDLER);
+
+    public static final DiagramClass TRANSITION_DIAGRAM_CLASS = DiagramClass.compile(
+            new PickContextImpl(),
+            new TransactionContextImpl(),
+            new MorphTopologyImpl(),
+            new TransitionDiagramHandler(),
+            new DataElementMapImpl()
+    );
+
+    public static final IDiagram createTransitionDiagram(IDiagram upperDiagram, IDiagram lowerDiagram, ElementClass transitionClass)
+    {
+        assert(upperDiagram!=null && lowerDiagram!=null && transitionClass!=null);
+        TransitionDiagram d = new TransitionDiagram();
+        d.setHint(KEY_TRANSITION_CLASS, transitionClass);
+        d.setHint(KEY_UPPER_DIAGRAM, upperDiagram);
+        d.setHint(KEY_LOWER_DIAGRAM, lowerDiagram);
+        d.fireCreated();
+        return d;
+    }
+
+    IDiagram upperDiagram, lowerDiagram;
+
+    /**
+     * Set a new element class to an element
+     * @param d
+     * @param e
+     * @param clazz
+     * @return new element
+     */
+    private static IElement setElementClass(IElement e, ElementClass clazz)
+    {
+        IDiagram d = e.getDiagram();
+        if (e.getElementClass().equals(clazz)) return e;
+        Map<Key, Object> hints = e.getHints();
+        int index = d.getElements().indexOf(e);
+        d.removeElement(e);
+        e.destroy();
+        e = Element.spawnNew(clazz);
+        e.setHints(hints);
+        d.addElement(e);
+        d.moveTo(e, index);
+        return e;
+    }
+
+    static class TransitionDiagramHandler implements LifeCycle {
+
+        private static final Key KEY_COMPOSITION_LISTENER =
+            new KeyOf(CompositionListener.class);
+
+        @Override
+        public void onDiagramCreated(final IDiagram transitionDiagram) {
+            // 1. listen to
+            CompositionListener cl = new CompositionListener() {
+                @Override
+                public void onElementAdded(IDiagram diagram, IElement element) {
+                    IDiagram upperDiagram = transitionDiagram.getHint(KEY_UPPER_DIAGRAM);
+                    IDiagram lowerDiagram = transitionDiagram.getHint(KEY_LOWER_DIAGRAM);
+                    boolean isUpper = diagram == upperDiagram;
+                    IDiagram oppositeDiagram = isUpper ? lowerDiagram : upperDiagram;
+                    // Element has been added to either upper or lower diagram
+                    DataElementMap m = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
+                    Object data = m.getData(diagram, element);
+
+                    // Check if the opposite diagram has a valid counter part
+                    m = oppositeDiagram.getDiagramClass().getSingleItem(DataElementMap.class);
+                    IElement counterPart = data==null?null:m.getElement(oppositeDiagram, data);
+
+
+                    ElementClass clazz;
+                    // Element has a counter-part, therefore it can be morphed
+                    if (counterPart!=null) {
+                        clazz = MORPH_ELEMENT_CLASS;
+                    } else
+                        // There is no counterpart, therefore it is a fading element
+                    {
+                        FadeDir dir = isUpper ? FadeDir.Out : FadeDir.In;
+                        clazz = getFadeElementClass(element.getElementClass(), dir);
+                    }
+
+                    // Check if transition element already exists
+                    m = transitionDiagram.getDiagramClass().getSingleItem(DataElementMap.class);
+                    IElement transitionElement = m.getElement(transitionDiagram, data);
+                    if (transitionElement!=null) {
+                        setElementClass(transitionElement, clazz);
+                        return;
+                    }
+
+                    transitionElement = Element.instantiate(clazz, null);
+
+                    // Element has a counter-part, therefore it can be morphed
+                    if (counterPart!=null) {
+                        transitionElement.setHint(KEY_UPPER_ELEMENT, isUpper?element:counterPart);
+                        transitionElement.setHint(KEY_LOWER_ELEMENT, isUpper?counterPart:element);
+                    } else
+                        // There is no counterpart, therefore it is a fading element
+                    {
+                        transitionElement.setHint(KEY_SOURCE_ELEMENT, element);
+                    }
+
+                    if (data!=null)
+                        transitionElement.setHint(ElementHints.KEY_OBJECT, data);
+                    Element.fireCreated(transitionElement);
+                    transitionDiagram.addElement(transitionElement);
+
+                }
+                @Override
+                public void onElementRemoved(IDiagram diagram, IElement element) {
+                    IDiagram upperDiagram = transitionDiagram.getHint(KEY_UPPER_DIAGRAM);
+                    IDiagram lowerDiagram = transitionDiagram.getHint(KEY_LOWER_DIAGRAM);
+                    boolean isUpper = diagram == upperDiagram;
+                    IDiagram oppositeDiagram = isUpper ? lowerDiagram : upperDiagram;
+                    // Element has been added to either upper or lower diagram
+                    DataElementMap m = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
+                    Object data = m.getData(diagram, element);
+
+                    // Check if the opposite diagram has a valid counter part
+                    m = oppositeDiagram.getDiagramClass().getSingleItem(DataElementMap.class);
+                    IElement counterPart = data==null?null:m.getElement(oppositeDiagram, data);
+
+                    // Check if transition element already exists
+                    m = transitionDiagram.getDiagramClass().getSingleItem(DataElementMap.class);
+                    IElement transitionElement = m.getElement(transitionDiagram, data);
+                    if (transitionElement==null) {
+                        for (IElement e : transitionDiagram.getElements())
+                            if (e.getHint(KEY_SOURCE_ELEMENT) == element)
+                            {
+                                transitionElement = e;
+                                break;
+                            }
+                    }
+                    // There is a mix-up .. not too serious
+                    if (transitionElement==null)
+                        return;
+
+                    // if class is morph and the counter part remains, transform transition element to fade element
+                    if (transitionElement.getElementClass().equals(MORPH_ELEMENT_CLASS) &&
+                            counterPart != null)
+                    {
+                        FadeDir dir = isUpper ? FadeDir.Out : FadeDir.In;
+                        ElementClass clazz = getFadeElementClass(element.getElementClass(), dir);
+                        transitionElement.removeHint(KEY_UPPER_ELEMENT);
+                        transitionElement.removeHint(KEY_LOWER_ELEMENT);
+                        setElementClass(transitionElement, clazz);
+                        transitionElement.setHint(KEY_SOURCE_ELEMENT, counterPart);
+                    } else {
+                        transitionDiagram.removeElement(transitionElement);
+                        transitionElement.destroy();
+                    }
+                }
+            };
+            IDiagram upperDiagram = transitionDiagram.getHint(KEY_UPPER_DIAGRAM);
+            IDiagram lowerDiagram = transitionDiagram.getHint(KEY_LOWER_DIAGRAM);
+            upperDiagram.addCompositionListener(cl);
+            lowerDiagram.addCompositionListener(cl);
+            transitionDiagram.setHint(KEY_COMPOSITION_LISTENER, cl);
+
+            // Add elements
+            for (IElement e : upperDiagram.getElements())
+                cl.onElementAdded(upperDiagram, e);
+            for (IElement e : lowerDiagram.getElements())
+                cl.onElementAdded(lowerDiagram, e);
+        }
+
+        @Override
+        public void onDiagramDisposed(IDiagram diagram) {
+            // Remove listener
+            CompositionListener cl = diagram.getHint(KEY_COMPOSITION_LISTENER);
+            IDiagram upperDiagram = diagram.getHint(KEY_UPPER_DIAGRAM);
+            IDiagram lowerDiagram = diagram.getHint(KEY_LOWER_DIAGRAM);
+            upperDiagram.removeCompositionListener(cl);
+            lowerDiagram.removeCompositionListener(cl);
+            diagram.removeHint(KEY_COMPOSITION_LISTENER);
+        }
+
+        @Override
+        public void onDiagramDestroyed(IDiagram diagram) {}
+        @Override
+        public void onDiagramLoaded(IDiagram diagram, Collection<IElement> initialElements) {}
+
+    }
+
+    // TODO REMOVE REDUNDANCY == Transitions to void
+    // In morph element handler there is always 2 counter parts
+    static class MorphElementHandler implements SceneGraph, FillColor, BorderColor, AdditionalColor, TextColor, Transform, InternalSize, EdgeVisuals, BendsHandler, Text {
+
+        private static final long serialVersionUID = 1907473087657477787L;
+
+        public static final MorphElementHandler INSTANCE = new MorphElementHandler();
+
+        /** key for sub-diagram -> element Map */
+        public final static Key KEY_ELEMENT_MAP = new KeyOf(Map.class);
+
+        static IElement getUpperSourceElement(IElement e)
+        {
+            return e.getHint(KEY_UPPER_ELEMENT);
+        }
+
+        static IElement getLowerSourceElement(IElement e)
+        {
+            return e.getHint(KEY_LOWER_ELEMENT);
+        }
+
+        /** Returns elements between when diagram is in transition */
+        static Transition getTransition(IElement e)
+        {
+            Transition t = new Transition();
+            Double phase = e.getDiagram().getHint(KEY_PHASE);
+            t.le = getLowerSourceElement(e);
+            t.ue = getUpperSourceElement(e);
+            t.phase = phase;
+            return t;
+        }
+
+        static private class Transition {
+            // Upper Element and Lower Element
+            IElement ue, le;
+            double phase;
+        }
+
+//             @Override
+//             public void paint(IElement e, ICanvasContext ctx,
+//                             GraphicsContext elementGC, GraphicsContext controlGC) {
+//                     Transition t = getTransition(e);
+//                     List<Paint> empty = Collections.EMPTY_LIST;
+//                     List<Paint> ups = t.ue==null?empty:t.ue.getElementClass().getItemsByClass(Paint.class);
+//                     List<Paint> lps = t.le==null?empty:t.le.getElementClass().getItemsByClass(Paint.class);
+//
+//                     if (ObjectUtils.equals(ups, lps))
+//                     {
+//                             for (Paint lp : lps)
+//                                     lp.paint(e, ctx, elementGC, controlGC);
+//                             return;
+//                     }
+//
+//                     if (!lps.isEmpty()) {
+////                           Graphics2D g = elementGC.createClone();
+////                           Graphics2D g2 = controlGC.createClone();
+////                           Composite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)t.phase);
+////                           g.setComposite(c);
+////                           g2.setComposite(c);
+//                             GraphicsContextImpl newCtx = new GraphicsContextImpl(elementGC.getBounds(), elementGC.getNode());
+//                             GraphicsContextImpl newCtx2 = new GraphicsContextImpl(controlGC.getBounds(), controlGC.getNode());
+//                             for (Paint lp : lps)
+//                             {
+//                                     lp.paint(t.le, ctx, newCtx, newCtx2);
+//                             }
+//                             newCtx.dispose();
+//                             newCtx2.dispose();
+//                     }
+//
+//                     if (!ups.isEmpty()) {
+////                           Graphics2D g = elementGC.createClone();
+////                           Graphics2D g2 = controlGC.createClone();
+////                           Composite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1-(float)t.phase);
+////                           g.setComposite(c);
+////                           g2.setComposite(c);
+//                             GraphicsContextImpl newCtx = new GraphicsContextImpl(elementGC.getBounds(), elementGC.getNode());
+//                             GraphicsContextImpl newCtx2 = new GraphicsContextImpl(controlGC.getBounds(), controlGC.getNode());
+//                             for (Paint up : ups)
+//                             {
+//                                     up.paint(t.ue, ctx, newCtx, newCtx2);
+//                             }
+//                             newCtx.dispose();
+//                             newCtx2.dispose();
+//                     }
+//             }
+
+        @Override
+        public AffineTransform getTransform(IElement e) {
+            Transition t = getTransition(e);
+            Transform ut = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(Transform.class);
+            Transform lt = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(Transform.class);
+            AffineTransform uat = ut==null?null:ut.getTransform(t.ue);
+            AffineTransform lat = lt==null?null:lt.getTransform(t.le);
+            if (uat==null) return lat;
+            if (lat==null) return uat;
+            // interpolate
+            double um[] = new double[6];
+            uat.getMatrix(um);
+            double lm[] = new double[6];
+            lat.getMatrix(lm);
+            double rm[] = new double[6];
+            for (int i=0; i<6; i++)
+                rm[i] = um[i]*(1-t.phase) + lm[i]*(t.phase);
+            return new AffineTransform(rm);
+        }
+
+        @Override
+        public void setTransform(IElement e, AffineTransform at) {
+        }
+
+        // Bounds
+
+        @Override
+        public Rectangle2D getBounds(IElement e, Rectangle2D size) {
+            Transition t = getTransition(e);
+            InternalSize ub = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(InternalSize.class);
+            InternalSize lb = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(InternalSize.class);
+            Rectangle2D us = ub==null?null:ub.getBounds(t.ue, null);
+            Rectangle2D ls = lb==null?null:lb.getBounds(t.le, null);
+            if (ls==null && us==null) return null;
+            if (size==null) size = new Rectangle2D.Double();
+            if (ls==null && us!=null) {
+                size.setRect(us);
+                return size;
+            }
+            if (us==null && ls!=null) {
+                size.setRect(ls);
+                return size;
+            }
+            // interpolate
+            double minX = us.getMinX() * (1-t.phase) + ls.getMinX() * (t.phase);
+            double minY = us.getMinY() * (1-t.phase) + ls.getMinY() * (t.phase);
+            double maxX = us.getMaxX() * (1-t.phase) + ls.getMaxX() * (t.phase);
+            double maxY = us.getMaxY() * (1-t.phase) + ls.getMaxY() * (t.phase);
+
+            size.setRect(minX, minY, maxX-minX, maxY-minY);
+            return size;
+        }
+
+
+        @Override
+        public double getArrowSize(IElement e, EdgeEnd end) {
+            Transition t = getTransition(e);
+            EdgeVisuals uev = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
+            EdgeVisuals lev = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
+            double us = uev==null?0:uev.getArrowSize(t.ue, end);
+            double ls = lev==null?0:lev.getArrowSize(t.le, end);
+            return us*(1-t.phase) + ls*(t.phase);
+        }
+
+        @Override
+        public StrokeType getStrokeType(IElement e) {
+            Transition t = getTransition(e);
+            EdgeVisuals uev = t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
+            EdgeVisuals lev = t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
+            StrokeType ust = uev==null?null:uev.getStrokeType(t.ue);
+            StrokeType lst = lev==null?null:lev.getStrokeType(t.le);
+            return ust==null?lst:ust;
+        }
+
+        @Override
+        public ArrowType getArrowType(IElement e, EdgeEnd end) {
+            Transition t = getTransition(e);
+            EdgeVisuals uev = t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
+            EdgeVisuals lev = t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
+            ArrowType uat = uev==null?null:uev.getArrowType(t.ue, end);
+            ArrowType lat = lev==null?null:lev.getArrowType(t.le, end);
+            return uat==null?lat:uat;
+        }
+
+        @Override
+        public Stroke getStroke(IElement e) {
+            Transition t = getTransition(e);
+            EdgeVisuals uev = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
+            EdgeVisuals lev = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
+            Stroke us = uev==null?null:uev.getStroke(t.ue);
+            Stroke ls = lev==null?null:lev.getStroke(t.le);
+            if (us==null) return ls;
+            if (ls==null) return us;
+            // interpolate width
+            if (!(us instanceof BasicStroke) || !(ls instanceof BasicStroke))
+                return us;
+            BasicStroke bsu = (BasicStroke) us;
+            BasicStroke bsl = (BasicStroke) ls;
+            double width = bsu.getLineWidth() * (1-t.phase) + bsl.getLineWidth() * (t.phase);
+
+            return new BasicStroke(
+                    (float)width,
+                    bsu.getEndCap(),
+                    bsu.getLineJoin(),
+                    bsu.getMiterLimit(),
+                    bsu.getDashArray(),
+                    bsu.getDashPhase());
+        }
+
+        @Override
+        public void setArrowSize(IElement e, EdgeEnd end, double size) {
+        }
+
+        @Override
+        public void setStrokeType(IElement e, StrokeType arrowType) {
+        }
+
+        @Override
+        public void setArrowType(IElement e, EdgeEnd end, ArrowType arrowType) {
+        }
+
+        @Override
+        public void setStroke(IElement e, Stroke s) {
+        }
+
+
+
+
+        @Override
+        public String getText(IElement e) {
+            Transition t = getTransition(e);
+            Text tu = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(Text.class);
+            Text tl = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(Text.class);
+            String su = tu==null?null:tu.getText(t.ue);
+            String sl = tl==null?null:tl.getText(t.le);
+
+            return su==null?sl:su;
+        }
+
+        @Override
+        public void setText(IElement e, String text) {
+        }
+
+        @Override
+        public Color getFillColor(IElement e) {
+            Transition t = getTransition(e);
+            Color uc = ElementUtils.getFillColor(t.ue);
+            Color lc = ElementUtils.getFillColor(t.le);
+            if (uc==null) return lc;
+            if (lc==null) return uc;
+            return GeometryUtils.interpolate(uc, lc, t.phase);
+        }
+        @Override
+        public void setFillColor(IElement e, Color c) {
+        }
+        @Override
+        public Color getBorderColor(IElement e) {
+            Transition t = getTransition(e);
+            Color uc = ElementUtils.getBorderColor(t.ue);
+            Color lc = ElementUtils.getBorderColor(t.le);
+            if (uc==null) return lc;
+            if (lc==null) return uc;
+            return GeometryUtils.interpolate(uc, lc, t.phase);
+        }
+
+        @Override
+        public void setBorderColor(IElement e, Color c) {
+        }
+
+        @Override
+        public Color getAdditionalColor(IElement e) {
+            Transition t = getTransition(e);
+            Color uc = ElementUtils.getAdditionalColor(t.ue);
+            Color lc = ElementUtils.getAdditionalColor(t.le);
+            if (uc==null) return lc;
+            if (lc==null) return uc;
+            return GeometryUtils.interpolate(uc, lc, t.phase);
+        }
+
+        @Override
+        public void setAdditionalColor(IElement e, Color c) {
+        }
+
+        @Override
+        public Color getTextColor(IElement e) {
+            Transition t = getTransition(e);
+            Color uc = ElementUtils.getTextColor(t.ue);
+            Color lc = ElementUtils.getTextColor(t.le);
+            if (uc==null) return lc;
+            if (lc==null) return uc;
+            return GeometryUtils.interpolate(uc, lc, t.phase);
+        }
+
+        @Override
+        public void setTextColor(IElement e, Color c) {
+        }
+
+        @Override
+        public AngleType getAngleType(IElement e) {
+            Transition t = getTransition(e);
+            BendsHandler       ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+            BendsHandler       leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+            if (ueb!=null) return ueb.getAngleType(t.ue);
+            if (leb!=null) return leb.getAngleType(t.le);
+            return null;
+        }
+        @Override
+        public void setAngleType(IElement e, AngleType angleType) {
+        }
+        @Override
+        public Bend addBend(IElement e, int index, Point2D pos) {
+            return null;
+        }
+        @Override
+        public void getBendPosition(IElement e, Bend b, Point2D pos) {
+            // TODO make better later
+            Transition t = getTransition(e);
+            BendsHandler       ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+            BendsHandler       leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+            if (ueb!=null) ueb.getBendPosition(e, b, pos);
+            if (leb!=null) leb.getBendPosition(e, b, pos);
+        }
+        @Override
+        public void getBends(IElement e, List<Bend> bends) {
+            // TODO make better later
+            Transition t = getTransition(e);
+            BendsHandler       ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+            BendsHandler       leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+            if (ueb!=null) ueb.getBends(e, bends);
+            if (leb!=null) leb.getBends(e, bends);
+        }
+        @Override
+        public boolean removeBend(IElement e, Bend b) {
+            return false;
+        }
+
+        @Override
+        public Path2D getPath(IElement e) {
+            Transition t = getTransition(e);
+            BendsHandler       ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+            BendsHandler       leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+            if (ueb==null && leb==null) return null;
+            if (ueb==null) return leb.getPath(t.le);
+            if (leb==null) return ueb.getPath(t.ue);
+            Path2D up = ueb.getPath(t.ue);
+            Path2D lp = leb.getPath(t.le);
+            // interpolate two paths
+            return PathUtils2.interpolatePaths(up, lp, t.phase);
+        }
+
+        @Override
+        public void setPath(IElement e, Path2D p) {
+        }
+
+        @Override
+        public int getBendsCount(IElement e) {
+            Transition t = getTransition(e);
+            BendsHandler       ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+            BendsHandler       leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
+            if (leb!=null) return leb.getBendsCount(t.le);
+            if (ueb!=null) return ueb.getBendsCount(t.ue);
+            return 0;
+        }
+
+        @Override
+        public void moveBend(IElement e, Bend b, Point2D pos) {
+            // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        public void cleanup(IElement e) {
+            // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        public void init(IElement e, G2DParentNode parent) {
+            Transition t = getTransition(e);
+            List<SceneGraph> empty = Collections.EMPTY_LIST;
+            List<SceneGraph> ups = t.ue==null?empty:t.ue.getElementClass().getItemsByClass(SceneGraph.class);
+            List<SceneGraph> lps = t.le==null?empty:t.le.getElementClass().getItemsByClass(SceneGraph.class);
+
+            if (ObjectUtils.equals(ups, lps))
+            {
+                for (SceneGraph lp : lps)
+                    lp.init(e, parent);
+                return;
+            }
+
+            if (!lps.isEmpty()) {
+                for (SceneGraph lp : lps)
+                {
+                    lp.init(t.le, parent);
+                }
+            }
+
+            if (!ups.isEmpty()) {
+                for (SceneGraph up : ups)
+                {
+                    up.init(t.ue, parent);
+                }
+            }
+        }
+
+    }
+
+
+    private static WeakHashMap<ElementClass, ElementClass> FADEIN_CLASSES =
+        new WeakHashMap<ElementClass, ElementClass>();
+    private static WeakHashMap<ElementClass, ElementClass> FADEOUT_CLASSES =
+        new WeakHashMap<ElementClass, ElementClass>();
+    public synchronized static ElementClass getFadeElementClass(ElementClass origClass, FadeDir dir)
+    {
+        if (dir==FadeDir.In)
+        {
+            ElementClass proxyClass = FADEIN_CLASSES.get(origClass);
+            if (proxyClass==null) {
+                proxyClass = createFadeElementClass(origClass, dir);
+                FADEIN_CLASSES.put(origClass, proxyClass);
+            }
+            return proxyClass;
+        } else {
+            ElementClass proxyClass = FADEOUT_CLASSES.get(origClass);
+            if (proxyClass==null) {
+                proxyClass = createFadeElementClass(origClass, dir);
+                FADEOUT_CLASSES.put(origClass, proxyClass);
+            }
+            return proxyClass;
+        }
+    }
+
+    private static final IProxyProvider PROXY_PROVIDER =
+        new IProxyProvider() {
+        @Override
+        public IElement provide(IElement src) {
+            return src.getHint(KEY_SOURCE_ELEMENT);
+        }
+    };
+
+    static ElementClass createFadeElementClass(ElementClass clazz, FadeDir dir)
+    {
+        List<ElementHandler> result = new ArrayList<ElementHandler>();
+        List<ElementHandler> lst = new ArrayList<ElementHandler>();
+        for (ElementHandler eh : clazz.getAll())
+        {
+            lst.clear();
+            ProxyHandler.addProxyElementHandlers(eh, PROXY_PROVIDER, lst);
+            for (ElementHandler eh2 : lst)
+            {
+                if (eh2 instanceof ProxyLifeCycle) continue;
+                if (eh2 instanceof TerminalTopology) continue;
+
+                if (eh2 instanceof SceneGraph) {
+                    result.add( new ProxyFadePaint(dir, (SceneGraph)eh) );
+                } else {
+                    result.add(eh2);
+                }
+            }
+        }
+        return ElementClass.compile(result);
+    }
+
+    static class ProxyFadePaint implements SceneGraph {
+        /**
+         * 
+         */
+        private static final long serialVersionUID = 3559624682436513231L;
+        static enum FadeDir {In, Out};
+        FadeDir dir;
+        SceneGraph orig;
+        public ProxyFadePaint(FadeDir dir, SceneGraph orig) {
+            this.dir = dir;
+            this.orig = orig;
+            assert(orig!=null);
+        }
+        public static IElement getSource(IElement element)
+        {
+            return element.getHint(KEY_SOURCE_ELEMENT);
+        }
+        public double getPhase(IElement element)
+        {
+            Double phase = element.getDiagram().getHint(KEY_PHASE);
+            if (phase==null) phase = 0.0;
+            if (dir == FadeDir.Out) phase = 1-phase;
+            return phase;
+        }
+        @Override
+        public void cleanup(IElement e) {
+            IElement sourceElement = getSource(e);
+            orig.cleanup(sourceElement);
+        }
+        @Override
+        public void init(IElement e, G2DParentNode parent) {
+            IElement sourceElement = getSource(e);
+            orig.init(sourceElement, parent);
+        }
+    }
+
+    static class MorphTopologyImpl implements Topology {
+
+        @Override
+        public void connect(IElement edge, EdgeEnd end, IElement node, Terminal terminal) {
+        }
+
+        @Override
+        public void disconnect(IElement edge, EdgeEnd end, IElement node, Terminal terminal) {
+        }
+
+        @Override
+        public Connection getConnection(IElement edge, EdgeEnd end) {
+            return null;
+        }
+
+        @Override
+        public void getConnections(IElement node, Terminal terminal, Collection<Connection> connections) {
+        }
+
+    }
+
+}