/*******************************************************************************
* 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) {
}
}
}