spine-runtimes/spine-cpp/src/spine/CurveTimeline.cpp
Mario Zechner cc4f11fded [cpp] Simplify directory structure, add DebugPrinter program
- spine-cpp/spine-cpp was moved into spine-cpp directory, so spine-cpp/spine-cpp/src -> spine-cpp/src, spine-cpp/spine-cpp/include -> spine-cpp/include
- Added scaffold for DebugPrinter to be used by runtime comparison tests
- Added build.sh
2025-07-11 11:00:44 +02:00

268 lines
8.3 KiB
C++

/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#include <spine/CurveTimeline.h>
#include <spine/MathUtil.h>
using namespace spine;
RTTI_IMPL(CurveTimeline, Timeline)
CurveTimeline::CurveTimeline(size_t frameCount, size_t frameEntries, size_t bezierCount) : Timeline(frameCount,
frameEntries) {
_curves.setSize(frameCount + bezierCount * BEZIER_SIZE, 0);
_curves[frameCount - 1] = STEPPED;
}
CurveTimeline::~CurveTimeline() {
}
void CurveTimeline::setLinear(size_t frame) {
_curves[frame] = LINEAR;
}
void CurveTimeline::setStepped(size_t frame) {
_curves[frame] = STEPPED;
}
void CurveTimeline::setBezier(size_t bezier, size_t frame, float value, float time1, float value1, float cx1, float cy1,
float cx2, float cy2, float time2, float value2) {
size_t i = getFrameCount() + bezier * BEZIER_SIZE;
if (value == 0) _curves[frame] = BEZIER + i;
float tmpx = (time1 - cx1 * 2 + cx2) * 0.03, tmpy = (value1 - cy1 * 2 + cy2) * 0.03;
float dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006, dddy = ((cy1 - cy2) * 3 - value1 + value2) * 0.006;
float ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy;
float dx = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667, dy = (cy1 - value1) * 0.3 + tmpy + dddy * 0.16666667;
float x = time1 + dx, y = value1 + dy;
for (size_t n = i + BEZIER_SIZE; i < n; i += 2) {
_curves[i] = x;
_curves[i + 1] = y;
dx += ddx;
dy += ddy;
ddx += dddx;
ddy += dddy;
x += dx;
y += dy;
}
}
float CurveTimeline::getBezierValue(float time, size_t frameIndex, size_t valueOffset, size_t i) {
if (_curves[i] > time) {
float x = _frames[frameIndex], y = _frames[frameIndex + valueOffset];
return y + (time - x) / (_curves[i] - x) * (_curves[i + 1] - y);
}
size_t n = i + BEZIER_SIZE;
for (i += 2; i < n; i += 2) {
if (_curves[i] >= time) {
float x = _curves[i - 2], y = _curves[i - 1];
return y + (time - x) / (_curves[i] - x) * (_curves[i + 1] - y);
}
}
frameIndex += getFrameEntries();
float x = _curves[n - 2], y = _curves[n - 1];
return y + (time - x) / (_frames[frameIndex] - x) * (_frames[frameIndex + valueOffset] - y);
}
Array<float> &CurveTimeline::getCurves() {
return _curves;
}
RTTI_IMPL(CurveTimeline1, CurveTimeline)
CurveTimeline1::CurveTimeline1(size_t frameCount, size_t bezierCount) : CurveTimeline(frameCount,
CurveTimeline1::ENTRIES,
bezierCount) {
}
CurveTimeline1::~CurveTimeline1() {
}
void CurveTimeline1::setFrame(size_t frame, float time, float value) {
frame <<= 1;
_frames[frame] = time;
_frames[frame + CurveTimeline1::VALUE] = value;
}
float CurveTimeline1::getCurveValue(float time) {
int i = (int) _frames.size() - 2;
for (int ii = 2; ii <= i; ii += 2) {
if (_frames[ii] > time) {
i = ii - 2;
break;
}
}
int curveType = (int) _curves[i >> 1];
switch (curveType) {
case CurveTimeline::LINEAR: {
float before = _frames[i], value = _frames[i + CurveTimeline1::VALUE];
return value + (time - before) / (_frames[i + CurveTimeline1::ENTRIES] - before) *
(_frames[i + CurveTimeline1::ENTRIES + CurveTimeline1::VALUE] - value);
}
case CurveTimeline::STEPPED:
return _frames[i + CurveTimeline1::VALUE];
}
return getBezierValue(time, i, CurveTimeline1::VALUE, curveType - CurveTimeline1::BEZIER);
}
float CurveTimeline1::getRelativeValue(float time, float alpha, MixBlend blend, float current, float setup) {
if (time < _frames[0]) {
switch (blend) {
case MixBlend_Setup:
return setup;
case MixBlend_First:
return current + (setup - current) * alpha;
default:
return current;
}
}
float value = getCurveValue(time);
switch (blend) {
case MixBlend_Setup:
return setup + value * alpha;
case MixBlend_First:
case MixBlend_Replace:
return current + (value + setup - current) * alpha;
case MixBlend_Add:
return current + value * alpha;
}
return current;
}
float CurveTimeline1::getAbsoluteValue(float time, float alpha, MixBlend blend, float current, float setup) {
if (time < _frames[0]) {
switch (blend) {
case MixBlend_Setup:
return setup;
case MixBlend_First:
return current + (setup - current) * alpha;
default:
return current;
}
}
float value = getCurveValue(time);
switch (blend) {
case MixBlend_Setup:
return setup + (value - setup) * alpha;
case MixBlend_First:
case MixBlend_Replace:
return current + (value - current) * alpha;
case MixBlend_Add:
return current + value * alpha;
}
return current;
}
float CurveTimeline1::getAbsoluteValue(float time, float alpha, MixBlend blend, float current, float setup, float value) {
if (time < _frames[0]) {
switch (blend) {
case MixBlend_Setup:
return setup;
case MixBlend_First:
return current + (setup - current) * alpha;
default:
return current;
}
}
switch (blend) {
case MixBlend_Setup:
return setup + (value - setup) * alpha;
case MixBlend_First:
case MixBlend_Replace:
return current + (value - current) * alpha;
case MixBlend_Add:
return current + value * alpha;
}
return current;
}
float CurveTimeline1::getScaleValue(float time, float alpha, MixBlend blend, MixDirection direction, float current,
float setup) {
if (time < _frames[0]) {
switch (blend) {
case MixBlend_Setup:
return setup;
case MixBlend_First:
return current + (setup - current) * alpha;
default:
return current;
}
}
float value = getCurveValue(time) * setup;
if (alpha == 1) {
if (blend == MixBlend_Add) return current + value - setup;
return value;
}
// Mixing out uses sign of setup or current pose, else use sign of key.
if (direction == MixDirection_Out) {
switch (blend) {
case MixBlend_Setup:
return setup + (MathUtil::abs(value) * MathUtil::sign(setup) - setup) * alpha;
case MixBlend_First:
case MixBlend_Replace:
return current + (MathUtil::abs(value) * MathUtil::sign(current) - current) * alpha;
default:
break;
}
} else {
float s;
switch (blend) {
case MixBlend_Setup:
s = MathUtil::abs(setup) * MathUtil::sign(value);
return s + (value - s) * alpha;
case MixBlend_First:
case MixBlend_Replace:
s = MathUtil::abs(current) * MathUtil::sign(value);
return s + (value - s) * alpha;
default:
break;
}
}
return current + (value - setup) * alpha;
}
RTTI_IMPL(CurveTimeline2, CurveTimeline)
CurveTimeline2::CurveTimeline2(size_t frameCount, size_t bezierCount) : CurveTimeline(frameCount,
CurveTimeline2::ENTRIES,
bezierCount) {
}
CurveTimeline2::~CurveTimeline2() {
}
void CurveTimeline2::setFrame(size_t frame, float time, float value1, float value2) {
frame *= CurveTimeline2::ENTRIES;
_frames[frame] = time;
_frames[frame + CurveTimeline2::VALUE1] = value1;
_frames[frame + CurveTimeline2::VALUE2] = value2;
}