@ -0,0 +1,390 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||
Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a |
||||
copy of this software and associated documentation files (the "Software"), |
||||
to deal in the Software without restriction, including without limitation |
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
and/or sell copies of the Software, and to permit persons to whom the |
||||
Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included |
||||
in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
DEALINGS IN THE SOFTWARE. |
||||
*/ |
||||
|
||||
/*
|
||||
Generator for plots in the Easing namespace. Run the executable and it'll |
||||
generate a shitload of files in CWD. Copy these to doc/snippets/, replacing |
||||
the previous, and regenerate the docs using dox2html5.py. |
||||
|
||||
Note that the SVGs are crafted for inline HTML, so they won't get |
||||
recognized by vector editors. Add the XML preamble |
||||
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||
|
||||
and the |
||||
|
||||
xmlns="http://www.w3.org/2000/svg" |
||||
|
||||
attribute to the <svg> element if you'd ever need that. |
||||
*/ |
||||
|
||||
#include <Corrade/Utility/Directory.h> |
||||
#include <Corrade/Utility/Format.h> |
||||
#include <Corrade/Utility/String.h> |
||||
|
||||
#include "Magnum/Magnum.h" |
||||
#include "Magnum/Math/Bezier.h" |
||||
#include "Magnum/Math/Color.h" |
||||
#include "Magnum/Math/Range.h" |
||||
#include "Magnum/Animation/Easing.h" |
||||
|
||||
using namespace Magnum; |
||||
using namespace Magnum::Math::Literals; |
||||
|
||||
namespace { |
||||
|
||||
constexpr Int Points = 96; |
||||
constexpr Int PointsOutside = 8; |
||||
constexpr Int EndMarkerSize = 12; |
||||
constexpr Int ExtraMargin = 32; |
||||
constexpr Vector2 Size{224, 128}; |
||||
constexpr Vector2 Border{16, 16}; |
||||
constexpr Int CircleRadius = 2; |
||||
|
||||
constexpr Int ThumbPoints = 64; |
||||
constexpr Int ThumbEndMarkerSize = 8; |
||||
constexpr Vector2 ThumbSize{128, 128}; |
||||
constexpr Vector2 ThumbBorder{0, 32}; |
||||
|
||||
void generateThumb(const std::string& file, Float(*function)(Float)) { |
||||
std::string out; |
||||
|
||||
Range2D viewBox{{}, ThumbSize}; |
||||
Range2D contentBox{ThumbBorder, ThumbSize - ThumbBorder}; |
||||
|
||||
Utility::formatInto(out, out.size(), R"(<svg class="m-image m-nopadb" style="width: {2}px; height: {3}px; margin-top: -16px; margin-bottom: -16px;" viewBox="{0} {1} {2} {3}"> |
||||
<path fill="none" stroke="#405363" stroke-width="1.0" d=" |
||||
M{4} {} L{} {} M{} {} L{} {} |
||||
M{} {} l{} {} M{} {} l{} {}"/> |
||||
<path fill="none" stroke="#dcdcdc" stroke-width="1.5" d=")", |
||||
viewBox.left(), viewBox.bottom(), viewBox.sizeX(), viewBox.sizeY(), |
||||
contentBox.left(), contentBox.bottom(), contentBox.right(), contentBox.bottom(), |
||||
contentBox.left(), contentBox.top(), contentBox.right(), contentBox.top(), |
||||
contentBox.left(), contentBox.top() - ThumbEndMarkerSize/2, 0, ThumbEndMarkerSize, |
||||
contentBox.right(), contentBox.bottom() - ThumbEndMarkerSize/2, 0, ThumbEndMarkerSize); |
||||
|
||||
for(Int i = 0; i != ThumbPoints; ++i) { |
||||
const Vector2 point = Math::lerp(ThumbBorder, ThumbSize - ThumbBorder, Vector2{i/Float(ThumbPoints - 1), function(i/Float(ThumbPoints - 1))}); |
||||
Utility::formatInto(out, out.size(), i ? " L{} {}" : "M{} {}", point.x(), ThumbSize.y() - point.y()); |
||||
} |
||||
|
||||
Utility::formatInto(out, out.size(), R"("/> |
||||
</svg> |
||||
)"); |
||||
|
||||
Utility::Directory::writeString("easings-" + file + "-thumb.svg", out); |
||||
} |
||||
|
||||
void generate(const std::string& file, Float(*function)(Float), std::initializer_list<Float(*)(Float)> related = {}, const CubicBezier2D& bezier = {}, const Color3& colorBefore = 0xcd3431_srgbf, const Color3 colorAfter = 0xcd3431_srgbf, bool extraMargin = false) { |
||||
std::string out; |
||||
|
||||
Vector2 size = Size; |
||||
Vector2 border = Border; |
||||
std::string extraStyle; |
||||
if(extraMargin) { |
||||
size.y() += 2*ExtraMargin; |
||||
border.y() += ExtraMargin; |
||||
extraStyle = " margin-top: -24px; margin-bottom: -8px;"; |
||||
} |
||||
|
||||
Range2D viewBox{{}, size}; |
||||
Range2D contentBox{border, size - border}; |
||||
|
||||
/* Background, extrapolated points */ |
||||
Utility::formatInto(out, out.size(), R"(<svg class="m-image" style="width: {2}px; height: {3}px;{4}" viewBox="{0} {1} {2} {3}"> |
||||
<path fill="none" stroke="#405363" stroke-width="1.0" d=" |
||||
M{5} {} L{} {} M{} {} L{} {} |
||||
M{} {} l{} {} M{} {} l{} {}"/> |
||||
<path fill="none" stroke="#{:.6x}" stroke-width="1.5" d=")", |
||||
viewBox.left(), viewBox.bottom(), viewBox.sizeX(), viewBox.sizeY(), |
||||
extraStyle, |
||||
contentBox.left(), contentBox.bottom(), contentBox.right(), contentBox.bottom(), |
||||
contentBox.left(), contentBox.top(), contentBox.right(), contentBox.top(), |
||||
contentBox.left(), contentBox.top() - EndMarkerSize/2, 0, EndMarkerSize, |
||||
contentBox.right(), contentBox.bottom() - EndMarkerSize/2, 0, EndMarkerSize, |
||||
colorBefore.toSrgbInt()); |
||||
|
||||
for(Int i = 1 - PointsOutside; i <= 0; ++i) { |
||||
const Vector2 point = Math::lerp(border, size - border, Vector2{i/Float(Points - 1), function(i/Float(Points - 1))}); |
||||
Utility::formatInto(out, out.size(), i != 1 - PointsOutside ? " L{} {}" : "M{} {}", point.x(), size.y() - point.y()); |
||||
} |
||||
|
||||
Utility::formatInto(out, out.size(), R"("/> |
||||
<path fill="none" stroke="#{:.6x}" stroke-width="1.5" d=")", |
||||
colorAfter.toSrgbInt()); |
||||
|
||||
for(Int i = Points - 1; i < Points + PointsOutside - 1; ++i) { |
||||
const Vector2 point = Math::lerp(border, size - border, Vector2{i/Float(Points - 1), function(i/Float(Points - 1))}); |
||||
Utility::formatInto(out, out.size(), i != Points - 1 ? " L{} {}" : "M{} {}", point.x(), size.y() - point.y()); |
||||
} |
||||
|
||||
Utility::formatInto(out, out.size(), R"("/> |
||||
)"); |
||||
|
||||
/* Related functions */ |
||||
auto print = [&](const Color3& color, Float strokeWidth, Float(*function)(Float)) { |
||||
Utility::formatInto(out, out.size(), R"(<path fill="none" stroke="#{:.6x}" stroke-width="{}" d=")", color.toSrgbInt(), strokeWidth); |
||||
|
||||
for(Int i = 0; i != Points; ++i) { |
||||
const Vector2 point = Math::lerp(border, size - border, Vector2{i/Float(Points - 1), function(i/Float(Points - 1))}); |
||||
Utility::formatInto(out, out.size(), i ? " L{} {}" : "M{} {}", point.x(), size.y() - point.y()); |
||||
} |
||||
|
||||
Utility::formatInto(out, out.size(), R"("/> |
||||
)"); |
||||
}; |
||||
|
||||
for(auto fn: related) print(0x747474_srgbf, 1.0f, fn); |
||||
|
||||
/* Bezier representation, if any */ |
||||
if(!bezier[3].isZero()) { |
||||
CubicBezier2D transformed{bezier[0]*contentBox.size() + border, |
||||
bezier[1]*contentBox.size() + border, |
||||
bezier[2]*contentBox.size() + border, |
||||
bezier[3]*contentBox.size() + border}; |
||||
|
||||
/* Handle end is slightly cut to make the circle nice */ |
||||
const Vector2 end1 = transformed[1] - (transformed[1] - transformed[0]).resized(CircleRadius); |
||||
const Vector2 end2 = transformed[2] - (transformed[2] - transformed[3]).resized(CircleRadius); |
||||
|
||||
/* Handles, handle points and the curve, all smashed togetherrrr so
|
||||
we can reuse the data*/ |
||||
Utility::formatInto(out, out.size(), R"(<path fill="none" stroke="#2f83cc" stroke-width="1.0" d="M{8} {9} L{0} {1} C{2} {3}, {4} {5}, {6} {7} L{10} {11}"/> |
||||
<circle cx="{2}" cy="{3}" r="2" stroke="#2f83cc" stroke-width="1.0" fill="none" /> |
||||
<circle cx="{4}" cy="{5}" r="2" stroke="#2f83cc" stroke-width="1.0" fill="none" /> |
||||
)", |
||||
transformed[0][0], size.y() - transformed[0][1], |
||||
transformed[1][0], size.y() - transformed[1][1], |
||||
transformed[2][0], size.y() - transformed[2][1], |
||||
transformed[3][0], size.y() - transformed[3][1], |
||||
end1.x(), size.y() - end1.y(), |
||||
end2.x(), size.y() - end2.y()); |
||||
} |
||||
|
||||
print(0xdcdcdc_srgbf, 1.75f, function); |
||||
|
||||
Utility::formatInto(out, out.size(), R"(</svg> |
||||
)"); |
||||
|
||||
Utility::Directory::writeString("easings-" + file + ".svg", out); |
||||
|
||||
generateThumb(file, function); |
||||
} |
||||
|
||||
const Color3 danger = 0xcd3431_srgbf; |
||||
const Color3 success = 0x3bd267_srgbf; |
||||
|
||||
} |
||||
|
||||
/** @todo better bezier approximations for more complex curves, easings.net has it awful */ |
||||
|
||||
int main() { |
||||
using namespace Animation::Easing; |
||||
|
||||
#define _c(name) Utility::String::lowercase(#name), name |
||||
generate(_c(linear), {}, |
||||
/* [linear] */ |
||||
CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f}, |
||||
Vector2{2.0f/3.0f}, Vector2{1.0f}} |
||||
/* [linear] */ |
||||
, success, success); |
||||
generate(_c(step), {}, {}, success, success); |
||||
generate(_c(smoothstep), {smootherstep}, |
||||
/* [smoothstep] */ |
||||
CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f, 0.0f}, |
||||
Vector2{2.0f/3.0f, 1.0f}, Vector2{1.0f}} |
||||
/* [smoothstep] */ |
||||
, success, success); |
||||
generate(_c(smootherstep), {smoothstep}, {}, success, success); |
||||
generate(_c(quadraticIn), {cubicIn, quarticIn, quinticIn}, |
||||
/* [quadraticIn] */ |
||||
CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f, 0.0f}, |
||||
Vector2{2.0f/3.0f, 1.0f/3.0f}, Vector2{1.0f}} |
||||
/* [quadraticIn] */ |
||||
, danger, success); |
||||
generate(_c(quadraticOut), {cubicOut, quarticOut, quinticOut}, |
||||
/* [quadraticOut] */ |
||||
CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f, 2.0f/3.0f}, |
||||
Vector2{2.0f/3.0f, 1.0f}, Vector2{1.0f}} |
||||
/* [quadraticOut] */ |
||||
, success, danger); |
||||
generate(_c(quadraticInOut), {cubicInOut, quarticInOut, quinticInOut}, |
||||
/* [quadraticInOut] */ |
||||
CubicBezier2D{Vector2{0.0f}, Vector2{0.455f, 0.0f}, |
||||
Vector2{0.545f, 1.0f}, Vector2{1.0f}} |
||||
/* [quadraticInOut] */ |
||||
); |
||||
generate(_c(cubicIn), {quadraticIn, quarticIn, quinticIn}, |
||||
/* [cubicIn] */ |
||||
CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f, 0.0f}, |
||||
Vector2{2.0f/3.0f, 0.0f}, Vector2{1.0f}} |
||||
/* [cubicIn] */ |
||||
, danger, success); |
||||
generate(_c(cubicOut), {quadraticOut, quarticOut, quinticOut}, |
||||
/* [cubicOut] */ |
||||
CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f, 1.0f}, |
||||
Vector2{2.0f/3.0f, 1.0f}, Vector2{1.0f}} |
||||
/* [cubicOut] */ |
||||
, success, danger); |
||||
generate(_c(cubicInOut), {quadraticInOut, quarticInOut, quinticInOut}, |
||||
/* [cubicInOut] */ |
||||
CubicBezier2D{Vector2{0.0f}, Vector2{0.645f, 0.0f}, |
||||
Vector2{0.355f, 1.0f}, Vector2{1.0f}} |
||||
/* [cubicInOut] */ |
||||
); |
||||
generate(_c(quarticIn), {quadraticIn, cubicIn, quinticIn}, {} |
||||
// ,
|
||||
// /* [quarticIn] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.895f, 0.03f},
|
||||
// Vector2{0.685f, 0.22f}, Vector2{1.0f}}
|
||||
// /* [quarticIn] */
|
||||
, danger, success); |
||||
generate(_c(quarticOut), {quadraticOut, cubicOut, quinticOut}, {} |
||||
// ,
|
||||
// /* [quarticOut] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.165f, 0.84f},
|
||||
// Vector2{0.44f, 1.0f}, Vector2{1.0f}}
|
||||
// /* [quarticOut] */
|
||||
, success, danger); |
||||
generate(_c(quarticInOut), {quadraticInOut, cubicInOut, quinticInOut} |
||||
// ,
|
||||
// /* [quarticInOut] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.77f, 0.0f},
|
||||
// Vector2{0.175f, 1.0f}, Vector2{1.0f}}
|
||||
// /* [quarticInOut] */
|
||||
); |
||||
generate(_c(quinticIn), {quadraticIn, cubicIn, quarticIn}, {} |
||||
// ,
|
||||
// /* [quinticIn] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.755f, 0.05f},
|
||||
// Vector2{0.855f, 0.06f}, Vector2{1.0f}}
|
||||
// /* [quinticIn] */
|
||||
, danger, success); |
||||
generate(_c(quinticOut), {quadraticOut, cubicOut, quarticOut}, {} |
||||
// ,
|
||||
// /* [quinticOut] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.23f, 1.0f},
|
||||
// Vector2{0.32f, 1.0f}, Vector2{1.0f}}
|
||||
// /* [quinticOut] */
|
||||
, success, danger); |
||||
generate(_c(quinticInOut), {quadraticInOut, cubicInOut, quarticInOut} |
||||
// ,
|
||||
// /* [quinticInOut] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.86f, 0.0f},
|
||||
// Vector2{0.07f, 1.0f}, Vector2{1.0f}}
|
||||
// /* [quinticInOut] */
|
||||
); |
||||
generate(_c(sineIn), {circularIn} |
||||
// ,
|
||||
// /* [sineIn] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.47f, 0.0f},
|
||||
// Vector2{0.745f, 0.715f}, Vector2{1.0f}}
|
||||
// /* [sineIn] */
|
||||
); |
||||
generate(_c(sineOut), {circularOut} |
||||
// ,
|
||||
// /* [sineOut] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.39f, 0.575f},
|
||||
// Vector2{0.565f, 1.0f}, Vector2{1.0f}}
|
||||
// /* [sineOut] */
|
||||
); |
||||
generate(_c(sineInOut), {circularInOut} |
||||
// ,
|
||||
// /* [sineInOut] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.445f, 0.05f},
|
||||
// Vector2{0.55f, 0.95f}, Vector2{1.0f}}
|
||||
// /* [sineInOut] */
|
||||
); |
||||
generate(_c(circularIn), {sineIn} |
||||
// ,
|
||||
// /* [circularIn] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.6f, 0.04f},
|
||||
// Vector2{0.98f, 0.335f}, Vector2{1.0f}}
|
||||
// /* [circularIn] */
|
||||
); |
||||
generate(_c(circularOut), {sineOut} |
||||
// ,
|
||||
// /* [circularOut] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.075f, 0.085f},
|
||||
// Vector2{0.165f, 1.0f}, Vector2{1.0f}}
|
||||
// /* [circularOut] */
|
||||
); |
||||
generate(_c(circularInOut), {sineInOut} |
||||
// ,
|
||||
// /* [circularInOut] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.785f, 0.135f},
|
||||
// Vector2{0.15f, 0.86f}, Vector2{1.0f}}
|
||||
// /* [circularInOut] */
|
||||
); |
||||
generate(_c(exponentialIn), {}, {} |
||||
// ,
|
||||
// /* [exponentialIn] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.95f, 0.05f},
|
||||
// Vector2{0.795f, 0.035f}, Vector2{1.0f}}
|
||||
// /* [exponentialIn] */
|
||||
, success, success); |
||||
generate(_c(exponentialOut), {}, {} |
||||
// ,
|
||||
// /* [exponentialOut] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.19f, 1.0f},
|
||||
// Vector2{0.22f, 1.0f}, Vector2{1.0f}}
|
||||
// /* [exponentialOut] */
|
||||
, success, success); |
||||
generate(_c(exponentialInOut), {}, {} |
||||
// ,
|
||||
// /* [exponentialInOut] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{1.0f, 0.0f},
|
||||
// Vector2{0.0f, 1.0f}, Vector2{1.0f}}
|
||||
// /* [exponentialInOut] */
|
||||
, success, success); |
||||
generate(_c(elasticIn), {}, {}, success, danger, true); |
||||
generate(_c(elasticOut), {}, {}, danger, success, true); |
||||
generate(_c(elasticInOut), {}, {}, success, success, true); |
||||
generate(_c(backIn), {}, {} |
||||
// ,
|
||||
// /* [backIn] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.6f, -0.28f},
|
||||
// Vector2{0.735f, 0.045f}, Vector2{1.0f}}
|
||||
// /* [backIn] */
|
||||
, danger, danger, true); |
||||
generate(_c(backOut), {}, {} |
||||
// ,
|
||||
// /* [backOut] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.175f, 0.885f},
|
||||
// Vector2{0.32f, 1.275f}, Vector2{1.0f}}
|
||||
// /* [backOut] */
|
||||
, danger, danger, true); |
||||
generate(_c(backInOut), {}, {} |
||||
// ,
|
||||
// /* [backInOut] */
|
||||
// CubicBezier2D{Vector2{0.0f}, Vector2{0.68f, -0.55f},
|
||||
// Vector2{0.265f, 1.55f}, Vector2{1.0f}}
|
||||
// /* [backInOut] */
|
||||
, danger, danger, true); |
||||
generate(_c(bounceIn)); |
||||
generate(_c(bounceOut)); |
||||
generate(_c(bounceInOut)); |
||||
#undef _c |
||||
} |
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,947 @@
|
||||
#ifndef Magnum_Animation_Easing_h |
||||
#define Magnum_Animation_Easing_h |
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||
Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a |
||||
copy of this software and associated documentation files (the "Software"), |
||||
to deal in the Software without restriction, including without limitation |
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
and/or sell copies of the Software, and to permit persons to whom the |
||||
Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included |
||||
in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
DEALINGS IN THE SOFTWARE. |
||||
*/ |
||||
|
||||
/** @file
|
||||
* @brief Namespace @ref Magnum::Animation::Easing |
||||
*/ |
||||
|
||||
#include "Magnum/Magnum.h" |
||||
#include "Magnum/Math/Constants.h" |
||||
#include "Magnum/Animation/Animation.h" |
||||
|
||||
namespace Magnum { namespace Animation { |
||||
|
||||
/**
|
||||
@brief Easing functions |
||||
|
||||
A collection of predefined [easing / tweening](https://en.wikipedia.org/wiki/Inbetweening)
|
||||
functions for adding life to animation interpolation. |
||||
|
||||
@m_class{m-row m-container-inflate} |
||||
|
||||
@parblock |
||||
|
||||
@m_div{m-col-l-2 m-push-l-2 m-col-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-linear-thumb.svg |
||||
@ref linear() @m_class{m-label m-primary} **B** @m_class{m-label m-success} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-push-l-2 m-col-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-step-thumb.svg |
||||
@ref step() @m_class{m-label m-success} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-push-l-2 m-col-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-smoothstep-thumb.svg |
||||
@ref smoothstep() @m_class{m-label m-primary} **B** @m_class{m-label m-success} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-push-l-2 m-col-m-3 m-col-t-6 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-smootherstep-thumb.svg |
||||
@ref smootherstep() @m_class{m-label m-success} **E** |
||||
@m_enddiv |
||||
|
||||
@endparblock |
||||
|
||||
@m_class{m-row m-container-inflate} |
||||
|
||||
@parblock |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-quadraticin-thumb.svg |
||||
@ref quadraticIn() @m_class{m-label m-primary} **B** @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-quadraticout-thumb.svg |
||||
@ref quadraticOut() @m_class{m-label m-primary} **B** @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-quadraticinout-thumb.svg |
||||
@ref quadraticInOut() @m_class{m-label m-info} **B** @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-cubicin-thumb.svg |
||||
@ref cubicIn() @m_class{m-label m-primary} **B** @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-cubicout-thumb.svg |
||||
@ref cubicOut() @m_class{m-label m-primary} **B** @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-cubicinout-thumb.svg |
||||
@ref cubicInOut() @m_class{m-label m-info} **B** @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-quarticin-thumb.svg |
||||
@ref quarticIn() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-quarticout-thumb.svg |
||||
@ref quarticOut() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-quarticinout-thumb.svg |
||||
@ref quarticInOut() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-quinticin-thumb.svg |
||||
@ref quinticIn() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-quinticout-thumb.svg |
||||
@ref quinticOut() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-quinticinout-thumb.svg |
||||
@ref quinticInOut() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-sinein-thumb.svg |
||||
@ref sineIn() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-sineout-thumb.svg |
||||
@ref sineOut() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-sineinout-thumb.svg |
||||
@ref sineInOut() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-circularin-thumb.svg |
||||
@ref circularIn() |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-circularout-thumb.svg |
||||
@ref circularOut() |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-circularinout-thumb.svg |
||||
@ref circularInOut() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-exponentialin-thumb.svg |
||||
@ref exponentialIn() @m_class{m-label m-success} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-exponentialout-thumb.svg |
||||
@ref exponentialOut() @m_class{m-label m-success} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-exponentialinout-thumb.svg |
||||
@ref exponentialInOut() @m_class{m-label m-success} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-elasticin-thumb.svg |
||||
@ref elasticIn() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-elasticout-thumb.svg |
||||
@ref elasticOut() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-elasticinout-thumb.svg |
||||
@ref elasticInOut() @m_class{m-label m-success} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-backin-thumb.svg |
||||
@ref backIn() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-backout-thumb.svg |
||||
@ref backOut() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-backinout-thumb.svg |
||||
@ref backInOut() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-bouncein-thumb.svg |
||||
@ref bounceIn() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-bounceout-thumb.svg |
||||
@ref bounceOut() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@m_div{m-col-l-2 m-col-t-4 m-text-center m-nopadt m-nopadx} |
||||
@htmlinclude easings-bounceinout-thumb.svg |
||||
@ref bounceInOut() @m_class{m-label m-danger} **E** |
||||
@m_enddiv |
||||
|
||||
@endparblock |
||||
|
||||
The easing function is meant to be used to modify the interpolation factor, such as: |
||||
|
||||
@snippet MagnumAnimation.cpp Easing-factor |
||||
|
||||
The @ref Animation library also provides the @ref ease() utility that combines |
||||
the interpolator together with the easing function: |
||||
|
||||
@snippet MagnumAnimation.cpp Easing-ease |
||||
|
||||
@section Animation-Easing-equations Equations |
||||
|
||||
Every function documentation shows a plot of its behavior, together with a |
||||
direction in which it extrapolates. Green color means the extrapolation goes in |
||||
a reasonable monotonic direction outside of the range (also denoted by |
||||
@m_class{m-label m-success} **E** in the above list), red color means the |
||||
extrapolation is defined, but behaves in a probably unwanted or non-monotonic |
||||
way (denoted by @m_class{m-label m-danger} **E** in the above list). If neither |
||||
is present, it means the function is not defined outside of the @f$ [ 0; 1 ] @f$ |
||||
range and produces a NaN. You may want to ensure the factor stays in bounds, |
||||
using either @ref Math::clamp() or the @ref easeClamped() function --- the |
||||
following two expressions are equivalent: |
||||
|
||||
@snippet MagnumAnimation.cpp Easing-clamp |
||||
|
||||
Out-function @f$ f_\text{out} @f$ for a corresponding in-function @f$ f_\text{in} @f$ |
||||
is defined as the following, the equations in the docs usually just show the |
||||
final derived form. Similarly goes for combined in-/out-function |
||||
@f$ f_\text{inout} @f$: @f[ |
||||
\begin{array}{rcl} |
||||
f_\text{out}(x) & = & 1 - f_\text{in}(1 - x) \\[5pt] |
||||
f_\text{inout}(x) & = & \left. \begin{cases} |
||||
\frac{1}{2} f_\text{in}(2x), & x < 0.5 \\
|
||||
\frac{1}{2} (1 + f_\text{out}(2x - 1)), & x \ge 0.5 |
||||
\end{cases} \right\} = \begin{cases} |
||||
\frac{1}{2} f_\text{in}(2x), & x < 0.5 \\
|
||||
1 - \frac{1}{2} f_\text{in}(2 - 2x), & x \ge 0.5 |
||||
\end{cases} |
||||
\end{array} |
||||
@f] |
||||
|
||||
Easing functions defined by simple polynomials can have an *exact* (denoted |
||||
by @m_class{m-label m-primary} **B** in the above list) or approximate (denoted |
||||
by @m_class{m-label m-info} **B** in the above list) cubic @ref Math::Bezier |
||||
curve representation (and thus, in turn, convertible to Cubic Hermite splines |
||||
using @ref Math::CubicHermite::fromBezier()). If that's the case, given |
||||
function documentation also lists the corresponding Bézier representation and |
||||
plots it with a thin blue line. The curve is always normalized to go from |
||||
@f$ (0, 0)^T @f$ to @f$ (1, 1)^T @f$, apply arbitrary transformation to each |
||||
point as needed: |
||||
|
||||
@snippet MagnumAnimation.cpp Easing-bezier-transform |
||||
|
||||
@section Animation-Easing-references References |
||||
|
||||
Functions follow the common naming from Robert Penner's Easing functions, |
||||
http://robertpenner.com/easing/. Implementation based on and inspired by
|
||||
https://easings.net/,
|
||||
[https://github.com/warrenm/AHEasing](https://github.com/warrenm/AHEasing/blob/master/AHEasing/easing.c),
|
||||
[https://github.com/bkaradzic/bx](https://github.com/bkaradzic/bx/blob/master/include/bx/inline/easing.inl),
|
||||
https://blog.demofox.org/2014/08/28/one-dimensional-bezier-curves/.
|
||||
*/ |
||||
namespace Easing { |
||||
|
||||
/**
|
||||
@brief Linear |
||||
|
||||
@htmlinclude easings-linear.svg |
||||
|
||||
@f[ |
||||
y = x |
||||
@f] |
||||
|
||||
One possible *exact* Bézier representation: |
||||
|
||||
@snippet easings.cpp linear |
||||
|
||||
*/ |
||||
inline Float linear(Float t) { return t; } |
||||
|
||||
/**
|
||||
@brief Step |
||||
|
||||
Similar to @ref Math::select(), but does the step in the middle of the range |
||||
instead of at the end. Implementation matching the GLSL @glsl step() @ce |
||||
function with @cpp edge = 0.5f @ce. |
||||
|
||||
@htmlinclude easings-step.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
0, & x < 0.5 \\
|
||||
1, & x \ge 0.5 |
||||
\end{cases} |
||||
@f] |
||||
|
||||
@m_keyword{step(),GLSL step(),} |
||||
@see @ref smoothstep(), @ref smootherstep() |
||||
*/ |
||||
inline Float step(Float t) { return t < 0.5f ? 0.0f : 1.0f; } |
||||
|
||||
/**
|
||||
@brief [Smoothstep](https://en.wikipedia.org/wiki/Smoothstep).
|
||||
|
||||
Implementation matching the GLSL @glsl smoothstep() @ce function. Combine with |
||||
@ref Math::lerp() to get the equivalent result: |
||||
|
||||
@snippet MagnumAnimation.cpp Easing-smoothstep |
||||
|
||||
@htmlinclude easings-smoothstep.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
0, & x < 0 \\
|
||||
3x^2 - 2x^3, & x \in [0, 1] \\
|
||||
1, & x > 1 |
||||
\end{cases} |
||||
@f] |
||||
|
||||
* *Exact* Bézier representation: |
||||
|
||||
@snippet easings.cpp smoothstep |
||||
|
||||
@m_keyword{smoothstep(),GLSL smoothstep(),} |
||||
@see @ref smootherstep(), @ref Math::clamp() |
||||
*/ |
||||
inline Float smoothstep(Float t) { |
||||
/* Deliberately *not* using Math::clamp() because that would drag in
|
||||
unneeded vector headers */ |
||||
if(t <= 0.0f) return 0.0f; |
||||
if(t >= 1.0f) return 1.0f; |
||||
return (3.0f - 2.0f*t)*t*t; |
||||
} |
||||
|
||||
/**
|
||||
@brief [Smootherstep](https://en.wikipedia.org/wiki/Smoothstep#Variations).
|
||||
|
||||
Improved version of @ref smoothstep() by [Ken Perlin](https://en.wikipedia.org/wiki/Ken_Perlin).
|
||||
|
||||
@htmlinclude easings-smootherstep.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
0, & x < 0 \\
|
||||
6x^5 - 15x^4 + 10x^3, & x \in [0, 1] \\
|
||||
1, & x > 1 |
||||
\end{cases} |
||||
@f] |
||||
|
||||
@see @ref Math::clamp() |
||||
*/ |
||||
inline Float smootherstep(Float t) { |
||||
/* Deliberately *not* using Math::clamp() because that would drag in
|
||||
unneeded vector headers */ |
||||
if(t <= 0.0f) return 0.0f; |
||||
if(t >= 1.0f) return 1.0f; |
||||
return t*t*t*(t*(t* 6.0f - 15.0f) + 10.0f); |
||||
} |
||||
|
||||
/**
|
||||
@brief Quadratic in |
||||
|
||||
@htmlinclude easings-quadraticin.svg |
||||
|
||||
@f[ |
||||
y = x^2 |
||||
@f] |
||||
|
||||
* *Exact* Bézier representation: |
||||
|
||||
@snippet easings.cpp quadraticIn |
||||
|
||||
@see @ref cubicIn(), @ref quarticIn(), @ref quinticIn() |
||||
*/ |
||||
inline Float quadraticIn(Float t) { return t*t; } |
||||
|
||||
/**
|
||||
@brief Quadratic out |
||||
|
||||
@htmlinclude easings-quadraticout.svg |
||||
|
||||
@f[ |
||||
y = 1 - (1 - x)^2 = (2 - x) x |
||||
@f] |
||||
|
||||
* *Exact* Bézier representation: |
||||
|
||||
@snippet easings.cpp quadraticOut |
||||
|
||||
@see @ref cubicOut(), @ref quarticOut(), @ref quinticOut() |
||||
*/ |
||||
inline Float quadraticOut(Float t) { return -t*(t - 2.0f); } |
||||
|
||||
/**
|
||||
@brief Quadratic in and out |
||||
|
||||
Combination of @ref quadraticIn() and @ref quadraticOut(). |
||||
|
||||
@htmlinclude easings-quadraticinout.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
2 x^2, & x < 0.5 \\
|
||||
1 - 2 (1 - x)^2, & x \ge 0.5 |
||||
\end{cases} |
||||
@f] |
||||
|
||||
Approximate Bézier representation: |
||||
|
||||
@snippet easings.cpp quadraticInOut |
||||
|
||||
@see @ref cubicInOut(), @ref quarticInOut(), @ref quinticInOut() |
||||
*/ |
||||
inline Float quadraticInOut(Float t) { |
||||
if(t < 0.5f) return 2.0f*t*t; |
||||
|
||||
const Float inv = 1.0f - t; |
||||
return 1.0f - 2.0f*inv*inv; |
||||
} |
||||
|
||||
/**
|
||||
@brief Cubic in |
||||
|
||||
@htmlinclude easings-cubicin.svg |
||||
|
||||
@f[ |
||||
y = x^3 |
||||
@f] |
||||
|
||||
* *Exact* Bézier representation: |
||||
|
||||
@snippet easings.cpp cubicIn |
||||
|
||||
@see @ref quadraticIn(), @ref quarticIn(), @ref quinticIn() |
||||
*/ |
||||
inline Float cubicIn(Float t) { return t*t*t; } |
||||
|
||||
/**
|
||||
@brief Cubic out |
||||
|
||||
@htmlinclude easings-cubicout.svg |
||||
|
||||
@f[ |
||||
y = 1 - (1 - x)^3 |
||||
@f] |
||||
|
||||
* *Exact* Bézier representation: |
||||
|
||||
@snippet easings.cpp cubicOut |
||||
|
||||
@see @ref quadraticOut(), @ref quarticOut(), @ref quinticOut() |
||||
*/ |
||||
inline Float cubicOut(Float t) { |
||||
const Float inv = t - 1.0f; |
||||
return inv*inv*inv + 1.0f; |
||||
} |
||||
|
||||
/**
|
||||
@brief Cubic in and out |
||||
|
||||
Combination of @ref cubicIn() and @ref cubicOut(). |
||||
|
||||
@htmlinclude easings-cubicinout.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
4 x^3, & x < 0.5 \\
|
||||
1 - 4 (1 - x)^3, & x \ge 0.5 |
||||
\end{cases} |
||||
@f] |
||||
|
||||
Approximate Bézier representation: |
||||
|
||||
@snippet easings.cpp cubicInOut |
||||
|
||||
@see @ref quadraticInOut(), @ref quarticInOut(), @ref quinticInOut() |
||||
*/ |
||||
inline Float cubicInOut(Float t) { |
||||
if(t < 0.5f) return 4.0f*t*t*t; |
||||
|
||||
const Float inv = 1.0f - t; |
||||
return 1.0f - 4.0f*inv*inv*inv; |
||||
} |
||||
|
||||
/**
|
||||
@brief Quartic in |
||||
|
||||
@htmlinclude easings-quarticin.svg |
||||
|
||||
@f[ |
||||
y = x^4 |
||||
@f] |
||||
|
||||
@see @ref quadraticIn(), @ref cubicIn(), @ref quinticIn() |
||||
*/ |
||||
inline Float quarticIn(Float t) { |
||||
/* Not just t*t*t*t, since compile can't optimize it on its own to just two
|
||||
multiplication without breaking precision. So doing that explicitly. */ |
||||
const Float tt = t*t; |
||||
return tt*tt; |
||||
} |
||||
|
||||
/**
|
||||
@brief Quartic out |
||||
|
||||
@htmlinclude easings-quarticout.svg |
||||
|
||||
@f[ |
||||
y = 1 - (1 - x)^4 |
||||
@f] |
||||
|
||||
@see @ref quadraticOut(), @ref cubicOut(), @ref quinticOut() |
||||
*/ |
||||
inline Float quarticOut(Float t) { |
||||
/* Instead of t*t*t*t suggesting the optimization as described above */ |
||||
const Float inv = 1.0f - t; |
||||
const Float quad = inv*inv; |
||||
return 1.0f - quad*quad; |
||||
} |
||||
|
||||
/**
|
||||
@brief Quartic in and out |
||||
|
||||
Combination of @ref quarticIn() and @ref quarticOut(). |
||||
|
||||
@htmlinclude easings-quarticinout.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
8 x^4, & x < 0.5 \\
|
||||
1 - 8 (1 - x)^4, & x \ge 0.5 |
||||
\end{cases} |
||||
@f] |
||||
|
||||
@see @ref quadraticInOut(), @ref cubicInOut(), @ref quinticInOut() |
||||
*/ |
||||
inline Float quarticInOut(Float t) { |
||||
/* Instead of t*t*t*t suggesting the optimization as described above */ |
||||
|
||||
if(t < 0.5f) { |
||||
const Float tt = t*t; |
||||
return 8*tt*tt; |
||||
} |
||||
|
||||
const Float inv = 1.0f - t; |
||||
const Float quad = inv*inv; |
||||
return 1.0f - 8.0f*quad*quad; |
||||
} |
||||
|
||||
/**
|
||||
@brief Quintic in |
||||
|
||||
@htmlinclude easings-quinticin.svg |
||||
|
||||
@f[ |
||||
y = x^5 |
||||
@f] |
||||
|
||||
@see @ref quadraticIn(), @ref cubicIn(), @ref quarticIn() |
||||
*/ |
||||
inline Float quinticIn(Float t) { |
||||
/* Instead of t*t*t*t*t suggesting the optimization as described above */ |
||||
const Float tt = t*t; |
||||
return tt*t*tt; |
||||
} |
||||
|
||||
/**
|
||||
@brief Quintic out |
||||
|
||||
@htmlinclude easings-quinticout.svg |
||||
|
||||
@f[ |
||||
y = 1 - (1 - x)^5 |
||||
@f] |
||||
|
||||
@see @ref quadraticOut(), @ref cubicOut(), @ref quarticOut() |
||||
*/ |
||||
inline Float quinticOut(Float t) { |
||||
/* Instead of t*t*t*t*t suggesting the optimization as described above */ |
||||
const Float inv = t - 1.0f; |
||||
const Float quad = inv*inv; |
||||
return 1.0f + quad*inv*quad; |
||||
} |
||||
|
||||
/**
|
||||
@brief Quintic in and out |
||||
|
||||
Combination of @ref quinticIn() and @ref quinticOut(). |
||||
|
||||
@htmlinclude easings-quinticinout.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
16 x^5, & x < 0.5 \\
|
||||
1 - 16 (1 - x)^5, & x \ge 0.5 |
||||
\end{cases} |
||||
@f] |
||||
|
||||
@see @ref quadraticInOut(), @ref cubicInOut(), @ref quarticInOut() |
||||
*/ |
||||
inline Float quinticInOut(Float t) { |
||||
/* Instead of t*t*t*t*t suggesting the optimization as described above */ |
||||
|
||||
if(t < 0.5f) { |
||||
const Float tt = t*t; |
||||
return 16.0f*tt*t*tt; |
||||
} |
||||
|
||||
const Float inv = 1.0f - t; |
||||
const Float quad = inv*inv; |
||||
return 1.0f - 16.0f*quad*inv*quad; |
||||
} |
||||
|
||||
/**
|
||||
@brief Sine in |
||||
|
||||
@htmlinclude easings-sinein.svg |
||||
|
||||
@f[ |
||||
y = 1 + \sin(\frac{\pi}{2} (x - 1)) |
||||
@f] |
||||
|
||||
@see @ref circularIn() |
||||
*/ |
||||
inline Float sineIn(Float t) { |
||||
return 1.0f + std::sin(Constants::piHalf()*(t - 1.0f)); |
||||
} |
||||
|
||||
/**
|
||||
@brief Sine out |
||||
|
||||
@htmlinclude easings-sineout.svg |
||||
|
||||
@f[ |
||||
y = \sin(\frac{\pi}{2} x) |
||||
@f] |
||||
|
||||
@see @ref circularOut() |
||||
*/ |
||||
inline Float sineOut(Float t) { |
||||
return std::sin(Constants::piHalf()*t); |
||||
} |
||||
|
||||
/**
|
||||
@brief Sine in and out |
||||
|
||||
Combination of @ref sineIn() and @ref sineOut(). |
||||
|
||||
@htmlinclude easings-sineinout.svg |
||||
|
||||
@f[ |
||||
y = \frac{1}{2} (1 - \cos(\pi x)) |
||||
@f] |
||||
|
||||
@see @ref circularInOut() |
||||
*/ |
||||
inline Float sineInOut(Float t) { |
||||
return 0.5f*(1.0f - std::cos(t*Constants::pi())); |
||||
} |
||||
|
||||
/**
|
||||
@brief Circular in |
||||
|
||||
@htmlinclude easings-circularin.svg |
||||
|
||||
@f[ |
||||
y = 1 - \sqrt{1 - x^2} |
||||
@f] |
||||
|
||||
@see @ref sineIn() |
||||
*/ |
||||
inline Float circularIn(Float t) { |
||||
return 1.0f - std::sqrt(1.0f - t*t); |
||||
} |
||||
|
||||
/**
|
||||
@brief Circular out |
||||
|
||||
@htmlinclude easings-circularout.svg |
||||
|
||||
@f[ |
||||
y = \sqrt{(2 - x) x} |
||||
@f] |
||||
|
||||
@see @ref sineOut() |
||||
*/ |
||||
inline Float circularOut(Float t) { |
||||
return std::sqrt((2.0f - t)*t); |
||||
} |
||||
|
||||
/**
|
||||
@brief Circular in and out |
||||
|
||||
Combination of @ref circularIn() and @ref circularOut(). |
||||
|
||||
@htmlinclude easings-circularinout.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
\frac{1}{2} (1 - \sqrt{1 - (2x)^2}), & x < 0.5 \\
|
||||
\frac{1}{2} (1 + \sqrt{1 - (2x - 2)^2}), & x \ge 0.5 |
||||
\end{cases} |
||||
@f] |
||||
|
||||
@see @ref sineInOut() |
||||
*/ |
||||
inline Float circularInOut(Float t) { |
||||
if(t < 0.5f) return 0.5f*(1.0f - std::sqrt(1.0f - 4*t*t)); |
||||
return 0.5f*(1.0f + std::sqrt(-4.0f*t*t + 8.0f*t - 3.0f)); |
||||
} |
||||
|
||||
/**
|
||||
@brief Exponential in |
||||
|
||||
Contrary to Robert Penner's book but consistently with other implementations |
||||
has a special case for @f$ x \le 0 @f$, because @f$ 2^{-10} = 0.0009765625 @f$ |
||||
otherwise. |
||||
|
||||
@htmlinclude easings-exponentialin.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
0, & x \le 0 \\
|
||||
2^{10(x - 1)}, & x \ne 0 |
||||
\end{cases} |
||||
@f] |
||||
|
||||
@todo could be done better like with the sRGB curve, but should I bother? |
||||
*/ |
||||
inline Float exponentialIn(Float t) { |
||||
return t <= 0.0f ? 0.0f : std::pow(2.0f, 10.0f * (t - 1.0f)); |
||||
} |
||||
|
||||
/**
|
||||
@brief Exponential out |
||||
|
||||
Contrary to Robert Penner's book but consistently with other implementations |
||||
has a special case for @f$ x \ge 1 @f$, because @f$ 2^{-10} = 0.0009765625 @f$ |
||||
otherwise. |
||||
|
||||
@htmlinclude easings-exponentialout.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
2^{-10 x}, & x < 1 \\
|
||||
1, & x \ge 1 |
||||
\end{cases} |
||||
@f] |
||||
|
||||
@todo could be done better like with the sRGB curve, but should I bother? |
||||
*/ |
||||
inline Float exponentialOut(Float t) { |
||||
return t >= 1.0f ? 1.0f : 1.0f - std::pow(2.0f, -10.0f*t); |
||||
} |
||||
|
||||
/**
|
||||
@brief Exponential in and out |
||||
|
||||
Combination of @ref exponentialIn() and @ref exponentialOut(). Contrary to |
||||
Robert Penner's book but consistently with other implementations has a special |
||||
case for @f$ x \notin \{0, 1\} @f$, because @f$ 2^{-10} = 0.0009765625 @f$ |
||||
otherwise. |
||||
|
||||
@htmlinclude easings-exponentialinout.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
0, & x \le 0 \\
|
||||
\frac{1}{2} 2^{20 x - 10}, & x \in (0, 0.5) \\
|
||||
1 - \frac{1}{2} 2^{10 - 20 x}, & x \in [0.5, 1) \\
|
||||
1, & x \ge 1 |
||||
\end{cases} |
||||
@f] |
||||
*/ |
||||
inline Float exponentialInOut(Float t) { |
||||
if(t <= 0.0f) return 0.0f; |
||||
if(t < 0.5f) return 0.5f*std::pow(2.0f, 20.0f*t - 10.0f); |
||||
if(t < 1.0f) return 1.0f - 0.5f*std::pow(2.0f, 10.0f - 20.0f*t); |
||||
return 1.0f; |
||||
} |
||||
|
||||
/**
|
||||
@brief Elastic in |
||||
|
||||
Combines @ref sineIn() and @ref exponentialIn(). |
||||
|
||||
@htmlinclude easings-elasticin.svg |
||||
|
||||
@f[ |
||||
y = 2^{10 (p - 1)} \sin(13 \frac{\pi}{2} x) |
||||
@f] |
||||
*/ |
||||
inline Float elasticIn(Float t) { |
||||
return std::pow(2.0f, 10.0f*(t - 1.0f))*std::sin(13.0f*Constants::piHalf()*t); |
||||
} |
||||
|
||||
/**
|
||||
@brief Elastic out |
||||
|
||||
Combines @ref sineOut() and @ref exponentialOut(). |
||||
|
||||
@htmlinclude easings-elasticout.svg |
||||
|
||||
@f[ |
||||
y = 1 - 2^{-10 x} \sin(13 \frac{\pi}{2} (x + 1)) |
||||
@f] |
||||
*/ |
||||
inline Float elasticOut(Float t) { |
||||
return 1.0f - std::pow(2.0f, -10.0f*t)*std::sin(13.0f*Constants::piHalf()*(t + 1.0f)); |
||||
} |
||||
|
||||
/**
|
||||
@brief Elastic in and out |
||||
|
||||
Combination of @ref elasticIn() and @ref elasticOut() (or @ref sineInOut() and |
||||
@ref exponentialInOut()). |
||||
|
||||
@htmlinclude easings-elasticinout.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
\frac{1}{2} 2^{10 (2x - 1)} \sin(13 \pi x), & x < 0.5 \\
|
||||
1 - \frac{1}{2} 2^{10 (1 - 2x)} \sin(13 \pi x), & x \ge 0.5 |
||||
\end{cases} |
||||
@f] |
||||
*/ |
||||
inline Float elasticInOut(Float t) { |
||||
if(t < 0.5f) |
||||
return 0.5f*std::pow(2.0f, 10.0f*(2.0f*t - 1.0f))*std::sin(13.0f*Constants::pi()*t); |
||||
return 1.0f - 0.5f*std::pow(2.0f, 10.0f*(1.0f - 2.0f*t))*std::sin(13.0f*Constants::pi()*t); |
||||
} |
||||
|
||||
/**
|
||||
@brief Back in |
||||
|
||||
@htmlinclude easings-backin.svg |
||||
|
||||
@f[ |
||||
y = x^3 - x \sin(\pi x) |
||||
@f] |
||||
*/ |
||||
inline Float backIn(Float t) { |
||||
return t*(t*t - std::sin(Constants::pi()*t)); |
||||
} |
||||
|
||||
/**
|
||||
@brief Back out |
||||
|
||||
@htmlinclude easings-backout.svg |
||||
|
||||
@f[ |
||||
y = 1 - ((1 - x)^3 - (1 - x) \sin(\pi (1 - x))) |
||||
@f] |
||||
*/ |
||||
inline Float backOut(Float t) { |
||||
const Float inv = 1.0f - t; |
||||
return 1 - inv*(inv*inv - std::sin(Constants::pi()*inv)); |
||||
} |
||||
|
||||
/**
|
||||
@brief Back in and out |
||||
|
||||
Combination of @ref backIn() and @ref backOut(). |
||||
|
||||
@htmlinclude easings-backinout.svg |
||||
|
||||
@f[ |
||||
y = \begin{cases} |
||||
\frac{1}{2} ((2x)^3 - 2x \sin(2 \pi x)), & x < 0.5 \\
|
||||
1 - \frac{1}{2} ((2 - 2x)^3 - (2 - 2x) \sin(\pi (2 - 2x)), & x \ge 0.5 |
||||
\end{cases} |
||||
@f] |
||||
*/ |
||||
inline Float backInOut(Float t) { |
||||
if(t < 0.5f) { |
||||
const Float t2 = 2.0f*t; |
||||
return 0.5f*t2*(t2*t2 - std::sin(Constants::pi()*t2)); |
||||
} |
||||
|
||||
const Float inv = 2.0f - 2.0f*t; |
||||
return 1.0f - 0.5f*inv*(inv*inv - std::sin(Constants::pi()*inv)); |
||||
} |
||||
|
||||
#ifndef DOXYGEN_GENERATING_OUTPUT |
||||
Float bounceOut(Float); |
||||
#endif |
||||
|
||||
/**
|
||||
@brief Bounce in |
||||
|
||||
@htmlinclude easings-bouncein.svg |
||||
*/ |
||||
inline Float bounceIn(Float t) { |
||||
return 1.0f - bounceOut(1.0f - t); |
||||
} |
||||
|
||||
/**
|
||||
@brief Bounce out |
||||
|
||||
@htmlinclude easings-bounceout.svg |
||||
*/ |
||||
inline Float bounceOut(Float t) { |
||||
if(t < 4.0f/11.0f) return (121.0f*t*t)/16.0f; |
||||
if(t < 8.0f/11.0f) return 363.0f/40.0f*t*t - 99.0f/10.0f*t + 17.0f/5.0f; |
||||
if(t < 9.0f/10.0f) return 4356.0f/361.0f*t*t - 35442.0f/1805.0f*t + 16061.0f/1805.0f; |
||||
return 54.0f/5.0f*t*t - 513.0f/25.0f*t + 268.0f/25.0f; |
||||
} |
||||
|
||||
/**
|
||||
@brief Bounce in and out |
||||
|
||||
Combination of @ref bounceIn() and @ref bounceOut(). |
||||
|
||||
@htmlinclude easings-bounceinout.svg |
||||
*/ |
||||
inline Float bounceInOut(Float t) { |
||||
if(t < 0.5f) return 0.5f*bounceIn(2.0f*t); |
||||
return 0.5f*bounceOut(2.0f*t - 1.0f) + 0.5f; |
||||
} |
||||
|
||||
}}} |
||||
|
||||
#endif |
||||
@ -0,0 +1,289 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||
Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a |
||||
copy of this software and associated documentation files (the "Software"), |
||||
to deal in the Software without restriction, including without limitation |
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
and/or sell copies of the Software, and to permit persons to whom the |
||||
Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included |
||||
in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
DEALINGS IN THE SOFTWARE. |
||||
*/ |
||||
|
||||
#include <cstring> |
||||
#include <Corrade/Containers/ArrayView.h> |
||||
#include <Corrade/TestSuite/Tester.h> |
||||
#include <Corrade/TestSuite/Compare/Numeric.h> |
||||
#include <Corrade/Utility/Format.h> |
||||
|
||||
#include "Magnum/Animation/Easing.h" |
||||
|
||||
namespace Magnum { namespace Animation { namespace Test { |
||||
|
||||
struct EasingTest: TestSuite::Tester { |
||||
explicit EasingTest(); |
||||
|
||||
void bounds(); |
||||
void monotonicity(); |
||||
void symmetry(); |
||||
void values(); |
||||
|
||||
void benchmark(); |
||||
}; |
||||
|
||||
namespace { |
||||
|
||||
#define _c(name) #name, Easing::name |
||||
constexpr struct { |
||||
const char* name; |
||||
Float(*function)(Float); |
||||
} BoundsData[] { |
||||
{_c(linear)}, |
||||
{_c(step)}, |
||||
{_c(smoothstep)}, |
||||
{_c(smootherstep)}, |
||||
{_c(quadraticIn)}, |
||||
{_c(quadraticOut)}, |
||||
{_c(quadraticInOut)}, |
||||
{_c(cubicIn)}, |
||||
{_c(cubicOut)}, |
||||
{_c(cubicInOut)}, |
||||
{_c(quarticIn)}, |
||||
{_c(quarticOut)}, |
||||
{_c(quarticInOut)}, |
||||
{_c(quinticIn)}, |
||||
{_c(quinticOut)}, |
||||
{_c(quinticInOut)}, |
||||
{_c(sineIn)}, |
||||
{_c(sineOut)}, |
||||
{_c(sineInOut)}, |
||||
{_c(circularIn)}, |
||||
{_c(circularOut)}, |
||||
{_c(circularInOut)}, |
||||
{_c(exponentialIn)}, |
||||
{_c(exponentialOut)}, |
||||
{_c(exponentialInOut)}, |
||||
/* elastic and back are out of [0, 1] bounds */ |
||||
{_c(bounceIn)}, |
||||
{_c(bounceOut)}, |
||||
{_c(bounceInOut)} |
||||
}; |
||||
|
||||
constexpr struct { |
||||
const char* name; |
||||
Float(*function)(Float); |
||||
} MonotonicityData[] { |
||||
{_c(linear)}, |
||||
{_c(step)}, |
||||
{_c(smoothstep)}, |
||||
{_c(smootherstep)}, |
||||
{_c(quadraticIn)}, |
||||
{_c(quadraticOut)}, |
||||
{_c(quadraticInOut)}, |
||||
{_c(cubicIn)}, |
||||
{_c(cubicOut)}, |
||||
{_c(cubicInOut)}, |
||||
{_c(quarticIn)}, |
||||
{_c(quarticOut)}, |
||||
{_c(quarticInOut)}, |
||||
{_c(quinticIn)}, |
||||
{_c(quinticOut)}, |
||||
{_c(quinticInOut)}, |
||||
{_c(sineIn)}, |
||||
{_c(sineOut)}, |
||||
{_c(sineInOut)}, |
||||
{_c(circularIn)}, |
||||
{_c(circularOut)}, |
||||
{_c(circularInOut)}, |
||||
{_c(exponentialIn)}, |
||||
{_c(exponentialOut)}, |
||||
{_c(exponentialInOut)} |
||||
/* elastic, back and bounce are not monotonic */ |
||||
}; |
||||
|
||||
constexpr struct { |
||||
const char* name; |
||||
Float(*function)(Float); |
||||
const char* symmetricName; |
||||
Float(*symmetric)(Float); |
||||
} SymmetryData[] { |
||||
{_c(linear), _c(linear)}, |
||||
{_c(step), _c(step)}, |
||||
{_c(smoothstep), _c(smoothstep)}, |
||||
{_c(smootherstep), _c(smootherstep)}, |
||||
{_c(quadraticIn), _c(quadraticOut)}, |
||||
{_c(quadraticInOut), _c(quadraticInOut)}, |
||||
{_c(cubicIn), _c(cubicOut)}, |
||||
{_c(cubicInOut), _c(cubicInOut)}, |
||||
{_c(quarticIn), _c(quarticOut)}, |
||||
{_c(quarticInOut), _c(quarticInOut)}, |
||||
{_c(quinticIn), _c(quinticOut)}, |
||||
{_c(quinticInOut), _c(quinticInOut)}, |
||||
{_c(sineIn), _c(sineOut)}, |
||||
{_c(sineInOut), _c(sineInOut)}, |
||||
{_c(circularIn), _c(circularOut)}, |
||||
{_c(circularInOut), _c(circularInOut)}, |
||||
{_c(exponentialIn), _c(exponentialOut)}, |
||||
{_c(exponentialInOut), _c(exponentialInOut)}, |
||||
{_c(elasticIn), _c(elasticOut)}, |
||||
{_c(elasticInOut), _c(elasticInOut)}, |
||||
{_c(backIn), _c(backOut)}, |
||||
{_c(backInOut), _c(backInOut)}, |
||||
{_c(bounceIn), _c(bounceOut)}, |
||||
{_c(bounceInOut), _c(bounceInOut)} |
||||
}; |
||||
|
||||
constexpr struct { |
||||
const char* name; |
||||
Float(*function)(Float); |
||||
Float values[3]; |
||||
} ValueData[] { |
||||
{_c(linear), {0.25f, 0.5f, 0.75f}}, |
||||
{_c(step), {0.0f, 1.0f, 1.0f}}, |
||||
{_c(smoothstep), {0.15625f, 0.5f, 0.84375f}}, |
||||
{_c(smootherstep), {0.103516f, 0.5f, 0.896484f}}, |
||||
{_c(quadraticIn), {0.0625f, 0.25f, 0.5625f}}, |
||||
{_c(quadraticOut), {0.4375f, 0.75f, 0.9375f}}, |
||||
{_c(quadraticInOut), {0.125f, 0.5f, 0.875f}}, |
||||
{_c(cubicIn), {0.015625f, 0.125f, 0.421875f}}, |
||||
{_c(cubicOut), {0.578125f, 0.875f, 0.984375f}}, |
||||
{_c(cubicInOut), {0.0625f, 0.5f, 0.9375f}}, |
||||
{_c(quarticIn), {0.00390625f, 0.0625f, 0.316406f}}, |
||||
{_c(quarticOut), {0.683594f, 0.9375f, 0.996094f}}, |
||||
{_c(quarticInOut), {0.03125f, 0.5f, 0.96875f}}, |
||||
{_c(quinticIn), {0.000976562f, 0.03125f, 0.237305f}}, |
||||
{_c(quinticOut), {0.762695f, 0.96875f, 0.999023f}}, |
||||
{_c(quinticInOut), {0.015625f, 0.5f, 0.984375f}}, |
||||
{_c(sineIn), {0.0761205f, 0.292893f, 0.617317f}}, |
||||
{_c(sineOut), {0.382683f, 0.707107f, 0.92388f}}, |
||||
{_c(sineInOut), {0.146447f, 0.5f, 0.853553f}}, |
||||
{_c(circularIn), {0.0317541f, 0.133975f, 0.338562f}}, |
||||
{_c(circularOut), {0.661438f, 0.866025f, 0.968246f}}, |
||||
{_c(circularInOut), {0.0669873f, 0.5f, 0.933013f}}, |
||||
{_c(exponentialIn), {0.00552427f, 0.03125f, 0.176777f}}, |
||||
{_c(exponentialOut), {0.823223f, 0.96875f, 0.994476f}}, |
||||
{_c(exponentialInOut), {0.015625f, 0.5f, 0.984375f}}, |
||||
{_c(elasticIn), {-0.00510376f, -0.0220971f, 0.0676494f}}, |
||||
{_c(elasticOut), {0.932351f, 1.022097f, 1.005104f}}, |
||||
{_c(elasticInOut), {-0.0110485f, 0.5f, 1.01105f}}, |
||||
{_c(backIn), {-0.161152f, -0.375f, -0.108455f}}, |
||||
{_c(backOut), {1.108455f, 1.375f, 1.161152f}}, |
||||
{_c(backInOut), {-0.1875f, 0.5f, 1.1875f}}, |
||||
{_c(bounceIn), {0.0411367f, 0.28125f, 0.527344f}}, |
||||
{_c(bounceOut), {0.472656f, 0.71875f, 0.958863f}}, |
||||
{_c(bounceInOut), {0.140625f, 0.5f, 0.859375f}} |
||||
}; |
||||
#undef _c |
||||
|
||||
} |
||||
|
||||
EasingTest::EasingTest() { |
||||
addInstancedTests({&EasingTest::bounds}, |
||||
Containers::arraySize(BoundsData)); |
||||
|
||||
addInstancedTests({&EasingTest::monotonicity}, |
||||
Containers::arraySize(MonotonicityData)); |
||||
|
||||
addInstancedTests({&EasingTest::symmetry}, |
||||
Containers::arraySize(SymmetryData)); |
||||
|
||||
addInstancedTests({&EasingTest::values}, |
||||
Containers::arraySize(ValueData)); |
||||
|
||||
addInstancedBenchmarks({&EasingTest::benchmark}, 100, |
||||
Containers::arraySize(ValueData)); |
||||
} |
||||
|
||||
namespace { |
||||
constexpr std::size_t PropertyVerificationStepCount = 50; |
||||
} |
||||
|
||||
void EasingTest::bounds() { |
||||
auto&& data = BoundsData[testCaseInstanceId()]; |
||||
setTestCaseDescription(data.name); |
||||
|
||||
Float scale = 1.0f/Float(PropertyVerificationStepCount - 1); |
||||
for(std::size_t i = 0; i != PropertyVerificationStepCount; ++i) { |
||||
Float t = i*scale; |
||||
CORRADE_COMPARE_AS(data.function(t), 0.0f, TestSuite::Compare::GreaterOrEqual); |
||||
CORRADE_COMPARE_AS(data.function(t), 1.0f, TestSuite::Compare::LessOrEqual); |
||||
} |
||||
} |
||||
|
||||
void EasingTest::monotonicity() { |
||||
auto&& data = MonotonicityData[testCaseInstanceId()]; |
||||
setTestCaseDescription(data.name); |
||||
|
||||
Float scale = 1.0f/Float(PropertyVerificationStepCount - 1); |
||||
Float prev = data.function(0); |
||||
for(std::size_t i = 1; i != PropertyVerificationStepCount; ++i) { |
||||
Float cur = data.function(i*scale); |
||||
CORRADE_COMPARE_AS(cur, prev, TestSuite::Compare::GreaterOrEqual); |
||||
prev = cur; |
||||
} |
||||
} |
||||
|
||||
void EasingTest::symmetry() { |
||||
auto&& data = SymmetryData[testCaseInstanceId()]; |
||||
if(std::strcmp(data.name, data.symmetricName) == 0) |
||||
setTestCaseDescription(data.name); |
||||
else |
||||
setTestCaseDescription(Utility::formatString("{} : {}", data.name, data.symmetricName)); |
||||
|
||||
/* Not testing the edges, as these are tested in values() anyway (and are
|
||||
problematic in functions that have explicit handling for them) */ |
||||
Float scale = 1.0f/Float(PropertyVerificationStepCount + 1); |
||||
std::size_t max = PropertyVerificationStepCount/(data.function == data.symmetric ? 2 : 1); |
||||
for(std::size_t i = 1; i != max; ++i) { |
||||
Float t = i*scale; |
||||
CORRADE_COMPARE(data.function(t), 1.0f - data.symmetric(1.0f - t)); |
||||
} |
||||
} |
||||
|
||||
void EasingTest::values() { |
||||
auto&& data = ValueData[testCaseInstanceId()]; |
||||
setTestCaseDescription(data.name); |
||||
|
||||
CORRADE_COMPARE(data.function(0.0f), 0.0f); |
||||
CORRADE_COMPARE(data.function(1.0f), 1.0f); |
||||
CORRADE_COMPARE(data.function(0.25f), data.values[0]); |
||||
CORRADE_COMPARE(data.function(0.50f), data.values[1]); |
||||
CORRADE_COMPARE(data.function(0.75f), data.values[2]); |
||||
} |
||||
|
||||
namespace { |
||||
constexpr Int BenchmarkStepCount = 5000; |
||||
} |
||||
|
||||
void EasingTest::benchmark() { |
||||
auto&& data = ValueData[testCaseInstanceId()]; |
||||
setTestCaseDescription(data.name); |
||||
|
||||
/* Skip edges because the cumulated number may exceed 1 (on asm.js
|
||||
Emscripten), producing NaN on some functions and failing the test */ |
||||
Float scale = 1.0f/Float(BenchmarkStepCount + 1); |
||||
Float result = 0.0f; |
||||
std::size_t i = 0; |
||||
CORRADE_BENCHMARK(BenchmarkStepCount) |
||||
result += data.function(++i*scale); |
||||
|
||||
/* backIn() has -340 */ |
||||
CORRADE_COMPARE_AS(result, -350.0f, TestSuite::Compare::Greater); |
||||
} |
||||
|
||||
}}} |
||||
|
||||
CORRADE_TEST_MAIN(Magnum::Animation::Test::EasingTest) |
||||