view dwtx/draw2d/Animation.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
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2005, 2006 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.Animation;

import dwt.dwthelper.utils;
import dwtx.dwtxhelper.Collection;

import dwtx.draw2d.Animator;
import dwtx.draw2d.IFigure;
import dwtx.draw2d.UpdateManager;


/**
 * A utility for coordinating figure animations. During animation, multiple
 * <i>animators</i> are employed to capture the <em>initial</em> and <em>final</em> states
 * for one or more figures. The animators then playback the animation by interpolating the
 * intermediate states for each figure using the initial and final "keyframes".
 * <P>
 * An animator is usually stateless and represents an specific technique. Any state
 * information is stored by the Animation utility. Therefore, one instance can be used
 * with multiple figures. Animators hook into the validation mechanism for figures and
 * connections. These hooks are used to capture the states, and to intercept the typical
 * layout process to insert the interpolated state.
 * <P>
 * To indicate that animation is desired, clients must call {@link #markBegin()} prior to
 * invalidating any figures that are to be included in the animation. After this method is
 * called, changes are made, and {@link #run()} is invoked. The run method will force a
 * validation pass to capture the final states, and then commence the animation. The
 * animation is synchronous and the method does not return until the animation has
 * completed.
 * @see LayoutAnimator
 * @since 3.2
 */
public class Animation {

static class AnimPair {

    const Animator animator;
    const IFigure figure;

    this(Animator animator, IFigure figure) {
        this.animator = animator;
        this.figure = figure;
    }

    public override int opEquals(Object obj) {
        AnimPair pair = cast(AnimPair)obj;
        return pair.animator is animator && pair.figure is figure;
    }

    public override hash_t toHash() {
        return (cast(Object)animator).toHash() ^ (cast(Object)figure).toHash();
    }
}
private static const int DEFAULT_DELAY = 250;
private static Set figureAnimators;
private static Map finalStates;

private static Map initialStates;
private static const int PLAYBACK = 3;
private static float progress;
private static const int RECORD_FINAL = 2;

private static const int RECORD_INITIAL = 1;
private static long startTime;
private static int state;
private static Set toCapture;

private static UpdateManager updateManager;

private static void capture() {
    Iterator keys = figureAnimators.iterator();
    while (keys.hasNext()) {
        AnimPair pair = cast(AnimPair) keys.next();
        if (toCapture.contains(pair))
            pair.animator.capture(pair.figure);
        else
            keys.remove();
    }
}

static void cleanup() {
    if (figureAnimators !is null) {
        Iterator keys = figureAnimators.iterator();
        while (keys.hasNext()) {
            AnimPair pair = cast(AnimPair) keys.next();
            pair.animator.tearDown(pair.figure);
        }
    }

    state = 0;
    step();
    //Allow layout to occur normally
    //updateManager.performUpdate();

    initialStates = null;
    finalStates = null;
    figureAnimators = null;
    updateManager = null;
    toCapture = null;
    state = 0;
}

private static void doRun(int duration) {
    state = RECORD_FINAL;
    findUpdateManager();
    updateManager.performValidation();
    capture();
    state = PLAYBACK;
    progress = 0.1f;
    startTime = System.currentTimeMillis();

    notifyPlaybackStarting();

    while (progress !is 0) {
        step();
        updateManager.performUpdate();
        if (progress is 1.0)
            progress = 0;
        else {
            int delta = cast(int)(System.currentTimeMillis() - startTime);
            if (delta >= duration)
                progress = 1f;
            else
                progress = 0.1f + 0.9f * delta / duration;
        }
    }
}

private static void findUpdateManager() {
    AnimPair pair = cast(AnimPair) figureAnimators.iterator().next();
    updateManager = pair.figure.getUpdateManager();
}

/**
 * Returns the final animation state for the given figure.
 * @param animator the animator for the figure
 * @param figure the figure being animated
 * @return the final state
 * @since 3.2
 */
public static Object getFinalState(Animator animator, IFigure figure) {
    return finalStates.get(new AnimPair(animator, figure));
}

/**
 * Returns the initial animation state for the given animator and figure. If no state was
 * recorded, <code>null</code> is returned.
 * @param animator the animator for the figure
 * @param figure the figure being animated
 * @return the initial state
 * @since 3.2
 */
public static Object getInitialState(Animator animator, IFigure figure) {
    return initialStates.get(new AnimPair(animator, figure));
}

/**
 * Returns the animation progress, where 0.0 < progress &#8804; 1.0.
 * @return the progress of the animation
 * @since 3.2
 */
public static float getProgress() {
    return progress;
}

static void hookAnimator(IFigure figure, Animator animator) {
    AnimPair pair = new AnimPair(animator, figure);
    if (figureAnimators.add(pair))
        animator.init(figure);
}

static void hookNeedsCapture(IFigure figure, Animator animator) {
    AnimPair pair = new AnimPair(animator, figure);
    if (figureAnimators.contains(pair))
        toCapture.add(pair);
}

static bool hookPlayback(IFigure figure, Animator animator) {
    if (toCapture.contains(new AnimPair(animator, figure)))
        return animator.playback_package(figure);
    return false;
}

/**
 * Returns <code>true</code> if animation is in progress.
 * @return <code>true</code> when animating
 * @since 3.2
 */
public static bool isAnimating() {
    return state is PLAYBACK;
}

static bool isFinalRecording() {
    return state is RECORD_FINAL;
}

static bool isInitialRecording() {
    return state is RECORD_INITIAL;
}

/**
 * Marks the beginning of the animation process. If the beginning has already been marked,
 * this has no effect.
 * @return returns <code>true</code> if beginning was not previously marked
 * @since 3.2
 */
public static bool markBegin() {
    if (state is 0) {
        state = RECORD_INITIAL;
        initialStates = new HashMap();
        finalStates = new HashMap();
        figureAnimators = new HashSet();
        toCapture = new HashSet();
        return true;
    }
    return false;
}

private static void notifyPlaybackStarting() {
    Iterator keys = figureAnimators.iterator();
    while (keys.hasNext()) {
        AnimPair pair = cast(AnimPair) keys.next();
        pair.animator.playbackStarting(pair.figure);
    }
}

static void putFinalState(Animator animator, IFigure key, Object state) {
    finalStates.put(new AnimPair(animator, key), state);
}

static void putInitialState(Animator animator, IFigure key, Object state) {
    initialStates.put(new AnimPair(animator, key), state);
}

/**
 * Runs animation using the recommended duration: 250 milliseconds.
 * @see #run(int)
 * @since 3.2
 */
public static void run() {
    run(DEFAULT_DELAY);
}

/**
 * Captures the final states for the animation and then plays the animation.
 * @param duration the length of animation in milliseconds
 * @since 3.2
 */
public static void run(int duration) {
    if (state is 0)
        return;
    try {
        if (!figureAnimators.isEmpty())
            doRun(duration);
    } finally {
        cleanup();
    }
}

private static void step() {
    Iterator iter = initialStates.keySet().iterator();
    while (iter.hasNext())
        (cast(AnimPair)iter.next()).figure.revalidate();
}

}