diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/intern/fcurve.c | 227 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_math_easing.h | 78 | ||||
-rw-r--r-- | source/blender/blenlib/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/blenlib/intern/math_easing.c | 312 | ||||
-rw-r--r-- | source/blender/editors/animation/keyframes_edit.c | 133 | ||||
-rw-r--r-- | source/blender/editors/include/ED_keyframes_edit.h | 1 | ||||
-rw-r--r-- | source/blender/editors/space_graph/graph_buttons.c | 21 | ||||
-rw-r--r-- | source/blender/editors/space_graph/graph_draw.c | 7 | ||||
-rw-r--r-- | source/blender/editors/space_graph/graph_edit.c | 66 | ||||
-rw-r--r-- | source/blender/editors/space_graph/graph_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/space_graph/graph_ops.c | 2 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_curve_types.h | 31 | ||||
-rw-r--r-- | source/blender/makesrna/RNA_enum_types.h | 1 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_curve.c | 22 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_fcurve.c | 32 |
15 files changed, 894 insertions, 42 deletions
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index c6b64a112c7..e496513131f 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -45,6 +45,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_math_easing.h" #include "BLI_utildefines.h" #include "BLF_translation.h" @@ -2080,49 +2081,199 @@ static float fcurve_eval_keyframes(FCurve *fcu, BezTriple *bezts, float evaltime } /* evaltime occurs within the interval defined by these two keyframes */ else if ((prevbezt->vec[1][0] <= evaltime) && (bezt->vec[1][0] >= evaltime)) { + const float begin = prevbezt->vec[1][1]; + const float change = bezt->vec[1][1] - prevbezt->vec[1][1]; + const float duration = bezt->vec[1][0] - prevbezt->vec[1][0]; + const float time = evaltime - prevbezt->vec[1][0]; + const float amplitude = prevbezt->amplitude; + const float period = prevbezt->period; + /* value depends on interpolation mode */ - if ((prevbezt->ipo == BEZT_IPO_CONST) || (fcu->flag & FCURVE_DISCRETE_VALUES)) { + if ((prevbezt->ipo == BEZT_IPO_CONST) || (fcu->flag & FCURVE_DISCRETE_VALUES) || (duration == 0)) { /* constant (evaltime not relevant, so no interpolation needed) */ cvalue = prevbezt->vec[1][1]; } - else if (prevbezt->ipo == BEZT_IPO_LIN) { - /* linear - interpolate between values of the two keyframes */ - fac = bezt->vec[1][0] - prevbezt->vec[1][0]; - - /* prevent division by zero */ - if (fac) { - fac = (evaltime - prevbezt->vec[1][0]) / fac; - cvalue = prevbezt->vec[1][1] + (fac * (bezt->vec[1][1] - prevbezt->vec[1][1])); - } - else { - cvalue = prevbezt->vec[1][1]; - } - } else { - /* bezier interpolation */ - /* (v1, v2) are the first keyframe and its 2nd handle */ - v1[0] = prevbezt->vec[1][0]; - v1[1] = prevbezt->vec[1][1]; - v2[0] = prevbezt->vec[2][0]; - v2[1] = prevbezt->vec[2][1]; - /* (v3, v4) are the last keyframe's 1st handle + the last keyframe */ - v3[0] = bezt->vec[0][0]; - v3[1] = bezt->vec[0][1]; - v4[0] = bezt->vec[1][0]; - v4[1] = bezt->vec[1][1]; - - /* adjust handles so that they don't overlap (forming a loop) */ - correct_bezpart(v1, v2, v3, v4); - - /* try to get a value for this position - if failure, try another set of points */ - b = findzero(evaltime, v1[0], v2[0], v3[0], v4[0], opl); - if (b) { - berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1); - cvalue = opl[0]; - /* break; */ - } - else { - if (G.debug & G_DEBUG) printf(" ERROR: findzero() failed at %f with %f %f %f %f\n", evaltime, v1[0], v2[0], v3[0], v4[0]); + switch (prevbezt->ipo) { + /* interpolation ...................................... */ + case BEZT_IPO_BEZ: + /* bezier interpolation */ + /* (v1, v2) are the first keyframe and its 2nd handle */ + v1[0] = prevbezt->vec[1][0]; + v1[1] = prevbezt->vec[1][1]; + v2[0] = prevbezt->vec[2][0]; + v2[1] = prevbezt->vec[2][1]; + /* (v3, v4) are the last keyframe's 1st handle + the last keyframe */ + v3[0] = bezt->vec[0][0]; + v3[1] = bezt->vec[0][1]; + v4[0] = bezt->vec[1][0]; + v4[1] = bezt->vec[1][1]; + + /* adjust handles so that they don't overlap (forming a loop) */ + correct_bezpart(v1, v2, v3, v4); + + /* try to get a value for this position - if failure, try another set of points */ + b = findzero(evaltime, v1[0], v2[0], v3[0], v4[0], opl); + if (b) { + berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1); + cvalue = opl[0]; + /* break; */ + } + else { + if (G.debug & G_DEBUG) printf(" ERROR: findzero() failed at %f with %f %f %f %f\n", evaltime, v1[0], v2[0], v3[0], v4[0]); + } + break; + + case BEZT_IPO_LIN: + /* linear - simply linearly interpolate between values of the two keyframes */ + cvalue = LinearEase(time, begin, change, duration); + break; + + /* easing ............................................ */ + case BEZT_IPO_BACK: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = BackEaseIn(time, begin, change, duration, prevbezt->back); + break; + case BEZT_IPO_EASE_OUT: + cvalue = BackEaseOut(time, begin, change, duration, prevbezt->back); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = BackEaseInOut(time, begin, change, duration, prevbezt->back); + break; + } + break; + + case BEZT_IPO_BOUNCE: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = BounceEaseIn(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = BounceEaseOut(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = BounceEaseInOut(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_CIRC: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = CircEaseIn(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = CircEaseOut(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = CircEaseInOut(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_CUBIC: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = CubicEaseIn(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = CubicEaseOut(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = CubicEaseInOut(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_ELASTIC: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = ElasticEaseIn(time, begin, change, duration, amplitude, period); + break; + case BEZT_IPO_EASE_OUT: + cvalue = ElasticEaseOut(time, begin, change, duration, amplitude, period); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = ElasticEaseInOut(time, begin, change, duration, amplitude, period); + break; + } + break; + + case BEZT_IPO_EXPO: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = ExpoEaseIn(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = ExpoEaseOut(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = ExpoEaseInOut(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_QUAD: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = QuadEaseIn(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = QuadEaseOut(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = QuadEaseInOut(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_QUART: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = QuartEaseIn(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = QuartEaseOut(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = QuartEaseInOut(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_QUINT: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = QuintEaseIn(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = QuintEaseOut(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = QuintEaseInOut(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_SINE: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = SineEaseIn(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = SineEaseOut(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = SineEaseInOut(time, begin, change, duration); + break; + } + break; + + + default: + cvalue = prevbezt->vec[1][1]; + break; } } } diff --git a/source/blender/blenlib/BLI_math_easing.h b/source/blender/blenlib/BLI_math_easing.h new file mode 100644 index 00000000000..c6518491bb3 --- /dev/null +++ b/source/blender/blenlib/BLI_math_easing.h @@ -0,0 +1,78 @@ +/* + * Copyright © 2001 Robert Penner + * 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. + * + * * Neither the name of the author nor the names of 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 <COPYRIGHT HOLDER> 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. + */ + +#ifndef __BLI_MATH_EASING_H__ +#define __BLI_MATH_EASING_H__ + +/** \file BLI_math_easing.h + * \ingroup bli + */ + +#ifdef __cplusplus +extern "C" { +#endif + +float BackEaseIn(float time, float begin, float change, float duration, float overshoot); +float BackEaseOut(float time, float begin, float change, float duration, float overshoot); +float BackEaseInOut(float time, float begin, float change, float duration, float overshoot); +float BounceEaseOut(float time, float begin, float change, float duration); +float BounceEaseIn(float time, float begin, float change, float duration); +float BounceEaseInOut(float time, float begin, float change, float duration); +float CircEaseIn(float time, float begin, float change, float duration); +float CircEaseOut(float time, float begin, float change, float duration); +float CircEaseInOut(float time, float begin, float change, float duration); +float CubicEaseIn(float time, float begin, float change, float duration); +float CubicEaseOut(float time, float begin, float change, float duration); +float CubicEaseInOut(float time, float begin, float change, float duration); +float ElasticEaseIn(float time, float begin, float change, float duration, float amplitude, float period); +float ElasticEaseOut(float time, float begin, float change, float duration, float amplitude, float period); +float ElasticEaseInOut(float time, float begin, float change, float duration, float amplitude, float period); +float ExpoEaseIn(float time, float begin, float change, float duration); +float ExpoEaseOut(float time, float begin, float change, float duration); +float ExpoEaseInOut(float time, float begin, float change, float duration); +float LinearEase(float time, float begin, float change, float duration); +float QuadEaseIn(float time, float begin, float change, float duration); +float QuadEaseOut(float time, float begin, float change, float duration); +float QuadEaseInOut(float time, float begin, float change, float duration); +float QuartEaseIn(float time, float begin, float change, float duration); +float QuartEaseOut(float time, float begin, float change, float duration); +float QuartEaseInOut(float time, float begin, float change, float duration); +float QuintEaseIn(float time, float begin, float change, float duration); +float QuintEaseOut(float time, float begin, float change, float duration); +float QuintEaseInOut(float time, float begin, float change, float duration); +float SineEaseIn(float time, float begin, float change, float duration); +float SineEaseOut(float time, float begin, float change, float duration); +float SineEaseInOut(float time, float begin, float change, float duration); + +#ifdef __cplusplus +} +#endif + +#endif // __BLI_MATH_EASING_H__ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 9194bb5a54c..24973cece91 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -72,6 +72,7 @@ set(SRC intern/math_color.c intern/math_color_blend_inline.c intern/math_color_inline.c + intern/math_easing.c intern/math_geom.c intern/math_geom_inline.c intern/math_interp.c @@ -141,6 +142,7 @@ set(SRC BLI_math_base.h BLI_math_color.h BLI_math_color_blend.h + BLI_math_easing.h BLI_math_geom.h BLI_math_inline.h BLI_math_interp.h diff --git a/source/blender/blenlib/intern/math_easing.c b/source/blender/blenlib/intern/math_easing.c new file mode 100644 index 00000000000..bec4e0b228a --- /dev/null +++ b/source/blender/blenlib/intern/math_easing.c @@ -0,0 +1,312 @@ +/* + * Copyright © 2001 Robert Penner + * 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. + * + * * Neither the name of the author nor the names of 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 <COPYRIGHT HOLDER> 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. + */ + +/** \file blender/blenlib/intern/math_easing.c + * \ingroup bli + */ + +#include <math.h> +#include <stdlib.h> + +#include "BLI_math_easing.h" + + +float BackEaseIn(float time, float begin, float change, float duration, float overshoot) +{ + if (overshoot == 0) + overshoot = 1.70158f; + time /= duration; + return change * time * time * ((overshoot + 1) * time - overshoot) + begin; +} + +float BackEaseOut(float time, float begin, float change, float duration, float overshoot) +{ + if (overshoot == 0) + overshoot = 1.70158f; + time = time / duration - 1; + return change * (time * time * ((overshoot + 1) * time + overshoot) + 1) + begin; +} + +float BackEaseInOut(float time, float begin, float change, float duration, float overshoot) +{ + if (overshoot == 0) + overshoot = 1.70158f; + overshoot *= 1.525f; + if ((time /= duration / 2) < 1) { + return change / 2 * (time * time * ((overshoot + 1) * time - overshoot)) + begin; + } + time -= 2; + return change / 2 * (time * time * ((overshoot + 1) * time + overshoot) + 2) + begin; + +} + +float BounceEaseOut(float time, float begin, float change, float duration) +{ + time /= duration; + if (time < (1 / 2.75f)) { + return change * (7.5625f * time * time) + begin; + } + else if (time < (2 / 2.75f)) { + time -= (1.5f / 2.75f); + return change * ((7.5625f * time) * time + 0.75f) + begin; + } + else if (time < (2.5f / 2.75f)) { + time -= (2.25f / 2.75f); + return change * ((7.5625f * time) * time + 0.9375f) + begin; + } + else { + time -= (2.625f / 2.75f); + return change * ((7.5625f * time) * time + .984375f) + begin; + } +} + +float BounceEaseIn(float time, float begin, float change, float duration) +{ + return change - BounceEaseOut(duration - time, 0, change, duration) + begin; +} + +float BounceEaseInOut(float time, float begin, float change, float duration) +{ + if (time < duration / 2) + return BounceEaseIn(time * 2, 0, change, duration) * 0.5f + begin; + else + return BounceEaseOut(time * 2 - duration, 0, change, duration) * 0.5f + change * 0.5f + begin; +} + +float CircEaseIn(float time, float begin, float change, float duration) +{ + time /= duration; + return -change * (sqrt(1 - time * time) - 1) + begin; +} + +float CircEaseOut(float time, float begin, float change, float duration) +{ + time = time / duration - 1; + return change * sqrt(1 - time * time) + begin; +} + +float CircEaseInOut(float time, float begin, float change, float duration) +{ + if ((time /= duration / 2) < 1) + return -change / 2 * (sqrt(1 - time * time) - 1) + begin; + time -= 2; + return change / 2 * (sqrt(1 - time * time) + 1) + begin; +} + +float CubicEaseIn(float time, float begin, float change, float duration) +{ + time /= duration; + return change * time * time * time + begin; +} + +float CubicEaseOut(float time, float begin, float change, float duration) +{ + time = time / duration - 1; + return change * (time * time * time + 1) + begin; +} + +float CubicEaseInOut(float time, float begin, float change, float duration) +{ + if ((time /= duration / 2) < 1) + return change / 2 * time * time * time + begin; + time -= 2; + return change / 2 * (time * time * time + 2) + begin; +} + +float ElasticEaseIn(float time, float begin, float change, float duration, float amplitude, float period) +{ + float s; + + if (time == 0) + return begin; + + if ((time /= duration) == 1) + return begin + change; + + if (!period) + period = duration * 0.3f; + + if (!amplitude || amplitude < abs(change)) { + amplitude = change; + s = period / 4; + } + else + s = period / (2 * M_PI) * asin(change / amplitude); + + time -= 1; + return -(amplitude * pow(2, 10 * time) * sin((time * duration - s) * (2 * M_PI) / period)) + begin; +} + +float ElasticEaseOut(float time, float begin, float change, float duration, float amplitude, float period) +{ + float s; + + if (time == 0) + return begin; + if ((time /= duration) == 1) + return begin + change; + if (!period) + period = duration * 0.3f; + if (!amplitude || amplitude < abs(change)) { + amplitude = change; + s = period / 4; + } + else + s = period / (2 * M_PI) * asin(change / amplitude); + + return (amplitude * pow(2, -10 * time) * sin((time * duration - s) * (2 * M_PI) / period ) + change + begin); +} + +float ElasticEaseInOut(float time, float begin, float change, float duration, float amplitude, float period) +{ + float s; + + if (time == 0) + return begin; + if ((time /= duration / 2) == 2) + return begin + change; + if (!period) + period = duration * (0.3f * 1.5f); + if (!amplitude || amplitude < abs(change)) { + amplitude = change; + s = period / 4; + } + else + s = period / ( 2 * M_PI) * asin(change / amplitude); + if (time < 1) { + time -= 1; + return -0.5f * (amplitude * pow(2, 10 * time) * sin((time * duration - s) * (2 * M_PI) / period)) + begin; + } + + time -= 1; + return amplitude * pow(2, -10 * time) * sin((time * duration - s) * (2 * M_PI) / period) * 0.5f + change + begin; +} + +float ExpoEaseIn(float time, float begin, float change, float duration) +{ + return (time == 0) ? begin : change * pow(2, 10 * (time / duration - 1)) + begin; +} + +float ExpoEaseOut(float time, float begin, float change, float duration) +{ + return (time == duration) ? begin + change : change * (-pow(2, -10 * time / duration) + 1) + begin; +} + +float ExpoEaseInOut(float time, float begin, float change, float duration) +{ + if (time == 0) + return begin; + if (time == duration) + return begin + change; + if ((time /= duration / 2) < 1) + return change/2 * pow(2, 10 * (time - 1)) + begin; + --time; + return change / 2 * (-pow(2, -10 * time) + 2) + begin; +} + +float LinearEase(float time, float begin, float change, float duration) +{ + return change * time / duration + begin; +} + +float QuadEaseIn(float time, float begin, float change, float duration) +{ + time /= duration; + return change * time * time + begin; +} + +float QuadEaseOut(float time, float begin, float change, float duration) +{ + time /= duration; + return -change * time * (time - 2) + begin; +} + +float QuadEaseInOut(float time, float begin, float change, float duration) +{ + if ((time /= duration / 2) < 1) + return change / 2 * time * time + begin; + --time; + return -change / 2 * (time * (time - 2) - 1) + begin; +} + + +float QuartEaseIn(float time, float begin, float change, float duration) +{ + time /= duration; + return change * time * time * time * time + begin; +} + +float QuartEaseOut(float time, float begin, float change, float duration) +{ + time = time / duration - 1; + return -change * (time * time * time * time - 1) + begin; +} + +float QuartEaseInOut(float time, float begin, float change, float duration) +{ + if ((time /= duration / 2) < 1) + return change / 2 * time * time * time * time + begin; + time -= 2; + return -change/2 * ( time * time * time * time - 2) + begin; +} + +float QuintEaseIn(float time, float begin, float change, float duration) +{ + time /= duration; + return change * time * time * time * time * time + begin; +} +float QuintEaseOut(float time, float begin, float change, float duration) +{ + time = time / duration - 1; + return change * (time * time * time * time * time + 1) + begin; +} +float QuintEaseInOut(float time, float begin, float change, float duration) +{ + if ((time /= duration / 2) < 1) + return change/2 * time * time * time * time * time + begin; + time -= 2; + return change / 2 * (time * time * time * time * time + 2) + begin; +} + +float SineEaseIn(float time, float begin, float change, float duration) +{ + return -change * cos(time / duration * M_PI_2) + change + begin; +} + +float SineEaseOut(float time, float begin, float change, float duration) +{ + return change * sin(time / duration * M_PI_2) + begin; +} + +float SineEaseInOut(float time, float begin, float change, float duration) +{ + return -change / 2 * (cos(M_PI * time / duration) - 1) + begin; +} + diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c index 6b294c93b42..b14bbe821e8 100644 --- a/source/blender/editors/animation/keyframes_edit.c +++ b/source/blender/editors/animation/keyframes_edit.c @@ -922,15 +922,109 @@ static short set_bezt_bezier(KeyframeEditData *UNUSED(ked), BezTriple *bezt) return 0; } +static short set_bezt_back(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo = BEZT_IPO_BACK; + return 0; +} + +static short set_bezt_bounce(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo = BEZT_IPO_BOUNCE; + return 0; +} + +static short set_bezt_circle(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo = BEZT_IPO_CIRC; + return 0; +} + +static short set_bezt_cubic(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo = BEZT_IPO_CUBIC; + return 0; +} + +static short set_bezt_elastic(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo = BEZT_IPO_ELASTIC; + return 0; +} + +static short set_bezt_expo(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo = BEZT_IPO_EXPO; + return 0; +} + +static short set_bezt_quad(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo = BEZT_IPO_QUAD; + return 0; +} + +static short set_bezt_quart(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo = BEZT_IPO_QUART; + return 0; +} + +static short set_bezt_quint(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo = BEZT_IPO_QUINT; + return 0; +} + +static short set_bezt_sine(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->ipo= BEZT_IPO_SINE; + return 0; +} + /* Set the interpolation type of the selected BezTriples in each F-Curve to the specified one */ // ANIM_editkeyframes_ipocurve_ipotype() ! KeyframeEditFunc ANIM_editkeyframes_ipo(short code) { switch (code) { + /* interpolation */ case BEZT_IPO_CONST: /* constant */ return set_bezt_constant; case BEZT_IPO_LIN: /* linear */ return set_bezt_linear; + + /* easing */ + case BEZT_IPO_BACK: + return set_bezt_back; + case BEZT_IPO_BOUNCE: + return set_bezt_bounce; + case BEZT_IPO_CIRC: + return set_bezt_circle; + case BEZT_IPO_CUBIC: + return set_bezt_cubic; + case BEZT_IPO_ELASTIC: + return set_bezt_elastic; + case BEZT_IPO_EXPO: + return set_bezt_expo; + case BEZT_IPO_QUAD: + return set_bezt_quad; + case BEZT_IPO_QUART: + return set_bezt_quart; + case BEZT_IPO_QUINT: + return set_bezt_quint; + case BEZT_IPO_SINE: + return set_bezt_sine; + default: /* bezier */ return set_bezt_bezier; } @@ -985,6 +1079,45 @@ KeyframeEditFunc ANIM_editkeyframes_keytype(short code) } } +/* ------- */ + +static short set_easingtype_easein(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->easing = BEZT_IPO_EASE_IN; + return 0; +} + +static short set_easingtype_easeout(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->easing = BEZT_IPO_EASE_OUT; + return 0; +} + +static short set_easingtype_easeinout(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + bezt->easing = BEZT_IPO_EASE_IN_OUT; + return 0; +} + +/* Set the easing type of the selected BezTriples in each F-Curve to the specified one */ +KeyframeEditFunc ANIM_editkeyframes_easing(short mode) +{ + switch (mode) { + case BEZT_IPO_EASE_IN: /* ease in */ + return set_easingtype_easein; + + case BEZT_IPO_EASE_OUT: /* ease out */ + return set_easingtype_easeout; + + case BEZT_IPO_EASE_IN_OUT: /* both */ + default: + return set_easingtype_easeinout; + } +} + /* ******************************************* */ /* Selection */ diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index 4e68b6a9494..c8365689803 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -217,6 +217,7 @@ KeyframeEditFunc ANIM_editkeyframes_select(short mode); KeyframeEditFunc ANIM_editkeyframes_handles(short mode); KeyframeEditFunc ANIM_editkeyframes_ipo(short mode); KeyframeEditFunc ANIM_editkeyframes_keytype(short mode); +KeyframeEditFunc ANIM_editkeyframes_easing(short mode); /* -------- BezTriple Callbacks (Selection Map) ---------- */ diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 42aa9935251..671a4c3afd3 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -291,7 +291,26 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) /* interpolation */ col = uiLayoutColumn(layout, FALSE); uiItemR(col, &bezt_ptr, "interpolation", 0, NULL, ICON_NONE); - + + /* easing type */ + if (bezt->ipo > BEZT_IPO_BEZ) + uiItemR(col, &bezt_ptr, "easing", 0, NULL, 0); + + /* easing extra */ + switch (bezt->ipo) { + case BEZT_IPO_BACK: + col = uiLayoutColumn(layout, 1); + uiItemR(col, &bezt_ptr, "back", 0, NULL, 0); + break; + case BEZT_IPO_ELASTIC: + col = uiLayoutColumn(layout, 1); + uiItemR(col, &bezt_ptr, "amplitude", 0, NULL, 0); + uiItemR(col, &bezt_ptr, "period", 0, NULL, 0); + break; + default: + break; + } + /* numerical coordinate editing * - we use the button-versions of the calls so that we can attach special update handlers * and unit conversion magic that cannot be achieved using a purely RNA-approach diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index ab69dc9e7b9..eb36abe6eef 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -623,6 +623,7 @@ static void draw_fcurve_curve_samples(bAnimContext *ac, ID *id, FCurve *fcu, Vie glPopMatrix(); } +#if 0 /* helper func - draw one repeat of an F-Curve */ static void draw_fcurve_curve_bezts(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d) { @@ -778,7 +779,8 @@ static void draw_fcurve_curve_bezts(bAnimContext *ac, ID *id, FCurve *fcu, View2 glEnd(); glPopMatrix(); -} +} +#endif /* Debugging -------------------------------- */ @@ -995,7 +997,8 @@ void graph_draw_curves(bAnimContext *ac, SpaceIpo *sipo, ARegion *ar, View2DGrid else if (((fcu->bezt) || (fcu->fpt)) && (fcu->totvert)) { /* just draw curve based on defined data (i.e. no modifiers) */ if (fcu->bezt) - draw_fcurve_curve_bezts(ac, ale->id, fcu, &ar->v2d); + //draw_fcurve_curve_bezts(ac, ale->id, fcu, &ar->v2d); + draw_fcurve_curve(ac, ale->id, fcu, &ar->v2d, grid); // XXX: better to do an optimised integration here instead, but for now, this works else if (fcu->fpt) draw_fcurve_curve_samples(ac, ale->id, fcu, &ar->v2d); } diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 93d103e11a6..0fa6edd0011 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -1502,6 +1502,72 @@ void GRAPH_OT_interpolation_type(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", beztriple_interpolation_mode_items, 0, "Type", ""); } +/* ******************** Set Easing Operator *********************** */ + +static void seteasing_graph_keys(bAnimContext *ac, short mode) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + KeyframeEditFunc set_cb = ANIM_editkeyframes_easing(mode); + + /* filter data */ + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* loop through setting BezTriple easing + * Note: we do not supply KeyframeEditData to the looper yet. Currently that's not necessary here... + */ + for (ale = anim_data.first; ale; ale = ale->next) + ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve); + + /* cleanup */ + BLI_freelistN(&anim_data); +} + +static int graphkeys_easing_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + short mode; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get handle setting mode */ + mode = RNA_enum_get(op->ptr, "type"); + + /* set handle type */ + seteasing_graph_keys(&ac, mode); + + /* validate keyframes after editing */ + ANIM_editkeyframes_refresh(&ac); + + /* set notifier that keyframe properties have changed */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); + + return OPERATOR_FINISHED; +} + +void GRAPH_OT_easing_type(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Keyframe Easing Type"; + ot->idname = "GRAPH_OT_easing_type"; + ot->description = "Set easing type for the F-Curve segments starting from the selected keyframes"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = graphkeys_easing_exec; + ot->poll = graphop_editable_keyframes_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* id-props */ + ot->prop = RNA_def_enum(ot->srna, "type", beztriple_interpolation_easing_items, 0, "Type", ""); +} + /* ******************** Set Handle-Type Operator *********************** */ /* this function is responsible for setting handle-type of selected keyframes */ diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index 567b0a60bb0..fe1378679d8 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -112,6 +112,7 @@ void GRAPH_OT_euler_filter(struct wmOperatorType *ot); void GRAPH_OT_handle_type(struct wmOperatorType *ot); void GRAPH_OT_interpolation_type(struct wmOperatorType *ot); void GRAPH_OT_extrapolation_type(struct wmOperatorType *ot); +void GRAPH_OT_easing_type(struct wmOperatorType *ot); void GRAPH_OT_frame_jump(struct wmOperatorType *ot); void GRAPH_OT_snap(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index 0b9936ff14e..1041c885697 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -227,6 +227,7 @@ void graphedit_operatortypes(void) WM_operatortype_append(GRAPH_OT_handle_type); WM_operatortype_append(GRAPH_OT_interpolation_type); WM_operatortype_append(GRAPH_OT_extrapolation_type); + WM_operatortype_append(GRAPH_OT_easing_type); WM_operatortype_append(GRAPH_OT_sample); WM_operatortype_append(GRAPH_OT_bake); WM_operatortype_append(GRAPH_OT_sound_bake); @@ -376,6 +377,7 @@ static void graphedit_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) WM_keymap_add_item(keymap, "GRAPH_OT_handle_type", VKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "GRAPH_OT_interpolation_type", TKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_easing_type", EKEY, KM_PRESS, KM_CTRL, 0); /* destructive */ WM_keymap_add_item(keymap, "GRAPH_OT_clean", OKEY, KM_PRESS, 0, 0); diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index f0c555792fc..ebba59ec785 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -108,10 +108,19 @@ typedef struct BevPoint { typedef struct BezTriple { float vec[3][3]; float alfa, weight, radius; /* alfa: tilt in 3D View, weight: used for softbody goal weight, radius: for bevel tapering */ + short ipo; /* ipo: interpolation mode for segment from this BezTriple to the next */ + char h1, h2; /* h1, h2: the handle type of the two handles */ char f1, f2, f3; /* f1, f2, f3: used for selection status */ + char hide; /* hide: used to indicate whether BezTriple is hidden (3D), type of keyframe (eBezTriple_KeyframeTypes) */ + + float back; /* BEZT_IPO_BACK */ + float amplitude, period; /* BEZT_IPO_ELASTIC */ + char easing; /* easing: easing type for interpolation mode (eBezTriple_Easing) */ + + char pad[3]; } BezTriple; /* note; alfa location in struct is abused by Key system */ @@ -341,11 +350,31 @@ typedef enum eBezTriple_Handle { /* interpolation modes (used only for BezTriple->ipo) */ typedef enum eBezTriple_Interpolation { + /* traditional interpolation */ BEZT_IPO_CONST = 0, /* constant interpolation */ BEZT_IPO_LIN = 1, /* linear interpolation */ - BEZT_IPO_BEZ = 2 /* bezier interpolation */ + BEZT_IPO_BEZ = 2, /* bezier interpolation */ + + /* easing equations */ + BEZT_IPO_BACK = 3, + BEZT_IPO_BOUNCE = 4, + BEZT_IPO_CIRC = 5, + BEZT_IPO_CUBIC = 6, + BEZT_IPO_ELASTIC = 7, + BEZT_IPO_EXPO = 8, + BEZT_IPO_QUAD = 9, + BEZT_IPO_QUART = 10, + BEZT_IPO_QUINT = 11, + BEZT_IPO_SINE = 12 } eBezTriple_Interpolation; +/* easing modes (used only for Keyframes - BezTriple->easing) */ +typedef enum eBezTriple_Easing { + BEZT_IPO_EASE_IN = 0, + BEZT_IPO_EASE_OUT = 1, + BEZT_IPO_EASE_IN_OUT = 2 +} eBezTriple_Easing; + /* types of keyframe (used only for BezTriple->hide when BezTriple is used in F-Curves) */ typedef enum eBezTriple_KeyframeType { BEZT_KEYTYPE_KEYFRAME = 0, /* default - 'proper' Keyframe */ diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index 49ff04192b8..0b73fc932a9 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -72,6 +72,7 @@ extern EnumPropertyItem color_sets_items[]; extern EnumPropertyItem beztriple_keyframe_type_items[]; extern EnumPropertyItem beztriple_interpolation_mode_items[]; +extern EnumPropertyItem beztriple_interpolation_easing_items[]; extern EnumPropertyItem keyframe_handle_type_items[]; extern EnumPropertyItem keyblock_type_items[]; diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 0c17b55d2c6..a4d099c69c7 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -34,6 +34,8 @@ #include "BLI_utildefines.h" #include "BLI_math.h" +#include "BLF_translation.h" + #include "BKE_font.h" #include "RNA_access.h" @@ -67,9 +69,29 @@ EnumPropertyItem keyframe_handle_type_items[] = { }; EnumPropertyItem beztriple_interpolation_mode_items[] = { + /* interpolation */ + {0, "", 0, N_("Interpolation"), "Standard transitions between keyframes"}, {BEZT_IPO_CONST, "CONSTANT", 0, "Constant", "No interpolation, value of A gets held until B is encountered"}, {BEZT_IPO_LIN, "LINEAR", 0, "Linear", "Straight-line interpolation between A and B (i.e. no ease in/out)"}, {BEZT_IPO_BEZ, "BEZIER", 0, "Bezier", "Smooth interpolation between A and B, with some control over curve shape"}, + + /* easing */ + {0, "", 0, N_("Easing (by strength)"), "Predefined inertial transitions, useful for motion graphics (from least to most ''dramatic'')"}, + {BEZT_IPO_QUAD, "QUAD", 0, "Quadratic", "Quadratic easing (weakest)"}, + {BEZT_IPO_CUBIC, "CUBIC", 0, "Cubic", "Cubic easing"}, + {BEZT_IPO_QUART, "QUART", 0, "Quartic", "Quartic easing"}, + {BEZT_IPO_QUINT, "QUINT", 0, "Quintic", "Quintic easing"}, + {BEZT_IPO_EXPO, "EXPO", 0, "Exponential", "Exponential easing (strongest)"}, + + {0, "", 0, N_("Dynamic Effects"), "Simple physics-inspired easing effects"}, + {BEZT_IPO_BACK, "BACK", 0, "Back", "Cubic easing with overshoot and settle"}, + {BEZT_IPO_BOUNCE, "BOUNCE", 0, "Bounce", "Exponentially decaying parabolic bounce, like when objects collide"}, + {BEZT_IPO_ELASTIC, "ELASTIC", 0, "Elastic", "Exponentially decaying sine wave, like an elastic band"}, + + {0, "", 0, N_("Other"), "Other easing equations"}, + {BEZT_IPO_SINE, "SINE", 0, "Sinusoidal", "Sinusoidal easing"}, + {BEZT_IPO_CIRC, "CIRC", 0, "Circular", "Circular easing"}, + {0, NULL, 0, NULL, NULL} }; diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index aad8d6057ed..81fa2ad62a4 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -27,6 +27,7 @@ #include <stdlib.h> #include "DNA_anim_types.h" +#include "DNA_curve_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -76,6 +77,13 @@ EnumPropertyItem beztriple_keyframe_type_items[] = { {0, NULL, 0, NULL, NULL} }; +EnumPropertyItem beztriple_interpolation_easing_items[] = { + {BEZT_IPO_EASE_IN, "EASE_IN", 0, "Ease In", "Only on the end closest to the next keyframe"}, + {BEZT_IPO_EASE_OUT, "EASE_OUT", 0, "Ease Out", "Only on the end closest to the first keyframe"}, + {BEZT_IPO_EASE_IN_OUT, "EASE_IN_OUT", 0, "Ease In and Out", "Segment between both keyframes"}, + {0, NULL, 0, NULL, NULL} +}; + #ifdef RNA_RUNTIME #include "WM_api.h" @@ -1652,6 +1660,30 @@ static void rna_def_fkeyframe(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Type", "Type of keyframe (for visual purposes only)"); RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); + + prop = RNA_def_property(srna, "easing", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "easing"); + RNA_def_property_enum_items(prop, beztriple_interpolation_easing_items); + RNA_def_property_ui_text(prop, "Easing", + "Which ends of the segment between this and the next keyframe easing " + "interpolation is applied to"); + RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_PROP, NULL); + + prop = RNA_def_property(srna, "back", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "back"); + RNA_def_property_ui_text(prop, "Back", "Amount of overshoot for 'back' easing"); + RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_PROP, NULL); + + prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "amplitude"); + RNA_def_property_ui_text(prop, "Amplitude", "Amplitude of bounces for elastic easing"); + RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_PROP, NULL); + + prop = RNA_def_property(srna, "period", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "period"); + RNA_def_property_ui_text(prop, "Period", "Time between bounces for elastic easing"); + RNA_def_property_update(prop, NC_ANIMATION|ND_KEYFRAME_PROP, NULL); + /* Vector values */ prop = RNA_def_property(srna, "handle_left", PROP_FLOAT, PROP_COORDS); /* keyframes are dimensionless */ RNA_def_property_array(prop, 2); |