1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g2d.multileveldiagram;
\r
14 import java.awt.BasicStroke;
\r
15 import java.awt.Color;
\r
16 import java.awt.Stroke;
\r
17 import java.awt.geom.AffineTransform;
\r
18 import java.awt.geom.Path2D;
\r
19 import java.awt.geom.Point2D;
\r
20 import java.awt.geom.Rectangle2D;
\r
21 import java.util.ArrayList;
\r
22 import java.util.Collection;
\r
23 import java.util.Collections;
\r
24 import java.util.List;
\r
25 import java.util.Map;
\r
26 import java.util.WeakHashMap;
\r
28 import org.simantics.g2d.diagram.DiagramClass;
\r
29 import org.simantics.g2d.diagram.IDiagram;
\r
30 import org.simantics.g2d.diagram.handler.DataElementMap;
\r
31 import org.simantics.g2d.diagram.handler.LifeCycle;
\r
32 import org.simantics.g2d.diagram.handler.Topology;
\r
33 import org.simantics.g2d.diagram.handler.impl.DataElementMapImpl;
\r
34 import org.simantics.g2d.diagram.handler.impl.PickContextImpl;
\r
35 import org.simantics.g2d.diagram.handler.impl.TransactionContextImpl;
\r
36 import org.simantics.g2d.diagram.impl.AbstractDiagram;
\r
37 import org.simantics.g2d.element.ElementClass;
\r
38 import org.simantics.g2d.element.ElementHints;
\r
39 import org.simantics.g2d.element.ElementUtils;
\r
40 import org.simantics.g2d.element.IElement;
\r
41 import org.simantics.g2d.element.handler.AdditionalColor;
\r
42 import org.simantics.g2d.element.handler.BendsHandler;
\r
43 import org.simantics.g2d.element.handler.BorderColor;
\r
44 import org.simantics.g2d.element.handler.EdgeVisuals;
\r
45 import org.simantics.g2d.element.handler.ElementHandler;
\r
46 import org.simantics.g2d.element.handler.FillColor;
\r
47 import org.simantics.g2d.element.handler.InternalSize;
\r
48 import org.simantics.g2d.element.handler.SceneGraph;
\r
49 import org.simantics.g2d.element.handler.TerminalTopology;
\r
50 import org.simantics.g2d.element.handler.Text;
\r
51 import org.simantics.g2d.element.handler.TextColor;
\r
52 import org.simantics.g2d.element.handler.Transform;
\r
53 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
\r
54 import org.simantics.g2d.element.handler.impl.MoveImpl;
\r
55 import org.simantics.g2d.element.handler.impl.proxy.IProxyProvider;
\r
56 import org.simantics.g2d.element.handler.impl.proxy.ProxyHandler;
\r
57 import org.simantics.g2d.element.handler.impl.proxy.ProxyLifeCycle;
\r
58 import org.simantics.g2d.element.impl.Element;
\r
59 import org.simantics.g2d.multileveldiagram.TransitionDiagram.ProxyFadePaint.FadeDir;
\r
60 import org.simantics.g2d.utils.GeometryUtils;
\r
61 import org.simantics.g2d.utils.PathUtils2;
\r
62 import org.simantics.scenegraph.g2d.G2DParentNode;
\r
63 import org.simantics.utils.ObjectUtils;
\r
64 import org.simantics.utils.datastructures.hints.HintContext;
\r
67 * Transition diagram is a diagram that is a transition between two diagrams.
\r
68 * Diagram in transition cannot be edited.
\r
69 * There is always upper and lower diagram.
\r
70 * There are different trasition effects : Morph and blend
\r
72 * TODO monitor upper and lower diagrams for element changes
\r
73 * TODO monitor upper and lower elements for KEY_OBJECT changes
\r
75 * @author Toni Kalajainen
\r
77 public class TransitionDiagram extends AbstractDiagram {
\r
79 TransitionDiagram() {
\r
80 super(TRANSITION_DIAGRAM_CLASS, new HintContext());
\r
83 public static final Key KEY_UPPER_DIAGRAM = new KeyOf(IDiagram.class);
\r
84 public static final Key KEY_LOWER_DIAGRAM = new KeyOf(IDiagram.class);
\r
85 public static final Key KEY_TRANSITION_CLASS = new KeyOf(ElementClass.class);
\r
87 /** Element keys refering to elements of source diagram */
\r
88 public static final Key KEY_UPPER_ELEMENT = new KeyOf(IElement.class);
\r
89 public static final Key KEY_LOWER_ELEMENT = new KeyOf(IElement.class);
\r
90 public static final Key KEY_SOURCE_ELEMENT = new KeyOf(IElement.class);
\r
92 /** Diagram hint, transition phase, value between 0..1, 0=upper, 1=lower */
\r
93 public static final Key KEY_PHASE = new KeyOf(Double.class);
\r
95 public static final ElementClass MORPH_ELEMENT_CLASS = ElementClass.compile(MorphElementHandler.INSTANCE, MoveImpl.HANDLER);
\r
97 public static final DiagramClass TRANSITION_DIAGRAM_CLASS = DiagramClass.compile(
\r
98 new PickContextImpl(),
\r
99 new TransactionContextImpl(),
\r
100 new MorphTopologyImpl(),
\r
101 new TransitionDiagramHandler(),
\r
102 new DataElementMapImpl()
\r
105 public static final IDiagram createTransitionDiagram(IDiagram upperDiagram, IDiagram lowerDiagram, ElementClass transitionClass)
\r
107 assert(upperDiagram!=null && lowerDiagram!=null && transitionClass!=null);
\r
108 TransitionDiagram d = new TransitionDiagram();
\r
109 d.setHint(KEY_TRANSITION_CLASS, transitionClass);
\r
110 d.setHint(KEY_UPPER_DIAGRAM, upperDiagram);
\r
111 d.setHint(KEY_LOWER_DIAGRAM, lowerDiagram);
\r
116 IDiagram upperDiagram, lowerDiagram;
\r
119 * Set a new element class to an element
\r
123 * @return new element
\r
125 private static IElement setElementClass(IElement e, ElementClass clazz)
\r
127 IDiagram d = e.getDiagram();
\r
128 if (e.getElementClass().equals(clazz)) return e;
\r
129 Map<Key, Object> hints = e.getHints();
\r
130 int index = d.getElements().indexOf(e);
\r
131 d.removeElement(e);
\r
133 e = Element.spawnNew(clazz);
\r
136 d.moveTo(e, index);
\r
140 static class TransitionDiagramHandler implements LifeCycle {
\r
142 private static final Key KEY_COMPOSITION_LISTENER =
\r
143 new KeyOf(CompositionListener.class);
\r
146 public void onDiagramCreated(final IDiagram transitionDiagram) {
\r
148 CompositionListener cl = new CompositionListener() {
\r
150 public void onElementAdded(IDiagram diagram, IElement element) {
\r
151 IDiagram upperDiagram = transitionDiagram.getHint(KEY_UPPER_DIAGRAM);
\r
152 IDiagram lowerDiagram = transitionDiagram.getHint(KEY_LOWER_DIAGRAM);
\r
153 boolean isUpper = diagram == upperDiagram;
\r
154 IDiagram oppositeDiagram = isUpper ? lowerDiagram : upperDiagram;
\r
155 // Element has been added to either upper or lower diagram
\r
156 DataElementMap m = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
\r
157 Object data = m.getData(diagram, element);
\r
159 // Check if the opposite diagram has a valid counter part
\r
160 m = oppositeDiagram.getDiagramClass().getSingleItem(DataElementMap.class);
\r
161 IElement counterPart = data==null?null:m.getElement(oppositeDiagram, data);
\r
164 ElementClass clazz;
\r
165 // Element has a counter-part, therefore it can be morphed
\r
166 if (counterPart!=null) {
\r
167 clazz = MORPH_ELEMENT_CLASS;
\r
169 // There is no counterpart, therefore it is a fading element
\r
171 FadeDir dir = isUpper ? FadeDir.Out : FadeDir.In;
\r
172 clazz = getFadeElementClass(element.getElementClass(), dir);
\r
175 // Check if transition element already exists
\r
176 m = transitionDiagram.getDiagramClass().getSingleItem(DataElementMap.class);
\r
177 IElement transitionElement = m.getElement(transitionDiagram, data);
\r
178 if (transitionElement!=null) {
\r
179 setElementClass(transitionElement, clazz);
\r
183 transitionElement = Element.instantiate(clazz, null);
\r
185 // Element has a counter-part, therefore it can be morphed
\r
186 if (counterPart!=null) {
\r
187 transitionElement.setHint(KEY_UPPER_ELEMENT, isUpper?element:counterPart);
\r
188 transitionElement.setHint(KEY_LOWER_ELEMENT, isUpper?counterPart:element);
\r
190 // There is no counterpart, therefore it is a fading element
\r
192 transitionElement.setHint(KEY_SOURCE_ELEMENT, element);
\r
196 transitionElement.setHint(ElementHints.KEY_OBJECT, data);
\r
197 Element.fireCreated(transitionElement);
\r
198 transitionDiagram.addElement(transitionElement);
\r
202 public void onElementRemoved(IDiagram diagram, IElement element) {
\r
203 IDiagram upperDiagram = transitionDiagram.getHint(KEY_UPPER_DIAGRAM);
\r
204 IDiagram lowerDiagram = transitionDiagram.getHint(KEY_LOWER_DIAGRAM);
\r
205 boolean isUpper = diagram == upperDiagram;
\r
206 IDiagram oppositeDiagram = isUpper ? lowerDiagram : upperDiagram;
\r
207 // Element has been added to either upper or lower diagram
\r
208 DataElementMap m = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
\r
209 Object data = m.getData(diagram, element);
\r
211 // Check if the opposite diagram has a valid counter part
\r
212 m = oppositeDiagram.getDiagramClass().getSingleItem(DataElementMap.class);
\r
213 IElement counterPart = data==null?null:m.getElement(oppositeDiagram, data);
\r
215 // Check if transition element already exists
\r
216 m = transitionDiagram.getDiagramClass().getSingleItem(DataElementMap.class);
\r
217 IElement transitionElement = m.getElement(transitionDiagram, data);
\r
218 if (transitionElement==null) {
\r
219 for (IElement e : transitionDiagram.getElements())
\r
220 if (e.getHint(KEY_SOURCE_ELEMENT) == element)
\r
222 transitionElement = e;
\r
226 // There is a mix-up .. not too serious
\r
227 if (transitionElement==null)
\r
230 // if class is morph and the counter part remains, transform transition element to fade element
\r
231 if (transitionElement.getElementClass().equals(MORPH_ELEMENT_CLASS) &&
\r
232 counterPart != null)
\r
234 FadeDir dir = isUpper ? FadeDir.Out : FadeDir.In;
\r
235 ElementClass clazz = getFadeElementClass(element.getElementClass(), dir);
\r
236 transitionElement.removeHint(KEY_UPPER_ELEMENT);
\r
237 transitionElement.removeHint(KEY_LOWER_ELEMENT);
\r
238 setElementClass(transitionElement, clazz);
\r
239 transitionElement.setHint(KEY_SOURCE_ELEMENT, counterPart);
\r
241 transitionDiagram.removeElement(transitionElement);
\r
242 transitionElement.destroy();
\r
246 IDiagram upperDiagram = transitionDiagram.getHint(KEY_UPPER_DIAGRAM);
\r
247 IDiagram lowerDiagram = transitionDiagram.getHint(KEY_LOWER_DIAGRAM);
\r
248 upperDiagram.addCompositionListener(cl);
\r
249 lowerDiagram.addCompositionListener(cl);
\r
250 transitionDiagram.setHint(KEY_COMPOSITION_LISTENER, cl);
\r
253 for (IElement e : upperDiagram.getElements())
\r
254 cl.onElementAdded(upperDiagram, e);
\r
255 for (IElement e : lowerDiagram.getElements())
\r
256 cl.onElementAdded(lowerDiagram, e);
\r
260 public void onDiagramDisposed(IDiagram diagram) {
\r
262 CompositionListener cl = diagram.getHint(KEY_COMPOSITION_LISTENER);
\r
263 IDiagram upperDiagram = diagram.getHint(KEY_UPPER_DIAGRAM);
\r
264 IDiagram lowerDiagram = diagram.getHint(KEY_LOWER_DIAGRAM);
\r
265 upperDiagram.removeCompositionListener(cl);
\r
266 lowerDiagram.removeCompositionListener(cl);
\r
267 diagram.removeHint(KEY_COMPOSITION_LISTENER);
\r
271 public void onDiagramDestroyed(IDiagram diagram) {}
\r
273 public void onDiagramLoaded(IDiagram diagram, Collection<IElement> initialElements) {}
\r
277 // TODO REMOVE REDUNDANCY == Transitions to void
\r
278 // In morph element handler there is always 2 counter parts
\r
279 static class MorphElementHandler implements SceneGraph, FillColor, BorderColor, AdditionalColor, TextColor, Transform, InternalSize, EdgeVisuals, BendsHandler, Text {
\r
281 private static final long serialVersionUID = 1907473087657477787L;
\r
283 public static final MorphElementHandler INSTANCE = new MorphElementHandler();
\r
285 /** key for sub-diagram -> element Map */
\r
286 public final static Key KEY_ELEMENT_MAP = new KeyOf(Map.class);
\r
288 static IElement getUpperSourceElement(IElement e)
\r
290 return e.getHint(KEY_UPPER_ELEMENT);
\r
293 static IElement getLowerSourceElement(IElement e)
\r
295 return e.getHint(KEY_LOWER_ELEMENT);
\r
298 /** Returns elements between when diagram is in transition */
\r
299 static Transition getTransition(IElement e)
\r
301 Transition t = new Transition();
\r
302 Double phase = e.getDiagram().getHint(KEY_PHASE);
\r
303 t.le = getLowerSourceElement(e);
\r
304 t.ue = getUpperSourceElement(e);
\r
309 static private class Transition {
\r
310 // Upper Element and Lower Element
\r
316 // public void paint(IElement e, ICanvasContext ctx,
\r
317 // GraphicsContext elementGC, GraphicsContext controlGC) {
\r
318 // Transition t = getTransition(e);
\r
319 // List<Paint> empty = Collections.EMPTY_LIST;
\r
320 // List<Paint> ups = t.ue==null?empty:t.ue.getElementClass().getItemsByClass(Paint.class);
\r
321 // List<Paint> lps = t.le==null?empty:t.le.getElementClass().getItemsByClass(Paint.class);
\r
323 // if (ObjectUtils.equals(ups, lps))
\r
325 // for (Paint lp : lps)
\r
326 // lp.paint(e, ctx, elementGC, controlGC);
\r
330 // if (!lps.isEmpty()) {
\r
331 //// Graphics2D g = elementGC.createClone();
\r
332 //// Graphics2D g2 = controlGC.createClone();
\r
333 //// Composite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float)t.phase);
\r
334 //// g.setComposite(c);
\r
335 //// g2.setComposite(c);
\r
336 // GraphicsContextImpl newCtx = new GraphicsContextImpl(elementGC.getBounds(), elementGC.getNode());
\r
337 // GraphicsContextImpl newCtx2 = new GraphicsContextImpl(controlGC.getBounds(), controlGC.getNode());
\r
338 // for (Paint lp : lps)
\r
340 // lp.paint(t.le, ctx, newCtx, newCtx2);
\r
342 // newCtx.dispose();
\r
343 // newCtx2.dispose();
\r
346 // if (!ups.isEmpty()) {
\r
347 //// Graphics2D g = elementGC.createClone();
\r
348 //// Graphics2D g2 = controlGC.createClone();
\r
349 //// Composite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1-(float)t.phase);
\r
350 //// g.setComposite(c);
\r
351 //// g2.setComposite(c);
\r
352 // GraphicsContextImpl newCtx = new GraphicsContextImpl(elementGC.getBounds(), elementGC.getNode());
\r
353 // GraphicsContextImpl newCtx2 = new GraphicsContextImpl(controlGC.getBounds(), controlGC.getNode());
\r
354 // for (Paint up : ups)
\r
356 // up.paint(t.ue, ctx, newCtx, newCtx2);
\r
358 // newCtx.dispose();
\r
359 // newCtx2.dispose();
\r
364 public AffineTransform getTransform(IElement e) {
\r
365 Transition t = getTransition(e);
\r
366 Transform ut = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(Transform.class);
\r
367 Transform lt = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(Transform.class);
\r
368 AffineTransform uat = ut==null?null:ut.getTransform(t.ue);
\r
369 AffineTransform lat = lt==null?null:lt.getTransform(t.le);
\r
370 if (uat==null) return lat;
\r
371 if (lat==null) return uat;
\r
373 double um[] = new double[6];
\r
375 double lm[] = new double[6];
\r
377 double rm[] = new double[6];
\r
378 for (int i=0; i<6; i++)
\r
379 rm[i] = um[i]*(1-t.phase) + lm[i]*(t.phase);
\r
380 return new AffineTransform(rm);
\r
384 public void setTransform(IElement e, AffineTransform at) {
\r
390 public Rectangle2D getBounds(IElement e, Rectangle2D size) {
\r
391 Transition t = getTransition(e);
\r
392 InternalSize ub = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(InternalSize.class);
\r
393 InternalSize lb = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(InternalSize.class);
\r
394 Rectangle2D us = ub==null?null:ub.getBounds(t.ue, null);
\r
395 Rectangle2D ls = lb==null?null:lb.getBounds(t.le, null);
\r
396 if (ls==null && us==null) return null;
\r
397 if (size==null) size = new Rectangle2D.Double();
\r
398 if (ls==null && us!=null) {
\r
402 if (us==null && ls!=null) {
\r
407 double minX = us.getMinX() * (1-t.phase) + ls.getMinX() * (t.phase);
\r
408 double minY = us.getMinY() * (1-t.phase) + ls.getMinY() * (t.phase);
\r
409 double maxX = us.getMaxX() * (1-t.phase) + ls.getMaxX() * (t.phase);
\r
410 double maxY = us.getMaxY() * (1-t.phase) + ls.getMaxY() * (t.phase);
\r
412 size.setRect(minX, minY, maxX-minX, maxY-minY);
\r
418 public double getArrowSize(IElement e, EdgeEnd end) {
\r
419 Transition t = getTransition(e);
\r
420 EdgeVisuals uev = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
\r
421 EdgeVisuals lev = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
\r
422 double us = uev==null?0:uev.getArrowSize(t.ue, end);
\r
423 double ls = lev==null?0:lev.getArrowSize(t.le, end);
\r
424 return us*(1-t.phase) + ls*(t.phase);
\r
428 public StrokeType getStrokeType(IElement e) {
\r
429 Transition t = getTransition(e);
\r
430 EdgeVisuals uev = t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
\r
431 EdgeVisuals lev = t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
\r
432 StrokeType ust = uev==null?null:uev.getStrokeType(t.ue);
\r
433 StrokeType lst = lev==null?null:lev.getStrokeType(t.le);
\r
434 return ust==null?lst:ust;
\r
438 public ArrowType getArrowType(IElement e, EdgeEnd end) {
\r
439 Transition t = getTransition(e);
\r
440 EdgeVisuals uev = t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
\r
441 EdgeVisuals lev = t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
\r
442 ArrowType uat = uev==null?null:uev.getArrowType(t.ue, end);
\r
443 ArrowType lat = lev==null?null:lev.getArrowType(t.le, end);
\r
444 return uat==null?lat:uat;
\r
448 public Stroke getStroke(IElement e) {
\r
449 Transition t = getTransition(e);
\r
450 EdgeVisuals uev = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
\r
451 EdgeVisuals lev = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(EdgeVisuals.class);
\r
452 Stroke us = uev==null?null:uev.getStroke(t.ue);
\r
453 Stroke ls = lev==null?null:lev.getStroke(t.le);
\r
454 if (us==null) return ls;
\r
455 if (ls==null) return us;
\r
456 // interpolate width
\r
457 if (!(us instanceof BasicStroke) || !(ls instanceof BasicStroke))
\r
459 BasicStroke bsu = (BasicStroke) us;
\r
460 BasicStroke bsl = (BasicStroke) ls;
\r
461 double width = bsu.getLineWidth() * (1-t.phase) + bsl.getLineWidth() * (t.phase);
\r
463 return new BasicStroke(
\r
467 bsu.getMiterLimit(),
\r
468 bsu.getDashArray(),
\r
469 bsu.getDashPhase());
\r
473 public void setArrowSize(IElement e, EdgeEnd end, double size) {
\r
477 public void setStrokeType(IElement e, StrokeType arrowType) {
\r
481 public void setArrowType(IElement e, EdgeEnd end, ArrowType arrowType) {
\r
485 public void setStroke(IElement e, Stroke s) {
\r
492 public String getText(IElement e) {
\r
493 Transition t = getTransition(e);
\r
494 Text tu = t.ue==null?null:t.ue.getElementClass().getAtMostOneItemOfClass(Text.class);
\r
495 Text tl = t.le==null?null:t.le.getElementClass().getAtMostOneItemOfClass(Text.class);
\r
496 String su = tu==null?null:tu.getText(t.ue);
\r
497 String sl = tl==null?null:tl.getText(t.le);
\r
499 return su==null?sl:su;
\r
503 public void setText(IElement e, String text) {
\r
507 public Color getFillColor(IElement e) {
\r
508 Transition t = getTransition(e);
\r
509 Color uc = ElementUtils.getFillColor(t.ue);
\r
510 Color lc = ElementUtils.getFillColor(t.le);
\r
511 if (uc==null) return lc;
\r
512 if (lc==null) return uc;
\r
513 return GeometryUtils.interpolate(uc, lc, t.phase);
\r
516 public void setFillColor(IElement e, Color c) {
\r
519 public Color getBorderColor(IElement e) {
\r
520 Transition t = getTransition(e);
\r
521 Color uc = ElementUtils.getBorderColor(t.ue);
\r
522 Color lc = ElementUtils.getBorderColor(t.le);
\r
523 if (uc==null) return lc;
\r
524 if (lc==null) return uc;
\r
525 return GeometryUtils.interpolate(uc, lc, t.phase);
\r
529 public void setBorderColor(IElement e, Color c) {
\r
533 public Color getAdditionalColor(IElement e) {
\r
534 Transition t = getTransition(e);
\r
535 Color uc = ElementUtils.getAdditionalColor(t.ue);
\r
536 Color lc = ElementUtils.getAdditionalColor(t.le);
\r
537 if (uc==null) return lc;
\r
538 if (lc==null) return uc;
\r
539 return GeometryUtils.interpolate(uc, lc, t.phase);
\r
543 public void setAdditionalColor(IElement e, Color c) {
\r
547 public Color getTextColor(IElement e) {
\r
548 Transition t = getTransition(e);
\r
549 Color uc = ElementUtils.getTextColor(t.ue);
\r
550 Color lc = ElementUtils.getTextColor(t.le);
\r
551 if (uc==null) return lc;
\r
552 if (lc==null) return uc;
\r
553 return GeometryUtils.interpolate(uc, lc, t.phase);
\r
557 public void setTextColor(IElement e, Color c) {
\r
561 public AngleType getAngleType(IElement e) {
\r
562 Transition t = getTransition(e);
\r
563 BendsHandler ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
\r
564 BendsHandler leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
\r
565 if (ueb!=null) return ueb.getAngleType(t.ue);
\r
566 if (leb!=null) return leb.getAngleType(t.le);
\r
570 public void setAngleType(IElement e, AngleType angleType) {
\r
573 public Bend addBend(IElement e, int index, Point2D pos) {
\r
577 public void getBendPosition(IElement e, Bend b, Point2D pos) {
\r
578 // TODO make better later
\r
579 Transition t = getTransition(e);
\r
580 BendsHandler ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
\r
581 BendsHandler leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
\r
582 if (ueb!=null) ueb.getBendPosition(e, b, pos);
\r
583 if (leb!=null) leb.getBendPosition(e, b, pos);
\r
586 public void getBends(IElement e, List<Bend> bends) {
\r
587 // TODO make better later
\r
588 Transition t = getTransition(e);
\r
589 BendsHandler ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
\r
590 BendsHandler leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
\r
591 if (ueb!=null) ueb.getBends(e, bends);
\r
592 if (leb!=null) leb.getBends(e, bends);
\r
595 public boolean removeBend(IElement e, Bend b) {
\r
600 public Path2D getPath(IElement e) {
\r
601 Transition t = getTransition(e);
\r
602 BendsHandler ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
\r
603 BendsHandler leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
\r
604 if (ueb==null && leb==null) return null;
\r
605 if (ueb==null) return leb.getPath(t.le);
\r
606 if (leb==null) return ueb.getPath(t.ue);
\r
607 Path2D up = ueb.getPath(t.ue);
\r
608 Path2D lp = leb.getPath(t.le);
\r
609 // interpolate two paths
\r
610 return PathUtils2.interpolatePaths(up, lp, t.phase);
\r
614 public void setPath(IElement e, Path2D p) {
\r
618 public int getBendsCount(IElement e) {
\r
619 Transition t = getTransition(e);
\r
620 BendsHandler ueb = t.ue.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
\r
621 BendsHandler leb = t.le.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
\r
622 if (leb!=null) return leb.getBendsCount(t.le);
\r
623 if (ueb!=null) return ueb.getBendsCount(t.ue);
\r
628 public void moveBend(IElement e, Bend b, Point2D pos) {
\r
629 // TODO Auto-generated method stub
\r
634 public void cleanup(IElement e) {
\r
635 // TODO Auto-generated method stub
\r
640 public void init(IElement e, G2DParentNode parent) {
\r
641 Transition t = getTransition(e);
\r
642 List<SceneGraph> empty = Collections.EMPTY_LIST;
\r
643 List<SceneGraph> ups = t.ue==null?empty:t.ue.getElementClass().getItemsByClass(SceneGraph.class);
\r
644 List<SceneGraph> lps = t.le==null?empty:t.le.getElementClass().getItemsByClass(SceneGraph.class);
\r
646 if (ObjectUtils.equals(ups, lps))
\r
648 for (SceneGraph lp : lps)
\r
649 lp.init(e, parent);
\r
653 if (!lps.isEmpty()) {
\r
654 for (SceneGraph lp : lps)
\r
656 lp.init(t.le, parent);
\r
660 if (!ups.isEmpty()) {
\r
661 for (SceneGraph up : ups)
\r
663 up.init(t.ue, parent);
\r
671 private static WeakHashMap<ElementClass, ElementClass> FADEIN_CLASSES =
\r
672 new WeakHashMap<ElementClass, ElementClass>();
\r
673 private static WeakHashMap<ElementClass, ElementClass> FADEOUT_CLASSES =
\r
674 new WeakHashMap<ElementClass, ElementClass>();
\r
675 public synchronized static ElementClass getFadeElementClass(ElementClass origClass, FadeDir dir)
\r
677 if (dir==FadeDir.In)
\r
679 ElementClass proxyClass = FADEIN_CLASSES.get(origClass);
\r
680 if (proxyClass==null) {
\r
681 proxyClass = createFadeElementClass(origClass, dir);
\r
682 FADEIN_CLASSES.put(origClass, proxyClass);
\r
686 ElementClass proxyClass = FADEOUT_CLASSES.get(origClass);
\r
687 if (proxyClass==null) {
\r
688 proxyClass = createFadeElementClass(origClass, dir);
\r
689 FADEOUT_CLASSES.put(origClass, proxyClass);
\r
695 private static final IProxyProvider PROXY_PROVIDER =
\r
696 new IProxyProvider() {
\r
698 public IElement provide(IElement src) {
\r
699 return src.getHint(KEY_SOURCE_ELEMENT);
\r
703 static ElementClass createFadeElementClass(ElementClass clazz, FadeDir dir)
\r
705 List<ElementHandler> result = new ArrayList<ElementHandler>();
\r
706 List<ElementHandler> lst = new ArrayList<ElementHandler>();
\r
707 for (ElementHandler eh : clazz.getAll())
\r
710 ProxyHandler.addProxyElementHandlers(eh, PROXY_PROVIDER, lst);
\r
711 for (ElementHandler eh2 : lst)
\r
713 if (eh2 instanceof ProxyLifeCycle) continue;
\r
714 if (eh2 instanceof TerminalTopology) continue;
\r
716 if (eh2 instanceof SceneGraph) {
\r
717 result.add( new ProxyFadePaint(dir, (SceneGraph)eh) );
\r
723 return ElementClass.compile(result);
\r
726 static class ProxyFadePaint implements SceneGraph {
\r
730 private static final long serialVersionUID = 3559624682436513231L;
\r
731 static enum FadeDir {In, Out};
\r
734 public ProxyFadePaint(FadeDir dir, SceneGraph orig) {
\r
737 assert(orig!=null);
\r
739 public static IElement getSource(IElement element)
\r
741 return element.getHint(KEY_SOURCE_ELEMENT);
\r
743 public double getPhase(IElement element)
\r
745 Double phase = element.getDiagram().getHint(KEY_PHASE);
\r
746 if (phase==null) phase = 0.0;
\r
747 if (dir == FadeDir.Out) phase = 1-phase;
\r
751 public void cleanup(IElement e) {
\r
752 IElement sourceElement = getSource(e);
\r
753 orig.cleanup(sourceElement);
\r
756 public void init(IElement e, G2DParentNode parent) {
\r
757 IElement sourceElement = getSource(e);
\r
758 orig.init(sourceElement, parent);
\r
762 static class MorphTopologyImpl implements Topology {
\r
765 public void connect(IElement edge, EdgeEnd end, IElement node, Terminal terminal) {
\r
769 public void disconnect(IElement edge, EdgeEnd end, IElement node, Terminal terminal) {
\r
773 public Connection getConnection(IElement edge, EdgeEnd end) {
\r
778 public void getConnections(IElement node, Terminal terminal, Collection<Connection> connections) {
\r