/******************************************************************************* * 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 *

* 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 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 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 empty = Collections.EMPTY_LIST; // List ups = t.ue==null?empty:t.ue.getElementClass().getItemsByClass(Paint.class); // List 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 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 empty = Collections.EMPTY_LIST; List ups = t.ue==null?empty:t.ue.getElementClass().getItemsByClass(SceneGraph.class); List 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 FADEIN_CLASSES = new WeakHashMap(); private static WeakHashMap FADEOUT_CLASSES = new WeakHashMap(); 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 result = new ArrayList(); List lst = new ArrayList(); 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 connections) { } } }