Newer
Older
MagnetRON / src / org / ntlab / animations / MagnetRONAnimation.java
Aki Hongo on 20 Aug 2021 11 KB #5 Implemented so that method call and return value,
package org.ntlab.animations;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.util.TimerTask;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.ntlab.deltaViewer.DeltaGraphAdapter;
import org.ntlab.deltaViewer.MagnetRONScheduledThreadPoolExecutor;

import com.mxgraph.model.mxICell;
import com.mxgraph.swing.mxGraphComponent;
/**
 * 
 * 
 * @author Nitta Lab.
 */
public abstract class MagnetRONAnimation {

	protected DeltaGraphAdapter mxgraph;
	protected mxGraphComponent mxgraphComponent;

	protected ThreadPoolExecutor threadPoolExecutor;
	protected ScheduledFuture<?> scheduledFuture;
	/**
	 * Initial delays the start of an animation.
	 *
	 * Cannot be negative. Setting to a negative number will result in {@link IllegalArgumentException}.
	 *
	 * @defaultValue 0ms
	 */
	private long initialDelay;
	private static final long DEFAULT_INITIAL_DELAY = 0L;
	/**
	 * Delays the interval between repeating an animation.
	 *
	 * Cannot be negative. Setting to a negative number will result in {@link IllegalArgumentException}.
	 *
	 * @defaultValue 0ms
	 */
	private long delay;
	private static final long DEFAULT_DELAY = 0L;

	/**
	 * Defines the direction/speed at which the {@code MagnetRONAnimation} is expected to
	 * be played.
	 * <p>
	 * The absolute value of {@code rate} indicates the speed which the
	 * {@code Animation} is to be played, while the sign of {@code rate}
	 * indicates the direction. A positive value of {@code rate} indicates
	 * forward play, a negative value indicates backward play and {@code 0.0} to
	 * stop a running {@code MagnetRONAnimation}.
	 * <p>
	 * Rate {@code 1.0} is normal play, {@code 2.0} is 2 time normal,
	 * {@code -1.0} is backwards, etc...
	 *
	 * <p>
	 * Inverting the rate of a running {@code MagnetRONAnimation} will cause the
	 * {@code MagnetRONAnimation} to reverse direction in place and play back over the
	 * portion of the {@code MagnetRONAnimation} that has already elapsed.
	 *
	 * @defaultValue 1.0
	 */
	private double rate;
	private static final double DEFAULT_RATE = 1.0;

	/**
	 * Read-only variable to indicate current direction/speed at which the
	 * {@code MagnetRONAnimation} is being played.
	 * <p>
	 * {@code currentRate} is not necessary equal to {@code rate}.
	 * {@code currentRate} is set to {@code 0.0} when animation is paused or
	 * stopped. {@code currentRate} may also point to different direction during
	 * reverse cycles when {@code reverse} is {@code true}
	 *
	 * @defaultValue 0.0
	 */
	private double currentRate;
	private static final double DEFAULT_CURRENT_RATE = 0.0;

	/**
	 * Defines the number of cycles in this animation. The {@code totalCycleCount}
	 * may be {@code INDEFINITE} for animations that repeat indefinitely, but
	 * must otherwise be > 0.
	 * <p>
	 * It is not possible to change the {@code totalCycleCount} of a running
	 * {@code MagnetRONAnimation}. If the value of {@code totalCycleCount} is changed for a
	 * running {@code MagnetRONAnimation}, the animation has to be stopped and started again to pick
	 * up the new value.
	 *
	 * @defaultValue 1
	 *
	 */
	private int totalCycleCount;
	private static final int DEFAULT_TOTAL_CYCLE_COUNT = 1;
	/**
	 * The current number of cycles in this animation.
	 * 
	 * @defaultValu 0
	 */
	private int currentCycleCount = 0;

	/**
	 * Used to specify an animation that repeats indefinitely, until the
	 * {@code stop()} method is called.
	 */
	private static final int INDEFINITE = -1;

	/**
	 * The status of the {@code MagnetRONAnimation}.
	 *
	 * In {@code MagnetRONAnimation} can be in one of three states:
	 * {@link Status#STOPPED}, {@link Status#PAUSED} or {@link Status#RUNNING}.
	 */
	private Status currentStatus;
	private static final Status DEFAULT_STATUS = Status.STOPPED;

	/**
	 * The action to be executed at the conclusion of this {@code MagnetRONAnimation}.
	 */
	private ActionListener onFinished;

	/**
	 * Defines whether this
	 * {@code MagnetRONAnimation} reverses direction on alternating cycles. If
	 * {@code true}, the
	 * {@code MagnetRONAnimation} will proceed reverses on the cycle. 
	 * Otherwise, animation will loop such that each cycle proceeds forward from the start.
	 *
	 * It is not possible to change the {@code reverse} flag of a running
	 * {@code MagnetRONAnimation}. If the value of {@code reverse} is changed for a
	 * running {@code MagnetRONAnimation}, the animation has to be stopped and started again to pick
	 * up the new value.
	 *
	 * @defaultValue false
	 */
	private boolean reverse;
	private static final boolean DEFAULT_REVERSE = false;

	/**
	 * The object to animate.
	 */
	private mxICell sourceCell;

	/**
	 * The initial point of sourceCell.
	 */
	private Point2D sourceInitPoint;
	/**
	 * The target point where the sourceCell animates.
	 */
	private Point2D destinationPoint;
	/**
	 * The point to update for each cycle count.
	 */
	private Point2D velocity;

	/**
	 * The possible state for MagnetRONAnimation.
	 */
	protected static enum Status {
		/**
		 * The paused state.
		 */
		PAUSED,
		/**
		 * The running state.
		 */
		RUNNING,
		/**
		 * The stopped state.
		 */
		STOPPED
	}

	/**
	 * The constructor of {@code MagnetRONAnimation}.
	 * 
	 * @param mxgraph: visualization model
	 * @param mxgraphComponent: visualization model group
	 */
	protected MagnetRONAnimation(DeltaGraphAdapter mxgraph, mxGraphComponent mxgraphComponent) {
		this.mxgraph = mxgraph;
		this.mxgraphComponent = mxgraphComponent;
	}

	protected void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) {
		this.threadPoolExecutor = threadPoolExecutor;
	}

	public void setInitialDelay(long initialDelay) {
		this.initialDelay = initialDelay;
	}

	public void setDelay(long delay) {
		this.delay = delay;
	}

	protected void setRate(double rate) {
		this.rate = rate;
	}

	protected void setCurrentRate(double currentRate) {
		this.currentRate = currentRate;
	}

	public void setTotalCycleCount(int totalCycleCount) {
		this.totalCycleCount = totalCycleCount;
	}

	protected void setCurrentCycleCount(int currentCycleCount) {
		this.currentCycleCount = currentCycleCount;
	}

	protected void setCurrentStatus(Status currentStatus) {
		this.currentStatus = currentStatus;
	}

	public void setOnFinished(ActionListener onFinished) {
		this.onFinished = onFinished;
	}

	public void setReverse(boolean reverse) {
		this.reverse = reverse;
	}

	protected void setSourceCell(mxICell sourceCell) {
		this.sourceCell = sourceCell;
	}

	protected void setSourceInitialPoint(Point2D sourceInitPoint) {
		this.sourceInitPoint = sourceInitPoint;
	}

	protected void setDestinationPoint(Point2D destinationPoint) {
		this.destinationPoint = destinationPoint;
	}

	protected void setVelocity(Point2D velocity) {
		this.velocity = velocity;
	}

	protected ThreadPoolExecutor getThreadPoolExecutor() {
		return threadPoolExecutor;
	}

	public long getInitialDelay() {
		if (initialDelay == 0L) return DEFAULT_INITIAL_DELAY;
		return initialDelay;
	}

	public long getDelay() {
		if (delay == 0L) return DEFAULT_DELAY;
		return delay;
	}

	protected double getRate() {
		if (rate == 0.0) return DEFAULT_RATE;
		return rate;
	}

	protected double getCurrentRate() {
		if (currentRate == 0.0) return DEFAULT_CURRENT_RATE;
		return currentRate;
	}

	public int getTotalCycleCount() {
		if (totalCycleCount == 0) return DEFAULT_TOTAL_CYCLE_COUNT;
		return totalCycleCount;
	}

	protected int getCurrentCycleCount() {
		return currentCycleCount;
	}

	protected Status getCurrentStatus() {
		if (currentStatus == null) return DEFAULT_STATUS;
		return currentStatus;
	}

	public ActionListener getOnFinished() {
		return onFinished;
	}

	public boolean getReverse() {
		if (!reverse) return DEFAULT_REVERSE;
		return reverse;
	}

	protected mxICell getSourceCell() {
		return sourceCell;
	}

	protected Point2D getSourceInitalPoint() {
		return sourceInitPoint;
	}

	protected Point2D getDestinationPoint() {
		return destinationPoint;
	}

	protected Point2D getVelocity() {
		return velocity;
	}

	/**
	 * Set expand or reduction animation of edge to targetPoint.
	 * Must be call {@code MagnetRONAnimation#init(mxICell, mxPoint, ThreadPoolExecutor)} before calling {@code MagnetRONAnimation#play()}.
	 * 
	 * @param sourceCell: edge object
	 * @param destinationPoint
	 */
	protected void init(mxICell sourceCell, Point2D destinationPoint, ThreadPoolExecutor threadPoolExecutor) {
		setSourceCell(sourceCell);
		setDestinationPoint(destinationPoint);
		setThreadPoolExecutor(threadPoolExecutor);
		setSourceInitialPoint(getSourceCell().getGeometry().getPoint());
		setCurrentCycleCount(0);
	}

	public void stepCurrentCycle(int currentCycleCount) {
		if (!getReverse()) { // Animation direction is forward.
			setCurrentCycleCount((int) (currentCycleCount + Math.signum(getTotalCycleCount())));
			System.out.println("curCycleCount: " + getCurrentCycleCount());
		} else {
			setCurrentCycleCount((int) (currentCycleCount - Math.signum(getTotalCycleCount())));
		}
	}

	public void interpolate(double cycleCount) {

	}

	public void playFrom() {

	}

	public void play() {
		switch (getCurrentStatus()) {
			case STOPPED:
				if (getThreadPoolExecutor() != null & getThreadPoolExecutor() instanceof ScheduledThreadPoolExecutor) {
					ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = (ScheduledThreadPoolExecutor) getThreadPoolExecutor();
					this.scheduledFuture = scheduledThreadPoolExecutor.scheduleWithFixedDelay(new TimerTask() {
						@Override
						public void run() {
							if(Math.abs(getCurrentCycleCount()) < Math.abs(getTotalCycleCount())) {
								jumpTo(getCurrentCycleCount());
								stepCurrentCycle(getCurrentCycleCount());
							} else if(Math.abs(getCurrentCycleCount()) >= Math.abs(getTotalCycleCount())){
								scheduledFuture.cancel(true);
								onFinished();
							}
						}
					}, getInitialDelay(), getDelay(), TimeUnit.MILLISECONDS);	
					setCurrentStatus(Status.RUNNING);
				};
				break;
			case PAUSED:
				if (getThreadPoolExecutor() != null & getThreadPoolExecutor() instanceof MagnetRONScheduledThreadPoolExecutor) {
					MagnetRONScheduledThreadPoolExecutor scheduledThreadPoolExecutor = (MagnetRONScheduledThreadPoolExecutor) getThreadPoolExecutor();
					scheduledThreadPoolExecutor.resume();
					setCurrentStatus(Status.RUNNING);
				}
				break;
			default:
				break;
		};
	}

	public void playFormStart() {

	}

	public void stop() {
		if (getCurrentStatus() == Status.RUNNING) {
		}
		setCurrentStatus(Status.STOPPED);
		setCurrentRate(0.0);
	}

	public void pause() {
		if (getCurrentStatus() == Status.RUNNING) {
			if (getThreadPoolExecutor() != null && getThreadPoolExecutor() instanceof MagnetRONScheduledThreadPoolExecutor) {
				MagnetRONScheduledThreadPoolExecutor scheduledThreadPoolExecutor = (MagnetRONScheduledThreadPoolExecutor) getThreadPoolExecutor();
				scheduledThreadPoolExecutor.pause();
				setCurrentStatus(Status.PAUSED);
			}
		}

	}

	private final void onFinished() {
		stop();
		final ActionListener listener = getOnFinished();
		if (listener != null) {
			try {
				listener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null));
			} catch (Exception e) {
				Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
			}
		}
	}

	protected abstract void jumpTo(int currentCycleCount);
	
	public void sleepThread(long millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
	}
}