1
14 package demo.view.anim;
15
16 import demo.view.DemoDefaults;
17 import y.anim.AnimationFactory;
18 import y.anim.AnimationObject;
19 import y.anim.AnimationPlayer;
20 import y.anim.CompositeAnimationObject;
21 import y.base.EdgeCursor;
22 import y.base.Node;
23 import y.base.NodeCursor;
24 import y.geom.Geom;
25 import y.util.DefaultMutableValue2D;
26 import y.util.MutableValue2D;
27 import y.util.Value2DSettable;
28 import y.view.Arrow;
29 import y.view.BezierEdgeRealizer;
30 import y.view.Drawable;
31 import y.view.EdgeLabel;
32 import y.view.EdgeRealizer;
33 import y.view.Graph2D;
34 import y.view.Graph2DView;
35 import y.view.SmartEdgeLabelModel;
36 import y.view.ViewAnimationFactory;
37
38 import javax.swing.JFrame;
39 import javax.swing.JPanel;
40 import javax.swing.JRootPane;
41 import javax.swing.Timer;
42 import java.awt.BorderLayout;
43 import java.awt.Color;
44 import java.awt.Graphics2D;
45 import java.awt.Rectangle;
46 import java.awt.EventQueue;
47 import java.awt.event.ActionEvent;
48 import java.awt.event.ActionListener;
49 import java.awt.geom.AffineTransform;
50 import java.awt.geom.GeneralPath;
51
52
53
56 public class LabelAnimationDemo {
57 private static final int PREFERRED_DURATION = 10000;
58
59 private final Graph2DView view;
60 private EdgeLabel[] labels;
61 private Timer timer;
62
63
67 public LabelAnimationDemo() {
68 this.view = new Graph2DView();
69 this.view.setFitContentOnResize(true);
70 init();
71 }
72
73
78 private void init() {
79 final Graph2D graph = view.getGraph2D();
80
81 Node node;
83 node = graph.createNode(225, 125);
84 graph.getRealizer(node).setSize(0, 0);
85
86 EdgeRealizer er;
87 er = graph.getRealizer(graph.createEdge(node, node));
88 er.setLineColor(Color.LIGHT_GRAY);
89 er.clearBends();
90 er.appendBend(225, 25);
91
92
93 final BezierEdgeRealizer ber = new BezierEdgeRealizer();
94 ber.appendBend(100, 0);
95 ber.appendBend(200, 400);
96 ber.appendBend(300, 100);
97 ber.setTargetArrow(Arrow.DELTA);
98 ber.setLineColor(Color.LIGHT_GRAY);
99 graph.createEdge(graph.createNode(50, 200), graph.createNode(400, 200),
100 ber.createCopy());
101
102 ber.clearBends();
103 ber.appendBend(300, 250);
104 ber.appendBend(200, 550);
105 ber.appendBend(100, 250);
106 graph.createEdge(graph.createNode(400, 350), graph.createNode(50, 350),
107 ber.createCopy());
108
109
110 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
111 graph.getRealizer(nc.node()).setVisible(false);
112 }
113
114 final String[] labelTexts = {
115 "Selfloop",
116 "A Label",
117 "Another Label"
118 };
119
120 labels = new EdgeLabel[labelTexts.length];
121
122 final EdgeCursor ec = graph.edges();
123 for (int i = 0, n = labelTexts.length; i < n; ++i, ec.next()) {
124 labels[i] = new EdgeLabel(labelTexts[i]);
125 labels[i].setLabelModel(new SmartEdgeLabelModel());
126 labels[i].setPosition(EdgeLabel.ANYWHERE);
127 labels[i].bindRealizer(graph.getRealizer(ec.edge()));
128 }
129
130 node = graph.createNode(10, 10);
131 graph.getRealizer(node).setSize(0, 0);
132
133 er = graph.getRealizer(graph.createEdge(node, node));
134 er.setLineColor(Color.LIGHT_GRAY);
135 er.clearBends();
136 er.appendBend(470, 10);
137 er.appendBend(470, 470);
138 er.appendBend(10, 470);
139
140
141 timer = new Timer(PREFERRED_DURATION + 500,
142 new ActionListener() {
143 private boolean invert;
144
145 public void actionPerformed(final ActionEvent e) {
146 play(invert);
147 invert = !invert;
148 }
149 });
150 timer.setInitialDelay(1000);
151 timer.start();
152 }
153
154
160 private void play(final boolean invert) {
161 final Graph2D graph = view.getGraph2D();
162 EdgeRealizer er;
163
164 final EdgeCursor ec = graph.edges();
165 int i = 0;
166
167 final ViewAnimationFactory factory = new ViewAnimationFactory(view);
168
169
173 er = graph.getRealizer(ec.edge());
175 ec.next();
176
177 final Drawable selfloopLabel = ViewAnimationFactory.createDrawable(labels[i++]);
179
180 final AnimationObject selfloopAnimation =
183 factory.traversePath(er.getPath(), false, selfloopLabel, true, false,
184 PREFERRED_DURATION);
185
186 er = graph.getRealizer(ec.edge());
188 ec.next();
189
190 final Drawable drawable = ViewAnimationFactory.createDrawable(labels[i++]);
192
193 final AnimationObject standard =
196 factory.traversePath(er.getPath(), invert, drawable, true, true,
197 PREFERRED_DURATION);
198
199
207 er = graph.getRealizer(ec.edge());
209 ec.next();
210 final GeneralPath path = er.getPath();
211
212 final AnimationDrawable animationDrawable =
214 new AnimationDrawable(ViewAnimationFactory.createDrawable(labels[i]));
215
216 final AnimationObject custom = new AnimationObject() {
219 private final AnimationObject delegate =
220 factory.traversePath(path, invert,
221 animationDrawable.createPositionMutator(),
222 animationDrawable.createDirectionMutator(),
223 PREFERRED_DURATION);
224 private final Value2DSettable internalOffset =
225 animationDrawable.createOffsetMutator();
226
227 public void initAnimation() {
228 graph.addDrawable(animationDrawable);
229 delegate.initAnimation();
230 }
231
232 public void calcFrame(final double time) {
233 internalOffset.setX(invert ? time : 1.0 - time);
234 delegate.calcFrame(time);
235 }
236
237 public void disposeAnimation() {
238 delegate.disposeAnimation();
239 graph.removeDrawable(animationDrawable);
240 }
241
242 public long preferredDuration() {
243 return delegate.preferredDuration();
244 }
245 };
246
247
248 er = graph.getRealizer(ec.edge());
249 final GeneralPath border = er.getPath();
250
251 final CompositeAnimationObject arrows = AnimationFactory.createConcurrency();
252
253 final AnimationObject NO_TIME = AnimationFactory.createPause(0);
254 final long pause = PREFERRED_DURATION / 19;
255 final long rest = PREFERRED_DURATION % 19;
256 final long duration = pause * 10 + rest;
257 for (int j = 0; j < 10; ++j) {
258 final Drawable arrowDrawable = new Drawable() {
259 private final Arrow arrow = Arrow.STANDARD;
260
261 public void paint(final Graphics2D gfx) {
262 final Color oldColor = gfx.getColor();
263 gfx.setColor(Color.GRAY);
264 arrow.paint(gfx, 0, 0, 1, 0);
265 gfx.setColor(oldColor);
266 }
267
268 public Rectangle getBounds() {
269 return arrow.getShape().getBounds();
270 }
271 };
272
273 final CompositeAnimationObject sequence = AnimationFactory.createLazySequence();
274 sequence.addAnimation(AnimationFactory.createPause(j * pause));
275 sequence.addAnimation(
276 factory.traversePath(border, false, arrowDrawable, true, false,
277 duration));
278 sequence.addAnimation(NO_TIME);
279 arrows.addAnimation(sequence);
280 }
281
282 final CompositeAnimationObject concurrency = AnimationFactory.createConcurrency();
283 concurrency.addAnimation(selfloopAnimation);
284 concurrency.addAnimation(standard);
285 concurrency.addAnimation(custom);
286 concurrency.addAnimation(arrows);
287
288 final AnimationPlayer player = factory.createConfiguredPlayer();
289 player.animate(concurrency);
290 }
291
292
297 private void start(final String title) {
298 final JFrame frame = new JFrame(title);
299
300 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
301 addContentTo(frame.getRootPane());
302 frame.pack();
303 frame.setLocationRelativeTo(null);
304 frame.setVisible(true);
305 }
306
307 public final void addContentTo(final JRootPane rootPane) {
308 final JPanel contentPane = new JPanel(new BorderLayout());
309 contentPane.add(view, BorderLayout.CENTER);
310
311 rootPane.setContentPane(contentPane);
312 }
313
314 public void dispose() {
315 if (timer != null) {
316 if (timer.isRunning()) {
317 timer.stop();
318 }
319 timer = null;
320 }
321 }
322
323 public static void main(String[] args) {
324 EventQueue.invokeLater(new Runnable() {
325 public void run() {
326 DemoDefaults.initLnF();
327 (new LabelAnimationDemo()).start("Label Animations");
328 }
329 });
330 }
331
332
333
337 private static final class AnimationDrawable implements Drawable {
338 private final Drawable wrappee;
339 private final Rectangle bounds;
340
341
342 private MutableValue2D position;
343
344
348 private MutableValue2D offset;
349
350
351 private MutableValue2D direction;
352
353 AnimationDrawable(final Drawable wrappee) {
354 this.wrappee = wrappee;
355 final Rectangle wrappeeBounds = wrappee.getBounds();
356 this.position = DefaultMutableValue2D.create(wrappeeBounds.getCenterX(),
357 wrappeeBounds.getCenterY());
358 this.offset = DefaultMutableValue2D.create(0, 1);
359 this.direction = DefaultMutableValue2D.create();
360 this.bounds = new Rectangle(wrappeeBounds);
361 }
362
363 public Rectangle getBounds() {
364 final Rectangle wrappeeBounds = wrappee.getBounds();
365 final double w = Math.ceil(wrappeeBounds.getWidth());
366 final double h = Math.ceil(wrappeeBounds.getHeight());
367
368 final double x = position.getX();
369 final double y = position.getY();
370
371 final AffineTransform transform = new AffineTransform();
372 transform.translate(x, y);
373 transform.rotate(radians());
374 transform.translate(-x, -y);
375
376 Geom.calcTransformedBounds(x - w * offset.getX(),
377 y - h * offset.getY(),
378 w, h, transform, bounds);
379 return bounds;
380 }
381
382 public void paint(final Graphics2D gfx) {
383 final AffineTransform oldAt = gfx.getTransform();
384 final Rectangle wrappeeBounds = wrappee.getBounds();
385 gfx.translate(position.getX(), position.getY());
386 gfx.rotate(radians());
387 gfx.translate(-wrappeeBounds.x - wrappeeBounds.getWidth() * offset.getX(),
388 -wrappeeBounds.y - wrappeeBounds.getHeight() * offset.getY());
389 wrappee.paint(gfx);
390 gfx.setTransform(oldAt);
391 }
392
393 Value2DSettable createPositionMutator() {
394 return position;
395 }
396
397 Value2DSettable createDirectionMutator() {
398 return direction;
399 }
400
401 Value2DSettable createOffsetMutator() {
402 return offset;
403 }
404
405 private static final double PI_HALF = Math.PI * 0.5;
406
407 private double radians() {
408 double radians = Math.atan2(direction.getY(), direction.getX());
409 if (radians < -PI_HALF) {
410 radians += Math.PI;
411 } else if (radians > PI_HALF) {
412 radians -= Math.PI;
413 }
414 return radians;
415 }
416 }
417 }
418