
org.monte.media.DefaultAnimator Maven / Gradle / Ivy
/**
* @(#)DefaultAnimator.java 1.0.1 2008-08-19
*
* Copyright (c) 2008 Werner Randelshofer
* Hausmatt 10, CH-6405 Goldau, Switzerland
* All rights reserved.
*
* The copyright of this software is owned by Werner Randelshofer.
* You may not use, copy or modify this software, except in
* accordance with the license agreement you entered into with
* Werner Randelshofer. For details see accompanying license terms.
*/
package org.monte.media;
import java.util.*;
import javax.swing.event.*;
/**
* DefaultAnimator.
*
* @author Werner Randelshofer
* @version 1.0.1 2008-08-19 finish method of interpolators was not always called.
*
1.0 Apr 28, 2008 Created.
*/
public class DefaultAnimator implements Animator {
protected EventListenerList listenerList = new EventListenerList();
/**
* List of active interpolators.
* Implementation note: This vector is only accessed by the animationThread.
*/
private Vector activeInterpolators;
/**
* List of new interpolators.
* Implementation note: The dispatcher thread adds items to this list, the
* animationThread removes items.
* This queue is used to synchronize the dispatcher thread with the animation
* thread.
* Note: the dispatcher thread is not necesseraly the Event Dispatcher
* thread. The dispatcher thread is any thread which dispatches interpolators.
*/
private Vector newInterpolators;
/**
* We keep a reference to the animationThread to be able to stop it and
* to be able to wait until it is finished.
*/
private Thread animationThread;
private boolean isAnimating;
private Object lock = new Object();
protected ChangeEvent changeEvent;
/**
* The sleep time controls the framerate of the animator.
* A value of 33 is approximately 30 frames per second.
*/
private int sleep = 33;
/** Creates a new instance. */
public DefaultAnimator() {
activeInterpolators = new Vector();
newInterpolators = new Vector();
}
/**
* Set the lock object, on which the animator synchronizes while
* animating the interpolators.
*/
public void setLock(Object lock) {
this.lock = lock;
}
public boolean isActive() {
return animationThread != null;
}
public void start() {
stop();
animationThread = new Thread(this);
animationThread.start();
}
public void stop() {
if (animationThread != null) {
Thread t = animationThread;
animationThread = null;
t.interrupt();
try {
t.join();
} catch (InterruptedException e) {
}
}
}
/**
* Dispatches an interpolator for the animation thread.
* This will launch the animation thread if it is not already active.
*/
public void dispatch(Interpolator interpolator) {
synchronized (newInterpolators) {
newInterpolators.addElement(interpolator);
if (! isActive()) start();
}
}
public void animateStep() {
long now = System.currentTimeMillis();
// Enqueue new interpolators into the activeInterpolators list
// Avoid enqueuing new interpolators which must be run sequentally
// with active interpolators.
OuterLoop: for (int i=0; i < newInterpolators.size(); i++) {
Interpolator candidate = (Interpolator) newInterpolators.elementAt(i);
boolean isEnqueueable = true;
for (int j=0; j < i; j++) {
Interpolator before = (Interpolator) newInterpolators.elementAt(j);
if (candidate.isSequential(before)) {
isEnqueueable = false;
break;
}
}
if (isEnqueueable) {
for (int j=0; j < activeInterpolators.size(); j++) {
Interpolator before = (Interpolator) activeInterpolators.elementAt(j);
if (candidate.replaces(before)) {
before.finish(now);
}
if (candidate.isSequential(before)) {
isEnqueueable = false;
break;
}
}
}
if (isEnqueueable) {
candidate.initialize(now);
activeInterpolators.addElement(candidate);
newInterpolators.removeElementAt(i--);
}
}
// Animate the active interpolators
// Remove finished interpolators.
for (int i=0; i < activeInterpolators.size(); i++) {
Interpolator active = (Interpolator) activeInterpolators.elementAt(i);
if (active.isFinished()) {
activeInterpolators.removeElementAt(i--);
} else if (active.isElapsed(now)) {
active.finish(now);
activeInterpolators.removeElementAt(i--);
} else {
active.interpolate(now);
}
}
}
public void run() {
//fireStateChanged();
// Animation loop.
// We loop here as long as we have something to do.
while (Thread.currentThread() == animationThread) {
synchronized (lock) {
animateStep();
}
boolean hasFinished = false;
synchronized (newInterpolators) {
if (activeInterpolators.size() == 0 && newInterpolators.size() == 0) {
animationThread = null;
hasFinished = true;
}
}
if (hasFinished) {
fireStateChanged();
return;
}
try {
Thread.sleep(sleep); // 30 frames per second
} catch (InterruptedException e) {
}
}
// If we have been stopped, we finish all interpolators.
// Note: We get here only if method stop() has been called.
// If we finish by ourselves, we return from within the while loop
// above.
synchronized (newInterpolators) {
synchronized (lock) {
long now = System.currentTimeMillis();
for (int i=0; i < activeInterpolators.size(); i++) {
Interpolator active = (Interpolator) activeInterpolators.elementAt(i);
active.finish(now);
}
for (int i=0; i < newInterpolators.size(); i++) {
Interpolator candidate = (Interpolator) newInterpolators.elementAt(i);
candidate.initialize(now);
candidate.finish(now);
}
activeInterpolators.removeAllElements();
newInterpolators.removeAllElements();
}
}
fireStateChanged();
}
public void addChangeListener(ChangeListener listener) {
listenerList.add(ChangeListener.class, listener);
}
public void removeChangeListener(ChangeListener listener) {
listenerList.remove(ChangeListener.class, listener);
}
/**
* Notify all listeners that have registered interest for
* notification on this event type.
*/
protected void fireStateChanged() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==ChangeListener.class) {
// Lazily create the event:
if (changeEvent == null) {
changeEvent = new ChangeEvent(this);
}
((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
}
}
}
public boolean isSynchronous() {
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy