Patch T22084: Robert Penner Easing Equations for FCurves

This commit introduces support for a number of new interpolation types
which are useful for motion-graphics work. These define a number of
"easing equations" (basically, equations which define some preset
ways that one keyframe transitions to another) which reduce the amount
of manual work (inserting and tweaking keyframes) to achieve certain
common effects. For example, snappy movements, and fake-physics such
as bouncing/springing effects.

The additional interpolation types introduced in this commit can be found
in many packages and toolkits (notably Qt and all modern web browsers).
For more info and a few live demos, see [1] and [2].


Credits:
* Dan Eicher (dna)              - Original patch
* Thomas Beck (plasmasolutions) - Porting/updating patch to 2.70 codebase
* Joshua Leung (aligorith)      - Code review and a few polishing tweaks

Additional Resources:
[1] http://easings.net
[2] http://www.robertpenner.com/easing/
This commit is contained in:
Joshua Leung 2014-03-22 02:50:24 +13:00
parent 6e99fb04b6
commit daccaa713b
Notes: blender-bot 2023-02-13 11:56:28 +01:00
Referenced by commit a801487ef5, Fix T65076: Missing EasingType implementation on the Dopesheet
16 changed files with 895 additions and 42 deletions

View File

@ -227,6 +227,7 @@ class GRAPH_MT_key(Menu):
layout.separator()
layout.operator_menu_enum("graph.handle_type", "type", text="Handle Type")
layout.operator_menu_enum("graph.interpolation_type", "type", text="Interpolation Mode")
layout.operator_menu_enum("graph.easing_type", "type", text="Easing Type")
layout.separator()
layout.operator("graph.clean")

View File

@ -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;
}
}
}

View File

@ -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__

View File

@ -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

View File

@ -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;
}

View File

@ -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 */

View File

@ -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) ---------- */

View File

@ -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

View File

@ -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);
}

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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[];

View File

@ -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}
};

View File

@ -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);