/* * This file is part of Spoutcraft. * * Copyright (c) 2011 SpoutcraftDev <http://spoutcraft.org/> * Spoutcraft is licensed under the GNU Lesser General Public License. * * Spoutcraft is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Spoutcraft is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.spoutcraft.api.animation; import java.util.Timer; import java.util.TimerTask; public class Animation { public enum Direction { FORWARD(1), BACKWARD(-1); public final double modifier; private Direction(double mod) { this.modifier = mod; } } public enum State { STOPPED, PAUSED, RUNNING; } private Animatable startValue = null, endValue = null; private Number startNumber = null, endNumber = null; private int duration; int currentTime; private Direction direction = Direction.FORWARD; private State state = State.STOPPED; private AnimationProgress animationProgress = new LinearAnimationProgress(); private ValueSetDelegate property; private static Timer timer = new Timer(); private int delay = 1000 / 60; private AnimationRunnable animator = new AnimationRunnable(this); public State getState() { return state; } public Animatable getStartValue() { return startValue; } public void setStartValue(Animatable startValue) { this.startValue = startValue; } public Animatable getEndValue() { return endValue; } public void setEndValue(Animatable endValue) { this.endValue = endValue; } public int getDuration() { return duration; } public void setDuration(int duration) { this.duration = duration; } public int getCurrentTime() { return currentTime; } public void setCurrentTime(int currentTime) { this.currentTime = currentTime; } public Direction getDirection() { return direction; } public void setDirection(Direction direction) { this.direction = direction; } public void setAnimationProgress(AnimationProgress animationProgress) { this.animationProgress = animationProgress; } public AnimationProgress getAnimationProgress() { return animationProgress; } public void start() { this.state = State.RUNNING; switch (direction) { case FORWARD: currentTime = 0; break; case BACKWARD: currentTime = duration; break; } timer.schedule(animator, delay, delay); } public void pause() { this.state = State.PAUSED; animator.cancel(); } public void resume() { if (this.state == State.PAUSED) { this.state = State.RUNNING; timer.schedule(animator, delay, delay); } } public void stop() { this.state = State.STOPPED; switch (direction) { case FORWARD: this.currentTime = duration; break; case BACKWARD: this.currentTime = 0; break; } animator.cancel(); if (startValue != null) { property.set(getCurrentValue()); } else { property.set(getCurrentValueNumber()); } } public Animatable getCurrentValue() { return startValue.getValueAt(getAnimationProgress().getValueAt((double) currentTime / (double) duration), startValue, endValue); } public void setValueDelegate(ValueSetDelegate property) { this.property = property; } public ValueSetDelegate getValueDelegate() { return property; } public void setStartNumber(Number startNumber) { this.startNumber = startNumber; } public Number getStartNumber() { return startNumber; } public void setEndNumber(Number endNumber) { this.endNumber = endNumber; } public Number getEndNumber() { return endNumber; } public void setFramesPerSecond(int fps) { delay = 1000 / fps; } private class AnimationRunnable extends TimerTask { public Animation animation; public AnimationRunnable(Animation ani) { animation = ani; } @Override public void run() { try { if (animation.getState() != Animation.State.RUNNING) { cancel(); return; } int time = animation.getCurrentTime(); time += delay * animation.getDirection().modifier; // For the direction if (time >= animation.getDuration() && animation.getDirection() == Direction.FORWARD) { animation.stop(); time = animation.getDuration(); } if (time <= 0 && animation.getDirection() == Direction.BACKWARD) { animation.stop(); time = 0; } animation.setCurrentTime(time); if (startNumber == null) { Animatable value = animation.getCurrentValue(); animation.getValueDelegate().set(value); } else { Number value = animation.getCurrentValueNumber(); animation.getValueDelegate().set(value); } } catch (Exception e) { e.printStackTrace(); animation.state = Animation.State.STOPPED; cancel(); } } } public Number getCurrentValueNumber() { double p = (double) currentTime / (double) duration; if (startNumber instanceof Integer) { int p1 = (Integer) startNumber, p2 = (Integer) endNumber; return (int) (p1 + (p2 - p1)*p); } if (startNumber instanceof Double) { double p1 = (Double) startNumber, p2 = (Double) endNumber; return p1 + (p2 - p1)*p; } if (startNumber instanceof Long) { long p1 = (Long) startNumber, p2 = (Long) endNumber; return (long) (p1 + (p2 - p1)*p); } if (startNumber instanceof Float) { float p1 = (Float) startNumber, p2 = (Float) endNumber; return (float) (p1 + (p2 - p1)*p); } if (startNumber instanceof Short) { short p1 = (Short) startNumber, p2 = (Short) endNumber; return (short) (p1 + (p2 - p1)*p); } if (startNumber instanceof Byte) { byte p1 = (Byte) startNumber, p2 = (Byte) endNumber; return (byte) (p1 + (p2 - p1)*p); } throw new IllegalStateException("Numbers of type " + startNumber.getClass().getSimpleName() + " cannot be used."); } }