renderd7/external/tween-engine/src/Tween.cpp
2021-07-23 13:58:16 +00:00

796 lines
25 KiB
C++

//
// Tween.cpp
//
// This code is derived from Universal Tween Engine
// Licensed under Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0
//
/**
* Core class of the Tween Engine. A Tween is basically an interpolation
* between two values of an object attribute. However, the main interest of a
* Tween is that you can apply an easing formula on this interpolation, in
* order to smooth the transitions or to achieve cool effects like springs or
* bounces.
* <p/>
*
* The Universal Tween Engine is called "universal" because it is able to apply
* interpolations on every attribute from every possible object. Therefore,
* every object in your application can be animated with cool effects: it does
* not matter if your application is a game, a desktop interface or even a
* console program! If it makes sense to animate something, then it can be
* animated through this engine.
* <p/>
*
* This class contains many static factory methods to create and instantiate
* new interpolations easily. The common way to create a Tween is by using one
* of these factories:
* <p/>
*
* - Tween.to(...)<br/>
* - Tween.from(...)<br/>
* - Tween.set(...)<br/>
* - Tween.call(...)
* <p/>
*
* <h2>Example - firing a Tween</h2>
*
* The following example will move the target horizontal position from its
* current value to x=200 and y=300, during 500ms, but only after a delay of
* 1000ms. The animation will also be repeated 2 times (the starting position
* is registered at the end of the delay, so the animation will automatically
* restart from this registered position).
* <p/>
*
* <pre> {@code
* Tween.to(myObject, POSITION_XY, 0.5f)
* .target(200, 300)
* .ease(Quad.INOUT)
* .delay(1.0f)
* .repeat(2, 0.2f)
* .start(myManager);
* }</pre>
*
* Tween life-cycles can be automatically managed for you, thanks to the
* {@link TweenManager} class. If you choose to manage your tween when you start
* it, then you don't need to care about it anymore. <b>Tweens are
* <i>fire-and-forget</i>: don't think about them anymore once you started
* them (if they are managed of course).</b>
* <p/>
*
* You need to periodicaly update the tween engine, in order to compute the new
* values. If your tweens are managed, only update the manager; else you need
* to call {@link #update()} on your tweens periodically.
* <p/>
*
* <h2>Example - setting up the engine</h2>
*
* The engine cannot directly change your objects attributes, since it doesn't
* know them. Therefore, you need to tell him how to get and set the different
* attributes of your objects: <b>you need to implement the {@link
* TweenAccessor} interface for each object class you will animate</b>. Once
* done, don't forget to register these implementations, using the static method
* {@link registerAccessor()}, when you start your application.
*
* @see TweenAccessor
* @see TweenManager
* @see TweenEquation
* @see Timeline
* @author Aurelien Ribon | http://www.aurelienribon.com/
*/
//#define NDEBUG
#include <TweenEngine/Tween.h>
#include <TweenEngine/TweenPool.h>
namespace TweenEngine
{
int Tween::combinedAttrsLimit = 3;
int Tween::waypointsLimit = 0;
/**
* Changes the limit for combined attributes. Defaults to 3 to reduce
* memory footprint.
*/
void Tween::setCombinedAttributesLimit(int limit) { Tween::combinedAttrsLimit = limit; }
/**
* Changes the limit of allowed waypoints for each tween. Defaults to 0 to
* reduce memory footprint.
*/
void Tween::setWaypointsLimit(int limit) { Tween::waypointsLimit = limit; }
/**
* Gets the version number of the library.
*/
const char *Tween::getVersion() { return "6.3.3"; }
/**
* Used for debug purpose. Gets the current number of objects that are
* waiting in the Tween pool.
*/
int Tween::getPoolSize() { return pool.size(); }
/**
* Increases the minimum capacity of the pool. Capacity defaults to 20.
*/
void Tween::ensurePoolCapacity(int minCapacity) { pool.ensureCapacity(minCapacity); }
TweenPool &Tween::pool = *(new TweenPool());
// -------------------------------------------------------------------------
// Static -- factories
// -------------------------------------------------------------------------
/**
* Factory creating a new standard interpolation. This is the most common
* type of interpolation. The starting values are retrieved automatically
* after the delay (if any).
* <br/><br/>
*
* <b>You need to set the target values of the interpolation by using one
* of the target() methods</b>. The interpolation will run from the
* starting values to these target values.
* <br/><br/>
*
* The common use of Tweens is "fire-and-forget": you do not need to care
* for tweens once you added them to a TweenManager, they will be updated
* automatically, and cleaned once finished. Common call:
* <br/><br/>
*
* <pre> {@code
* Tween.to(myObject, POSITION, 1.0f)
* .target(50, 70)
* .ease(Quad.INOUT)
* .start(myManager);
* }</pre>
*
* Several options such as delay, repetitions and callbacks can be added to
* the tween.
*
* @param target The target object of the interpolation.
* @param tweenType The desired type of interpolation.
* @param duration The duration of the interpolation, in milliseconds.
* @return The generated Tween.
*/
Tween &Tween::to(Tweenable& target, int tweenType, float duration)
{
Tween &tween = *(pool.get());
tween.setup(&target, tweenType, duration);
tween.ease(TweenEquations::easeInOutQuad);
tween.path(TweenPaths::catmullRom);
return tween;
}
/**
* Factory creating a new reversed interpolation. The ending values are
* retrieved automatically after the delay (if any).
* <br/><br/>
*
* <b>You need to set the starting values of the interpolation by using one
* of the target() methods</b>. The interpolation will run from the
* starting values to these target values.
* <br/><br/>
*
* The common use of Tweens is "fire-and-forget": you do not need to care
* for tweens once you added them to a TweenManager, they will be updated
* automatically, and cleaned once finished. Common call:
* <br/><br/>
*
* <pre> {@code
* Tween.from(myObject, POSITION, 1.0f)
* .target(0, 0)
* .ease(Quad.INOUT)
* .start(myManager);
* }</pre>
*
* Several options such as delay, repetitions and callbacks can be added to
* the tween.
*
* @param target The target object of the interpolation.
* @param tweenType The desired type of interpolation.
* @param duration The duration of the interpolation, in milliseconds.
* @return The generated Tween.
*/
Tween &Tween::from(Tweenable& target, int tweenType, float duration)
{
Tween &tween = *(pool.get());
tween.setup(&target, tweenType, duration);
tween.ease(TweenEquations::easeInOutQuad);
tween.path(TweenPaths::catmullRom);
tween.isFrom = true;
return tween;
}
/**
* Factory creating a new instantaneous interpolation (thus this is not
* really an interpolation).
* <br/><br/>
*
* <b>You need to set the target values of the interpolation by using one
* of the target() methods</b>. The interpolation will set the target
* attribute to these values after the delay (if any).
* <br/><br/>
*
* The common use of Tweens is "fire-and-forget": you do not need to care
* for tweens once you added them to a TweenManager, they will be updated
* automatically, and cleaned once finished. Common call:
* <br/><br/>
*
* <pre> {@code
* Tween.set(myObject, POSITION)
* .target(50, 70)
* .delay(1.0f)
* .start(myManager);
* }</pre>
*
* Several options such as delay, repetitions and callbacks can be added to
* the tween.
*
* @param target The target object of the interpolation.
* @param tweenType The desired type of interpolation.
* @return The generated Tween.
*/
Tween &Tween::set(Tweenable& target, int tweenType)
{
Tween &tween = *(pool.get());
tween.setup(&target, tweenType, 0);
tween.ease(TweenEquations::easeInOutQuad);
return tween;
}
/**
* Factory creating a new timer. The given callback will be triggered on
* each iteration start, after the delay.
* <br/><br/>
*
* The common use of Tweens is "fire-and-forget": you do not need to care
* for tweens once you added them to a TweenManager, they will be updated
* automatically, and cleaned once finished. Common call:
* <br/><br/>
*
* <pre> {@code
* Tween.call(myCallback)
* .delay(1.0f)
* .repeat(10, 1000)
* .start(myManager);
* }</pre>
*
* @param callback The callback that will be triggered on each iteration
* start.
* @return The generated Tween.
* @see TweenCallback
*/
Tween &Tween::call(TweenCallback &callback)
{
Tween &tween = *(pool.get());
tween.setup(nullptr, -1, 0);
tween.setCallback(&callback);
tween.setCallbackTriggers(TweenCallback::START);
return tween;
}
/**
* Convenience method to create an empty tween. Such object is only useful
* when placed inside animation sequences (see {@link Timeline}), in which
* it may act as a beacon, so you can set a callback on it in order to
* trigger some action at the right moment.
*
* @return The generated Tween.
* @see Timeline
*/
Tween &Tween::mark()
{
Tween &tween = *(pool.get());
tween.setup(nullptr, -1, 0);
return tween;
}
// -------------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------------
Tween::Tween()
{
startValues = new float[combinedAttrsLimit];
targetValues = new float[combinedAttrsLimit];
waypoints = new float[waypointsLimit*combinedAttrsLimit];
accessorBuffer = new float[combinedAttrsLimit];
accessorBufferSize = combinedAttrsLimit;
pathBuffer = new float[(2+waypointsLimit)*combinedAttrsLimit];
pathBufferSize = (2+waypointsLimit)*combinedAttrsLimit;
targetObj = nullptr;
}
Tween::~Tween()
{
delete startValues;
delete targetValues;
delete waypoints;
delete accessorBuffer;
delete pathBuffer;
// if (accessor != nullptr) Block_release(accessor);
}
void Tween::reset()
{
BaseTween::reset();
equation = nullptr;
pathAlgorithm = nullptr;
isFrom = isRelative = false;
combinedAttrsCnt = waypointsCnt = 0;
if (accessorBufferSize != combinedAttrsLimit) {
accessorBuffer = new float[combinedAttrsLimit];
}
if (pathBufferSize != (2+waypointsLimit)*combinedAttrsLimit) {
pathBuffer = new float[(2+waypointsLimit)*combinedAttrsLimit];
}
// if (accessor != nullptr)
// {
// Block_release(accessor);
targetObj = nullptr;
// }
type = -1;
}
void Tween::setup(Tweenable *target, int tweenType, float duration)
{
// assert(duration >= 0);
this->targetObj = target;
this->type = tweenType;
this->duration = duration;
}
// -------------------------------------------------------------------------
// Public API
// -------------------------------------------------------------------------
/**
* Sets the easing equation of the tween. Existing equations are located in
* <i>aurelienribon.tweenengine.equations</i> package, but you can of course
* implement your owns, see {@link TweenEquation}. You can also use the
* {@link TweenEquations} static instances to quickly access all the
* equations. Default equation is Quad.INOUT.
* <p/>
*
* <b>Proposed equations are:</b><br/>
* - Linear.INOUT,<br/>
* - Quad.IN | OUT | INOUT,<br/>
* - Cubic.IN | OUT | INOUT,<br/>
* - Quart.IN | OUT | INOUT,<br/>
* - Quint.IN | OUT | INOUT,<br/>
* - Circ.IN | OUT | INOUT,<br/>
* - Sine.IN | OUT | INOUT,<br/>
* - Expo.IN | OUT | INOUT,<br/>
* - Back.IN | OUT | INOUT,<br/>
* - Bounce.IN | OUT | INOUT,<br/>
* - Elastic.IN | OUT | INOUT
*
* @return The current tween, for chaining instructions.
* @see TweenEquation
* @see TweenEquations
*/
Tween &Tween::ease(TweenEquation &easeEquation)
{
this->equation = &easeEquation;
return *this;
}
/**
* Sets the target value of the interpolation. The interpolation will run
* from the <b>value at start time (after the delay, if any)</b> to this
* target value.
* <p/>
*
* To sum-up:<br/>
* - start value: value at start time, after delay<br/>
* - end value: param
*
* @param targetValue The target value of the interpolation.
* @return The current tween, for chaining instructions.
*/
Tween &Tween::target(float targetValue)
{
targetValues[0] = targetValue;
return *this;
}
/**
* Sets the target values of the interpolation. The interpolation will run
* from the <b>values at start time (after the delay, if any)</b> to these
* target values.
* <p/>
*
* To sum-up:<br/>
* - start values: values at start time, after delay<br/>
* - end values: params
*
* @param targetValue1 The 1st target value of the interpolation.
* @param targetValue2 The 2nd target value of the interpolation.
* @return The current tween, for chaining instructions.
*/
Tween &Tween::target(float targetValue1, float targetValue2)
{
targetValues[0] = targetValue1;
targetValues[1] = targetValue2;
return *this;
}
/**
* Sets the target values of the interpolation. The interpolation will run
* from the <b>values at start time (after the delay, if any)</b> to these
* target values.
* <p/>
*
* To sum-up:<br/>
* - start values: values at start time, after delay<br/>
* - end values: params
*
* @param targetValue1 The 1st target value of the interpolation.
* @param targetValue2 The 2nd target value of the interpolation.
* @param targetValue3 The 3rd target value of the interpolation.
* @return The current tween, for chaining instructions.
*/
Tween &Tween::target(float targetValue1, float targetValue2, float targetValue3)
{
targetValues[0] = targetValue1;
targetValues[1] = targetValue2;
targetValues[2] = targetValue3;
return *this;
}
Tween &Tween::target(float *targetValues, int len)
{
if (len <= combinedAttrsLimit)
{
for (int i=0; i<len; i++)
this->targetValues[i] = targetValues[i];
}
return *this;
}
/**
* Sets the target value of the interpolation, relatively to the <b>value
* at start time (after the delay, if any)</b>.
* <p/>
*
* To sum-up:<br/>
* - start value: value at start time, after delay<br/>
* - end value: param + value at start time, after delay
*
* @param targetValue The relative target value of the interpolation.
* @return The current tween, for chaining instructions.
*/
Tween &Tween::targetRelative(float targetValue)
{
isRelative = true;
targetValues[0] = isInitialized() ? targetValue + startValues[0] : targetValue;
return *this;
}
/**
* Sets the target values of the interpolation, relatively to the <b>values
* at start time (after the delay, if any)</b>.
* <p/>
*
* To sum-up:<br/>
* - start values: values at start time, after delay<br/>
* - end values: params + values at start time, after delay
*
* @param targetValue1 The 1st relative target value of the interpolation.
* @param targetValue2 The 2nd relative target value of the interpolation.
* @return The current tween, for chaining instructions.
*/
Tween &Tween::targetRelative(float targetValue1, float targetValue2)
{
isRelative = true;
targetValues[0] = isInitialized() ? targetValue1 + startValues[0] : targetValue1;
targetValues[1] = isInitialized() ? targetValue2 + startValues[1] : targetValue2;
return *this;
}
/**
* Sets the target values of the interpolation, relatively to the <b>values
* at start time (after the delay, if any)</b>.
* <p/>
*
* To sum-up:<br/>
* - start values: values at start time, after delay<br/>
* - end values: params + values at start time, after delay
*
* @param targetValue1 The 1st relative target value of the interpolation.
* @param targetValue2 The 2nd relative target value of the interpolation.
* @param targetValue3 The 3rd relative target value of the interpolation.
* @return The current tween, for chaining instructions.
*/
Tween &Tween::targetRelative(float targetValue1, float targetValue2, float targetValue3)
{
isRelative = true;
targetValues[0] = isInitialized() ? targetValue1 + startValues[0] : targetValue1;
targetValues[1] = isInitialized() ? targetValue2 + startValues[1] : targetValue2;
targetValues[2] = isInitialized() ? targetValue3 + startValues[2] : targetValue3;
return *this;
}
/**
* Sets the target values of the interpolation, relatively to the <b>values
* at start time (after the delay, if any)</b>.
* <p/>
*
* To sum-up:<br/>
* - start values: values at start time, after delay<br/>
* - end values: params + values at start time, after delay
*
* @param targetValues The relative target values of the interpolation.
* @return The current tween, for chaining instructions.
*/
Tween &Tween::targetRelative(float *targetValues, int len)
{
if (len <= combinedAttrsLimit)
{
for (int i=0; i<len; i++)
this->targetValues[i] = isInitialized() ? targetValues[i] + startValues[i] : targetValues[i];
}
isRelative = true;
return *this;
}
/**
* Adds a waypoint to the path. The default path runs from the start values
* to the end values linearly. If you add waypoints, the default path will
* use a smooth catmull-rom spline to navigate between the waypoints, but
* you can change this behavior by using the {@link #path(TweenPath)}
* method.
* <p/>
* Note that if you want waypoints relative to the start values, use one of
* the .targetRelative() methods to define your target.
*
* @param targetValue The target of this waypoint.
* @return The current tween, for chaining instructions.
*/
Tween &Tween::waypoint(float targetValue)
{
if (waypointsCnt < waypointsLimit)
{
waypoints[waypointsCnt] = targetValue;
waypointsCnt += 1;
}
return *this;
}
/**
* Adds a waypoint to the path. The default path runs from the start values
* to the end values linearly. If you add waypoints, the default path will
* use a smooth catmull-rom spline to navigate between the waypoints, but
* you can change this behavior by using the {@link #path(TweenPath)}
* method.
* <p/>
* Note that if you want waypoints relative to the start values, use one of
* the .targetRelative() methods to define your target.
*
* @param targetValue1 The 1st target of this waypoint.
* @param targetValue2 The 2nd target of this waypoint.
* @return The current tween, for chaining instructions.
*/
Tween &Tween::waypoint(float targetValue1, float targetValue2)
{
if (waypointsCnt < waypointsLimit)
{
waypoints[waypointsCnt*2] = targetValue1;
waypoints[waypointsCnt*2+1] = targetValue2;
waypointsCnt += 1;
}
return *this;
}
/**
* Adds a waypoint to the path. The default path runs from the start values
* to the end values linearly. If you add waypoints, the default path will
* use a smooth catmull-rom spline to navigate between the waypoints, but
* you can change this behavior by using the {@link #path(TweenPath)}
* method.
* <p/>
* Note that if you want waypoints relative to the start values, use one of
* the .targetRelative() methods to define your target.
*
* @param targetValue1 The 1st target of this waypoint.
* @param targetValue2 The 2nd target of this waypoint.
* @param targetValue3 The 3rd target of this waypoint.
* @return The current tween, for chaining instructions.
*/
Tween &Tween::waypoint(float targetValue1, float targetValue2, float targetValue3)
{
if (waypointsCnt < waypointsLimit)
{
waypoints[waypointsCnt*3] = targetValue1;
waypoints[waypointsCnt*3+1] = targetValue2;
waypoints[waypointsCnt*3+2] = targetValue3;
waypointsCnt += 1;
}
return *this;
}
/**
* Adds a waypoint to the path. The default path runs from the start values
* to the end values linearly. If you add waypoints, the default path will
* use a smooth catmull-rom spline to navigate between the waypoints, but
* you can change this behavior by using the {@link #path(TweenPath)}
* method.
* <p/>
* Note that if you want waypoints relative to the start values, use one of
* the .targetRelative() methods to define your target.
*
* @param targetValues The targets of this waypoint.
* @return The current tween, for chaining instructions.
*/
Tween &Tween::waypoint(float *targetValues, int len)
{
if (waypointsCnt < waypointsLimit)
{
for (int i=0; i<len; i++)
this->waypoints[waypointsCnt*len+i] = targetValues[i];
waypointsCnt += 1;
}
return *this;
}
/**
* Sets the algorithm that will be used to navigate through the waypoints,
* from the start values to the end values. Default is a catmull-rom spline,
* but you can find other paths in the {@link TweenPaths} class.
*
* @param path A TweenPath implementation.
* @return The current tween, for chaining instructions.
* @see TweenPath
* @see TweenPaths
*/
Tween &Tween::path(TweenPath &path)
{
this->pathAlgorithm = &path;
return *this;
}
// -------------------------------------------------------------------------
// Getters
// -------------------------------------------------------------------------
/**
* Gets the easing equation.
*/
TweenEquation *Tween::getEasing() { return equation; }
/**
* Gets the target values. The returned buffer is as long as the maximum
* allowed combined values. Therefore, you're surely not interested in all
* its content. Use {@link #getCombinedTweenCount()} to get the number of
* interesting slots.
*/
float *Tween::getTargetValues() { return targetValues; }
/**
* Gets the number of combined animations.
*/
int Tween::getCombinedAttributesCount() { return combinedAttrsCnt; }
// -------------------------------------------------------------------------
// Base Class
// -------------------------------------------------------------------------
Tween &Tween::build()
{
if (targetObj != nullptr) {
combinedAttrsCnt = targetObj->getValues(type, accessorBuffer);
}
// assert(combinedAttrsCnt <= combinedAttrsLimit);
return *this;
}
void Tween::free() { pool.free(this); }
void Tween::initializeOverride()
{
targetObj->getValues(type, startValues);
for (int i=0; i<combinedAttrsCnt; i++) {
targetValues[i] += isRelative ? startValues[i] : 0;
for (int ii=0; ii<waypointsCnt; ii++) {
waypoints[ii*combinedAttrsCnt+i] += isRelative ? startValues[i] : 0;
}
if (isFrom) {
float tmp = startValues[i];
startValues[i] = targetValues[i];
targetValues[i] = tmp;
}
}
}
void Tween::updateOverride(int step, int lastStep, bool isIterationStep, float delta)
{
if (equation == nullptr) return;
// Case iteration end has been reached
if (!isIterationStep && step > lastStep)
{
targetObj->setValues(type, isReverse(lastStep) ? startValues : targetValues);
return;
}
if (!isIterationStep && step < lastStep)
{
targetObj->setValues(type, isReverse(lastStep) ? targetValues : startValues);
return;
}
// Validation
// assert(isIterationStep);
// assert(getCurrentTime() >= 0);
// assert(getCurrentTime() <= duration);
// Case duration equals zero
if (duration < 0.00000000001f && delta > -0.00000000001f)
{
targetObj->setValues(type, isReverse(step) ? targetValues : startValues);
return;
}
if (duration < 0.00000000001f && delta < 0.00000000001f) {
targetObj->setValues(type, isReverse(step) ? startValues : targetValues);
return;
}
// Normal behavior
float time = isReverse(step) ? duration - getCurrentTime() : getCurrentTime();
float t = equation->compute(time/duration);
if (waypointsCnt == 0 || pathAlgorithm == nullptr)
{
for (int i=0; i<combinedAttrsCnt; i++)
{
accessorBuffer[i] = startValues[i] + t * (targetValues[i] - startValues[i]);
}
}
else
{
for (int i=0; i<combinedAttrsCnt; i++)
{
pathBuffer[0] = startValues[i];
pathBuffer[1+waypointsCnt] = targetValues[i];
for (int ii=0; ii<waypointsCnt; ii++)
{
pathBuffer[ii+1] = waypoints[ii*combinedAttrsCnt+i];
}
accessorBuffer[i] = pathAlgorithm->compute(t, pathBuffer, waypointsCnt+2);
}
}
targetObj->setValues(type, accessorBuffer);
}
void Tween::forceStartValues(){
targetObj->setValues(type, startValues);
}
void Tween::forceEndValues(){
targetObj->setValues(type, targetValues);
}
int Tween::getTweenCount() { return 1; }
int Tween::getTimelineCount() { return 0; }
}