org.piccolo2d.activities.PInterpolatingActivity Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2019, Piccolo2D project, http://piccolo2d.org
* Copyright (c) 1998-2008, University of Maryland
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions
* and the following disclaimer in the documentation and/or other materials provided with the
* distribution.
*
* None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
* contributors may be used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.piccolo2d.activities;
import org.piccolo2d.util.PUtil;
/**
* PInterpolatingActivity interpolates between two states (source and
* destination) over the duration of the activity. The interpolation can be
* either linear or slow- in, slow-out.
*
* The mode determines how the activity interpolates between the two states. The
* default mode interpolates from source to destination, but you can also go
* from destination to source, and from source to destination to source.
*
* A loopCount of greater then one will make the activity reschedule itself when
* it has finished. This makes the activity loop between the two states.
*
*
* @version 1.0
* @author Jesse Grosjean
*/
public class PInterpolatingActivity extends PActivity {
/**
* Specifies that interpolation will be from the source value to the
* destination value.
*/
public static final int SOURCE_TO_DESTINATION = 1;
/**
* Specifies that interpolation will be from the destination value to the
* source value.
*/
public static final int DESTINATION_TO_SOURCE = 2;
/**
* Specifies that interpolation proceed from the source to the destination
* then back to the source. Can be used to perform flashes. source value.
*/
public static final int SOURCE_TO_DESTINATION_TO_SOURCE = 3;
private int mode;
private boolean slowInSlowOut;
private int loopCount;
private boolean firstLoop;
/**
* Constructs an interpolating activity that will last the duration given.
*
* @since 1.3
* @param duration duration in milliseconds of the entire activity
*/
public PInterpolatingActivity(final long duration) {
this(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION);
}
/**
* Constructs an interpolating activity that will last the duration given
* and will update its target at the given rate.
*
* @param duration duration in milliseconds of the entire activity
* @param stepRate interval in milliseconds between updates to target
*/
public PInterpolatingActivity(final long duration, final long stepRate) {
this(duration, stepRate, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION);
}
/**
* Constructs an interpolating activity that will last the duration given
* and will update its target at the given rate. Once done, it will repeat
* the loopCount times.
*
* @param duration duration in milliseconds of the entire activity
* @param stepRate interval in milliseconds between updates to target
* @param loopCount # of times to repeat this activity.
* @param mode controls the direction of the interpolation (source to
* destination, destination to source, or source to destination
* back to source)
*/
public PInterpolatingActivity(final long duration, final long stepRate, final int loopCount, final int mode) {
this(duration, stepRate, System.currentTimeMillis(), loopCount, mode);
}
/**
* Create a new PInterpolatingActivity.
*
*
* @param duration the length of one loop of the activity
* @param stepRate the amount of time between steps of the activity
* @param startTime the time (relative to System.currentTimeMillis()) that
* this activity should start. This value can be in the future.
* @param loopCount number of times the activity should reschedule itself
* @param mode defines how the activity interpolates between states
*/
public PInterpolatingActivity(final long duration, final long stepRate, final long startTime, final int loopCount,
final int mode) {
super(duration, stepRate, startTime);
this.loopCount = loopCount;
this.mode = mode;
slowInSlowOut = true;
firstLoop = true;
}
/**
* Set the amount of time that this activity should take to complete, after
* the startStepping method is called. The duration must be greater then
* zero so that the interpolation value can be computed.
*
* @param duration new duration of this activity
*/
public void setDuration(final long duration) {
if (duration <= 0) {
throw new IllegalArgumentException("Duration for PInterpolatingActivity must be greater then 0");
}
super.setDuration(duration);
}
// ****************************************************************
// Basics.
// ****************************************************************
/**
* Return the mode used for interpolation.
*
* Acceptable values are: SOURCE_TO_DESTINATION, DESTINATION_TO_SOURCE and
* SOURCE_TO_DESTINATION_TO_SOURCE
*
* @return current mode of this activity
*/
public int getMode() {
return mode;
}
/**
* Set the direction in which interpolation is going to occur.
*
* Acceptable values are: SOURCE_TO_DESTINATION, DESTINATION_TO_SOURCE and
* SOURCE_TO_DESTINATION_TO_SOURCE
*
* @param mode the new mode to use when interpolating
*/
public void setMode(final int mode) {
this.mode = mode;
}
/**
* Return the number of times the activity should automatically reschedule
* itself after it has finished.
*
* @return number of times to repeat this activity
*/
public int getLoopCount() {
return loopCount;
}
/**
* Set the number of times the activity should automatically reschedule
* itself after it has finished.
*
* @param loopCount number of times to repeat this activity
*/
public void setLoopCount(final int loopCount) {
this.loopCount = loopCount;
}
/**
* Return true if the activity is executing its first loop. Subclasses
* normally initialize their source state on the first loop.
*
* @return true if executing first loop
*/
public boolean getFirstLoop() {
return firstLoop;
}
/**
* Set if the activity is executing its first loop. Subclasses normally
* initialize their source state on the first loop. This method will rarely
* need to be called, unless your are reusing activities.
*
* @param firstLoop true if executing first loop
*/
public void setFirstLoop(final boolean firstLoop) {
this.firstLoop = firstLoop;
}
/**
* Returns whether this interpolation accelerates and then decelerates as it
* interpolates.
*
* @return true if accelerations are being applied apply
*/
public boolean getSlowInSlowOut() {
return slowInSlowOut;
}
/**
* Sets whether this interpolation accelerates and then decelerates as it
* interpolates.
*
* @param isSlowInSlowOut true if this interpolation inovolves some
* accelerations
*/
public void setSlowInSlowOut(final boolean isSlowInSlowOut) {
slowInSlowOut = isSlowInSlowOut;
}
// ****************************************************************
// Stepping - Instead of overriding the step methods subclasses
// of this activity will normally override setRelativeTargetValue().
// This method will be called for every step of the activity with
// a value ranging from 0,0 (for the first step) to 1.0 (for the
// final step). See PTransformActivity for an example.
// ****************************************************************
/**
* Called when activity is started. Makes sure target value is set properly
* for start of activity.
*/
protected void activityStarted() {
super.activityStarted();
setRelativeTargetValueAdjustingForMode(0);
}
/**
* Called at each step of the activity. Sets the current position taking
* mode into account.
*
* @param elapsedTime number of milliseconds since the activity began
*/
protected void activityStep(final long elapsedTime) {
super.activityStep(elapsedTime);
float t = elapsedTime / (float) getDuration();
t = Math.min(1, t);
t = Math.max(0, t);
if (getSlowInSlowOut()) {
t = computeSlowInSlowOut(t);
}
setRelativeTargetValueAdjustingForMode(t);
}
/**
* Called whenever the activity finishes. Reschedules it if the value of
* loopCount is > 0.
*/
protected void activityFinished() {
setRelativeTargetValueAdjustingForMode(1);
super.activityFinished();
final PActivityScheduler scheduler = getActivityScheduler();
if (loopCount > 1) {
if (loopCount != Integer.MAX_VALUE) {
loopCount--;
}
firstLoop = false;
setStartTime(scheduler.getRoot().getGlobalTime());
scheduler.addActivity(this);
}
}
/**
* Stop this activity immediately, and remove it from the activity
* scheduler. If this activity is currently running then stoppedStepping
* will be called after it has been removed from the activity scheduler.
*/
public void terminate() {
loopCount = 0; // set to zero so that we don't reschedule self.
super.terminate();
}
/**
* Subclasses should override this method and set the value on their target
* (the object that they are modifying) accordingly.
*
* @param zeroToOne relative completion of task.
*/
public void setRelativeTargetValue(final float zeroToOne) {
}
/**
* Computes percent or linear interpolation to apply when taking
* acceleration into account.
*
* @param zeroToOne Percentage of activity completed
* @return strength of acceleration
*/
public float computeSlowInSlowOut(final float zeroToOne) {
if (zeroToOne < 0.5f) {
return 2.0f * zeroToOne * zeroToOne;
}
else {
final float complement = 1.0f - zeroToOne;
return 1.0f - 2.0f * complement * complement;
}
}
/**
* Assigns relative target value taking the mode into account.
*
* @param zeroToOne Percentage of activity completed
*/
protected void setRelativeTargetValueAdjustingForMode(final float zeroToOne) {
final float adjustedZeroToOne;
switch (mode) {
case DESTINATION_TO_SOURCE:
adjustedZeroToOne = 1 - zeroToOne;
break;
case SOURCE_TO_DESTINATION_TO_SOURCE:
if (zeroToOne <= 0.5f) {
adjustedZeroToOne = zeroToOne * 2;
}
else {
adjustedZeroToOne = 2 * (1 - zeroToOne);
}
break;
case SOURCE_TO_DESTINATION:
default:
// Just treat the zeroToOne as how far along the interpolation
// we are.
adjustedZeroToOne = zeroToOne;
}
setRelativeTargetValue(adjustedZeroToOne);
}
}