--- /dev/null
+/*******************************************************************************\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