diff dwtx/draw2d/RoutingAnimator.d @ 98:95307ad235d9

Added Draw2d code, still work in progress
author Frank Benoit <benoit@tionex.de>
date Sun, 03 Aug 2008 00:52:14 +0200
parents
children 2d6540440fe6
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/draw2d/RoutingAnimator.d	Sun Aug 03 00:52:14 2008 +0200
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * 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:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.draw2d.RoutingAnimator;
+
+import dwt.dwthelper.utils;
+
+import dwtx.draw2d.geometry.Point;
+import dwtx.draw2d.geometry.PointList;
+import dwtx.draw2d.Animator;
+import dwtx.draw2d.RoutingListener;
+import dwtx.draw2d.IFigure;
+import dwtx.draw2d.Connection;
+import dwtx.draw2d.Animation;
+
+/**
+ * Animates the routing of a connection. The animator will capture the effects of the
+ * connection's router, and the play back the placement of the routing, interpolating the
+ * intermediate routes.
+ * <P>
+ * To use a routing animator, hook it as a routing listener for the connection whose
+ * points are to be animated, by calling {@link
+ * PolylineConnection#addRoutingListener(RoutingListener)}. An animator is active
+ * only when the Animation utility is activated.
+ *
+ * @since 3.2
+ */
+public class RoutingAnimator : Animator , RoutingListener {
+
+static const RoutingAnimator INSTANCE;
+static this(){
+    INSTANCE = new RoutingAnimator();
+}
+/**
+ * Constructs a routing animator for use with one or more connections. The default
+ * instance ({@link #getDefault()} can be used on any number of connections.
+ *
+ * @since 3.2
+ */
+protected this() { }
+
+/**
+ * Overridden to sync initial and final states.
+ * @see Animator#playbackStarting(IFigure)
+ */
+public void playbackStarting(IFigure connection) {
+    reconcileStates(cast(Connection)connection);
+}
+
+/**
+ * Returns the current state of the connection. Currently, this is a copy of the list of
+ * points. However this Object could change in future releases and should not be
+ * considered API.
+ * @see Animator#getCurrentState(IFigure)
+ */
+protected Object getCurrentState(IFigure connection) {
+    return (cast(Connection)connection).getPoints().getCopy();
+}
+
+/**
+ * Returns the default instance.
+ * @return the default instance
+ * @since 3.2
+ */
+public static RoutingAnimator getDefault() {
+    return INSTANCE;
+}
+
+/**
+ * Hooks invalidate for animation purposes.
+ * @see RoutingListener#invalidate(Connection)
+ */
+public final void invalidate(Connection conn) {
+    if (Animation.isInitialRecording())
+        Animation.hookAnimator(conn, this);
+}
+
+/**
+ * Plays back the interpolated state.
+ * @see Animator#playback(IFigure)
+ */
+protected bool playback(IFigure figure) {
+    Connection conn = cast(Connection) figure;
+
+    PointList list1 = cast(PointList)Animation.getInitialState(this, conn);
+    PointList list2 = cast(PointList)Animation.getFinalState(this, conn);
+    if (list1 is null) {
+        conn.setVisible(false);
+        return true;
+    }
+
+    float progress = Animation.getProgress();
+    if (list1.size() is list2.size()) {
+        Point pt1 = new Point(), pt2 = new Point();
+        PointList points = conn.getPoints();
+        points.removeAllPoints();
+        for (int i = 0; i < list1.size(); i++) {
+            list1.getPoint(pt2, i);
+            list2.getPoint(pt1, i);
+            pt1.x = cast(int)Math.round(pt1.x * progress + (1 - progress) * pt2.x);
+            pt1.y = cast(int)Math.round(pt1.y * progress + (1 - progress) * pt2.y);
+            points.addPoint(pt1);
+        }
+        conn.setPoints(points);
+    }
+    return true;
+}
+
+/**
+ * Hooks post routing for animation purposes.
+ * @see RoutingListener#postRoute(Connection)
+ */
+public final void postRoute(Connection connection) {
+    if (Animation.isFinalRecording())
+        Animation.hookNeedsCapture(connection, this);
+}
+
+private void reconcileStates(Connection conn) {
+    PointList points1 = cast(PointList)Animation.getInitialState(this, conn);
+    PointList points2 = cast(PointList)Animation.getFinalState(this, conn);
+
+    if (points1 !is null && points1.size() !is points2.size()) {
+        Point p = new Point(), q = new Point();
+
+        int size1 = points1.size() - 1;
+        int size2 = points2.size() - 1;
+
+        int i1 = size1;
+        int i2 = size2;
+
+        double current1 = 1.0;
+        double current2 = 1.0;
+
+        double prev1 = 1.0;
+        double prev2 = 1.0;
+
+        while (i1 > 0 || i2 > 0) {
+            if (Math.abs(current1 - current2) < 0.1
+              && i1 > 0 && i2 > 0) {
+                //Both points are the same, use them and go on;
+                prev1 = current1;
+                prev2 = current2;
+                i1--;
+                i2--;
+                current1 = cast(double)i1 / size1;
+                current2 = cast(double)i2 / size2;
+            } else if (current1 < current2) {
+                //2 needs to catch up
+                // current1 < current2 < prev1
+                points1.getPoint(p, i1);
+                points1.getPoint(q, i1 + 1);
+
+                p.x = cast(int)(((q.x * (current2 - current1) + p.x * (prev1 - current2))
+                    / (prev1 - current1)));
+                p.y = cast(int)(((q.y * (current2 - current1) + p.y * (prev1 - current2))
+                    / (prev1 - current1)));
+
+                points1.insertPoint(p, i1 + 1);
+
+                prev1 = prev2 = current2;
+                i2--;
+                current2 = cast(double)i2 / size2;
+
+            } else {
+                //1 needs to catch up
+                // current2< current1 < prev2
+
+                points2.getPoint(p, i2);
+                points2.getPoint(q, i2 + 1);
+
+                p.x = cast(int)(((q.x * (current1 - current2) + p.x * (prev2 - current1))
+                    / (prev2 - current2)));
+                p.y = cast(int)(((q.y * (current1 - current2) + p.y * (prev2 - current1))
+                    / (prev2 - current2)));
+
+                points2.insertPoint(p, i2 + 1);
+
+                prev2 = prev1 = current1;
+                i1--;
+                current1 = cast(double)i1 / size1;
+            }
+        }
+    }
+}
+
+/**
+ * This callback is unused. Reserved for possible future use.
+ * @see RoutingListener#remove(Connection)
+ */
+public final void remove(Connection connection) { }
+
+/**
+ * Hooks route to intercept routing during animation playback.
+ * @see RoutingListener#route(Connection)
+ */
+public final bool route(Connection conn) {
+    return Animation.isAnimating() && Animation.hookPlayback(conn, this);
+}
+
+/**
+ * This callback is unused. Reserved for possible future use.
+ * @see RoutingListener#setConstraint(Connection, Object)
+ */
+public final void setConstraint(Connection connection, Object constraint) { }
+
+}