1
1

Compare commits

...

4 Commits

Author SHA1 Message Date
eea32ffa4d Support Copy To Selected and Alt-Click for F-Curves in the curve editor.
This affects both the new smoothing mode setting and display color.
2017-08-20 17:09:50 +03:00
644a5ed7ef TEMPORARY HACK: convert the old flag to the new auto_smoothing enum.
Only for my branch build; not to be merged.
2017-08-20 17:09:50 +03:00
7a765e7afa Implement a new automatic handle algorithm to produce smooth F-Curves.
The legacy algorithm only considers two adjacent points when computing
the bezier handles, which cannot produce satisfactory results. Animators
are often forced to manually adjust all curves.

The new approach instead solves a system of equations to trace a cubic spline
with continuous second derivative through the whole segment of auto points,
delimited at ends by keyframes with handles set by other requirements.

This algorithm also adjusts Vector handles that face ordinary bezier keyframes
to achieve zero acceleration at the Vector keyframe, instead of simply pointing
it at the adjacent point.

Original idea and implementation by Benoit Bolsee <benoit.bolsee@online.be>;
code mostly rewritten to improve code clarity and extensibility.
2017-08-20 17:09:50 +03:00
0d814a2e49 Make auto handle placement aware of cyclic extrapolation.
Cyclic extrapolation is implemented as an f-curve modifier, so this
technically violates abstraction separation and is something of a hack.
However without such behavior achieving smooth looping with cyclic
extrapolation is extremely cumbersome.

The new behavior is applied when the first modifier is Cyclic
extrapolation in Repeat or Repeat with Offset mode without
using influence, repeat count or range restrictions.

This change in behavior means that curve handles have to be updated
when the modifier is added, removed or its options change. Due to the
way code is structured, it seems it requires a helper link to the
containing curve from the modifier object.

Reviewers: aligorith

Differential Revision: https://developer.blender.org/D2783
2017-08-20 17:09:49 +03:00
27 changed files with 852 additions and 48 deletions

View File

@@ -202,10 +202,12 @@ void BKE_nurb_bpoint_calc_normal(struct Nurb *nu, struct BPoint *bp, float r_nor
void BKE_nurb_bpoint_calc_plane(struct Nurb *nu, struct BPoint *bp, float r_plane[3]);
void BKE_nurb_handle_calc(struct BezTriple *bezt, struct BezTriple *prev, struct BezTriple *next,
const bool is_fcurve);
const bool is_fcurve, const char smoothing);
void BKE_nurb_handle_calc_simple(struct Nurb *nu, struct BezTriple *bezt);
void BKE_nurb_handle_calc_simple_auto(struct Nurb *nu, struct BezTriple *bezt);
void BKE_nurb_handle_smooth_fcurve(struct BezTriple *bezt, int total, bool cyclic);
void BKE_nurb_handles_calc(struct Nurb *nu);
void BKE_nurb_handles_autocalc(struct Nurb *nu, int flag);
void BKE_nurb_bezt_handle_test(struct BezTriple *bezt, const bool use_handle);

View File

@@ -188,7 +188,7 @@ const FModifierTypeInfo *get_fmodifier_typeinfo(const int type);
/* ---------------------- */
struct FModifier *add_fmodifier(ListBase *modifiers, int type);
struct FModifier *add_fmodifier(ListBase *modifiers, int type, struct FCurve *owner_fcu);
struct FModifier *copy_fmodifier(const struct FModifier *src);
void copy_fmodifiers(ListBase *dst, const ListBase *src);
bool remove_fmodifier(ListBase *modifiers, struct FModifier *fcm);

View File

@@ -41,6 +41,7 @@
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
#include "DNA_anim_types.h"
#include "DNA_curve_types.h"
#include "DNA_material_types.h"
@@ -3134,7 +3135,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
static void calchandleNurb_intern(
BezTriple *bezt, const BezTriple *prev, const BezTriple *next,
bool is_fcurve, bool skip_align)
bool is_fcurve, bool skip_align, char fcurve_smoothing)
{
/* defines to avoid confusion */
#define p2_h1 ((p2) - 3)
@@ -3148,6 +3149,9 @@ static void calchandleNurb_intern(
float len_ratio;
const float eps = 1e-5;
/* assume normal handle until we check */
bezt->f5 = HD_AUTOTYPE_NORMAL;
if (bezt->h1 == 0 && bezt->h2 == 0) {
return;
}
@@ -3199,7 +3203,13 @@ static void calchandleNurb_intern(
tvec[2] = dvec_b[2] / len_b + dvec_a[2] / len_a;
if (is_fcurve) {
len = tvec[0];
if (fcurve_smoothing != FCURVE_SMOOTH_NONE) {
/* force the handlers transition to be 1/3 */
len = 6.0f/2.5614f;
}
else {
len = tvec[0];
}
}
else {
len = len_v3(tvec);
@@ -3210,10 +3220,12 @@ static void calchandleNurb_intern(
/* only for fcurves */
bool leftviolate = false, rightviolate = false;
if (len_a > 5.0f * len_b)
len_a = 5.0f * len_b;
if (len_b > 5.0f * len_a)
len_b = 5.0f * len_a;
if (!is_fcurve || fcurve_smoothing == FCURVE_SMOOTH_NONE) {
if (len_a > 5.0f * len_b)
len_a = 5.0f * len_b;
if (len_b > 5.0f * len_a)
len_b = 5.0f * len_a;
}
if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM)) {
len_a /= len;
@@ -3224,6 +3236,7 @@ static void calchandleNurb_intern(
float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) {
bezt->vec[0][1] = bezt->vec[1][1];
bezt->f5 = HD_AUTOTYPE_SPECIAL;
}
else { /* handles should not be beyond y coord of two others */
if (ydiff1 <= 0.0f) {
@@ -3250,6 +3263,7 @@ static void calchandleNurb_intern(
float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
if ( (ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f) ) {
bezt->vec[2][1] = bezt->vec[1][1];
bezt->f5 = HD_AUTOTYPE_SPECIAL;
}
else { /* handles should not be beyond y coord of two others */
if (ydiff1 <= 0.0f) {
@@ -3397,7 +3411,7 @@ static void calchandlesNurb_intern(Nurb *nu, bool skip_align)
next = bezt + 1;
while (a--) {
calchandleNurb_intern(bezt, prev, next, 0, skip_align);
calchandleNurb_intern(bezt, prev, next, 0, skip_align, 0);
prev = bezt;
if (a == 1) {
if (nu->flagu & CU_NURB_CYCLIC)
@@ -3412,9 +3426,519 @@ static void calchandlesNurb_intern(Nurb *nu, bool skip_align)
}
}
void BKE_nurb_handle_calc(BezTriple *bezt, BezTriple *prev, BezTriple *next, const bool is_fcurve)
static void *allocate_arrays(int count, float ***floats, char ***chars, const char *name)
{
calchandleNurb_intern(bezt, prev, next, is_fcurve, false);
int num_floats = 0, num_chars = 0;
while (floats && floats[num_floats]) {
num_floats++;
}
while (chars && chars[num_chars]) {
num_chars++;
}
void *buffer = (float*)MEM_mallocN(count * (sizeof(float)*num_floats + num_chars), name);
if (!buffer)
return NULL;
float *fptr = buffer;
for (int i = 0; i < num_floats; i++, fptr += count)
*floats[i] = fptr;
char *cptr = (char*)fptr;
for (int i = 0; i < num_chars; i++, cptr += count)
*chars[i] = cptr;
return buffer;
}
/* computes in which direction to change h[i] to satisfy conditions better */
static float bezier_relax_direction(float *a, float *b, float *c, float *d, float *h, int i, int count)
{
/* current deviation between sides of the equation */
float state = a[i] * h[(i+count-1)%count] + b[i] * h[i] + c[i] * h[(i+1)%count] - d[i];
/* only the sign is meaningful */
return -state * b[i];
}
static void bezier_lock_unknown(float *a, float *b, float *c, float *d, int i, float value)
{
a[i] = c[i] = 0.0f;
b[i] = 1.0f;
d[i] = value;
}
static bool tridiagonal_solve_with_limits(float *a, float *b, float *c, float *d, float *h, float *hmin, float *hmax, int solve_count)
{
float *a0, *b0, *c0, *d0;
float **arrays[] = { &a0, &b0, &c0, &d0, NULL };
char *is_locked, *num_unlocks;
char **flagarrays[] = { &is_locked, &num_unlocks, NULL };
void *tmps = allocate_arrays(solve_count, arrays, flagarrays, "tridiagonal_solve_with_limits");
if (!tmps)
return false;
memcpy(a0, a, sizeof(float)*solve_count);
memcpy(b0, b, sizeof(float)*solve_count);
memcpy(c0, c, sizeof(float)*solve_count);
memcpy(d0, d, sizeof(float)*solve_count);
memset(is_locked, 0, solve_count);
memset(num_unlocks, 0, solve_count);
bool overshoot, unlocked;
do
{
if (!BLI_tridiagonal_solve_cyclic(a, b, c, d, h, solve_count)) {
MEM_freeN(tmps);
return false;
}
/* first check if any handles overshoot the limits, and lock them */
bool all = false, locked = false;
overshoot = unlocked = false;
do
{
for (int i = 0; i < solve_count; i++) {
if (h[i] >= hmin[i] && h[i] <= hmax[i])
continue;
overshoot = true;
float target = h[i] > hmax[i] ? hmax[i] : hmin[i];
/* heuristically only lock handles that go in the right direction if there are such ones */
if (target != 0.0f || all) {
/* mark item locked */
is_locked[i] = 1;
bezier_lock_unknown(a, b, c, d, i, target);
locked = true;
}
}
all = true;
}
while (overshoot && !locked);
/* if no handles overshot and were locked, see if it may be a good idea to unlock some handles */
if (!locked) {
for (int i = 0; i < solve_count; i++) {
// to definitely avoid infinite loops limit this to 2 times
if (!is_locked[i] || num_unlocks[i] >= 2)
continue;
/* if the handle wants to move in allowable direction, release it */
float relax = bezier_relax_direction(a0, b0, c0, d0, h, i, solve_count);
if ((relax > 0 && h[i] < hmax[i]) || (relax < 0 && h[i] > hmin[i])) {
/* restore equation coefficients */
a[i] = a0[i]; b[i] = b0[i]; c[i] = c0[i]; d[i] = d0[i];
is_locked[i] = 0;
num_unlocks[i]++;
unlocked = true;
}
}
}
}
while (overshoot || unlocked);
MEM_freeN(tmps);
return true;
}
/*
* This function computes the handles of a series of auto bezier points
* on the basis of 'no acceleration discontinuities' at the points.
* The first and last bezier points are considered 'fixed' (their handles are not touched)
* The result is the smoothest possible trajectory going through intemediate points.
* The difficulty is that the handles depends on their neighbours.
*
* The exact solution is found by solving a tridiagonal matrix equation formed
* by the continuity and boundary conditions. Although theoretically handle position
* is affected by all other points of the curve segment, in practice the influence
* decreases exponentially with distance.
*
* Note: this algorithm assumes that the handle horizontal size if always 1/3 of the
* of the interval to the next point. This rule ensures linear interpolation of time.
*
* ^ height (co 1)
* | yN
* | yN-1 |
* | y2 | |
* | y1 | | |
* | y0 | | | |
* | | | | | |
* | | | | | |
* | | | | | |
* |-------t1---------t2--------- ~ --------tN-------------------> time (co 0)
*
*
* Mathematical basis:
*
* 1. Handle lengths on either side of each point are connected by a factor
* ensuring continuity of the first derivative:
*
* l[i] = t[i+1]/t[i]
*
* 2. The tridiagonal system is formed by the following equation, which is derived
* by differentiating the bezier curve and specifies second derivative continuity
* at every point:
*
* l[i]^2 * h[i-1] + (2*l[i]+2) * h[i] + 1/l[i+1] * h[i+1] = (y[i]-y[i-1])*l[i]^2 + y[i+1]-y[i]
*
* 3. If this point is adjacent to a manually set handle with X size not equal to 1/3
* of the horizontal interval, this equation becomes slightly more complex:
*
* l[i]^2 * h[i-1] + (3*(1-R[i-1])*l[i] + 3*(1-L[i+1])) * h[i] + 1/l[i+1] * h[i+1] = (y[i]-y[i-1])*l[i]^2 + y[i+1]-y[i]
*
* The difference between equations amounts to this, and it's obvious that when R[i-1]
* and L[i+1] are both 1/3, it becomes zero:
*
* ( (1-3*R[i-1])*l[i] + (1-3*L[i+1]) ) * h[i]
*
* 4. The equations for zero acceleration border conditions are basically the above
* equation with parts omitted, so the handle size correction also applies.
*/
static void bezier_eq_continuous(float *a, float *b, float *c, float *d, float *dy, float *l, int i)
{
a[i] = l[i]*l[i];
b[i] = 2.0f*(l[i] + 1);
c[i] = 1.0f/l[i+1];
d[i] = dy[i]*l[i]*l[i] + dy[i+1];
}
static void bezier_eq_noaccel_right(float *a, float *b, float *c, float *d, float *dy, float *l, int i)
{
a[i] = 0.0f;
b[i] = 2.0f;
c[i] = 1.0f/l[i+1];
d[i] = dy[i+1];
}
static void bezier_eq_noaccel_left(float *a, float *b, float *c, float *d, float *dy, float *l, int i)
{
a[i] = l[i]*l[i];
b[i] = 2.0f*l[i];
c[i] = 0.0f;
d[i] = dy[i]*l[i]*l[i];
}
/* auto clamp prevents its own point going the wrong way, and adjacent handles overshooting */
static void bezier_clamp(float *hmax, float *hmin, int i, float dy, bool no_reverse, bool no_overshoot)
{
if (dy > 0) {
if (no_overshoot)
hmax[i] = min_ff(hmax[i], dy);
if (no_reverse)
hmin[i] = 0.0f;
}
else if (dy < 0) {
if (no_reverse)
hmax[i] = 0.0f;
if (no_overshoot)
hmin[i] = max_ff(hmin[i], dy);
}
else if (no_reverse || no_overshoot) {
hmax[i] = hmin[i] = 0.0f;
}
}
/* write changes to a bezier handle */
static void bezier_output_handle_inner(BezTriple *bezt, bool right, float newval[3], bool endpoint)
{
float tmp[3];
int idx = right ? 2 : 0;
char hr = right ? bezt->h2 : bezt->h1;
char hm = right ? bezt->h1 : bezt->h2;
/* only assign Auto/Vector handles */
if (!ELEM(hr, HD_AUTO, HD_AUTO_ANIM, HD_VECT))
return;
copy_v3_v3(bezt->vec[idx], newval);
/* fix up the Align handle if any */
if (ELEM(hm, HD_ALIGN, HD_ALIGN_DOUBLESIDE)) {
float hlen = len_v3v3(bezt->vec[1], bezt->vec[2-idx]);
float h2len = len_v3v3(bezt->vec[1], bezt->vec[idx]);
sub_v3_v3v3(tmp, bezt->vec[1], bezt->vec[idx]);
madd_v3_v3v3fl(bezt->vec[2-idx], bezt->vec[1], tmp, hlen/h2len);
}
/* at end points of the curve, mirror handle to the other side */
else if (endpoint && ELEM(hm, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
sub_v3_v3v3(tmp, bezt->vec[1], bezt->vec[idx]);
add_v3_v3v3(bezt->vec[2-idx], bezt->vec[1], tmp);
}
}
static void bezier_output_handle(BezTriple *bezt, bool right, float dy, bool endpoint)
{
float tmp[3];
copy_v3_v3(tmp, bezt->vec[right ? 2 : 0]);
tmp[1] = bezt->vec[1][1] + dy;
bezier_output_handle_inner(bezt, right, tmp, endpoint);
}
static bool bezier_check_solve_end_handle(BezTriple *bezt, char htype, bool end)
{
return (htype == HD_VECT) || (end && ELEM(htype, HD_AUTO, HD_AUTO_ANIM) && bezt->f5 == HD_AUTOTYPE_NORMAL);
}
static float bezier_calc_handle_adj(float hsize[2], float dx)
{
/* if handles intersect in x direction, they are scaled to fit */
float fac = dx/(hsize[0] + dx/3.0f);
if (fac < 1.0f)
mul_v2_fl(hsize, fac);
return 1.0f - 3.0f*hsize[0]/dx;
}
static void bezier_handle_calc_smooth_fcurve(BezTriple *bezt, int total, int start, int count, bool cycle)
{
float *dx, *dy, *l, *a, *b, *c, *d, *h, *hmax, *hmin;
float **arrays[] = { &dx, &dy, &l, &a, &b, &c, &d, &h, &hmax, &hmin, NULL };
int solve_count = count;
/* verify index ranges */
if (count < 2)
return;
BLI_assert(start < total-1 && count <= total);
BLI_assert(start + count <= total || cycle);
bool full_cycle = (start == 0 && count == total && cycle);
BezTriple *bezt_first = &bezt[start];
BezTriple *bezt_last = &bezt[(start+count > total) ? start+count-total : start+count-1];
bool solve_first = bezier_check_solve_end_handle(bezt_first, bezt_first->h2, start==0);
bool solve_last = bezier_check_solve_end_handle(bezt_last, bezt_last->h1, start+count==total);
if (count == 2 && !full_cycle && solve_first == solve_last)
return;
/* allocate all */
void *tmp_buffer = allocate_arrays(count, arrays, NULL, "bezier_calc_smooth_tmp");
if (!tmp_buffer)
return;
/* point locations */
dx[0] = dy[0] = NAN_FLT;
for (int i = 1, j = start+1; i < count; i++, j++) {
dx[i] = bezt[j].vec[1][0] - bezt[j-1].vec[1][0];
dy[i] = bezt[j].vec[1][1] - bezt[j-1].vec[1][1];
/* when cyclic, jump from last point to first */
if (cycle && j == total-1)
j = 0;
}
/* ratio of x intervals */
l[0] = l[count-1] = 1.0f;
for (int i = 1; i < count-1; i++)
l[i] = dx[i+1] / dx[i];
/* compute handle clamp ranges */
bool clamped_prev = false, clamped_cur = ELEM(HD_AUTO_ANIM, bezt_first->h1, bezt_first->h2);
for (int i = 0; i < count; i++) {
hmax[i] = FLT_MAX;
hmin[i] = -FLT_MAX;
}
for (int i = 1, j = start+1; i < count; i++, j++) {
clamped_prev = clamped_cur;
clamped_cur = ELEM(HD_AUTO_ANIM, bezt[j].h1, bezt[j].h2);
if (cycle && j == total-1)
{
j = 0;
clamped_cur = clamped_cur || ELEM(HD_AUTO_ANIM, bezt[j].h1, bezt[j].h2);
}
bezier_clamp(hmax, hmin, i-1, dy[i], clamped_prev, clamped_prev);
bezier_clamp(hmax, hmin, i, dy[i] * l[i], clamped_cur, clamped_cur);
}
/* full cycle merges first and last points into continuous loop */
float first_handle_adj = 0.0f, last_handle_adj = 0.0f;
if (full_cycle) {
/* reduce the number of uknowns by one */
int i = solve_count = count-1;
dx[0] = dx[i];
dy[0] = dy[i];
l[0] = l[i] = dx[1] / dx[0];
hmin[0] = max_ff(hmin[0], hmin[i]);
hmax[0] = min_ff(hmax[0], hmax[i]);
solve_first = solve_last = true;
bezier_eq_continuous(a, b, c, d, dy, l, 0);
}
else {
float tmp[2];
/* boundary condition: fixed handles or zero curvature */
if (!solve_first) {
sub_v2_v2v2(tmp, bezt_first->vec[2], bezt_first->vec[1]);
first_handle_adj = bezier_calc_handle_adj(tmp, dx[1]);
bezier_lock_unknown(a, b, c, d, 0, tmp[1]);
}
else
bezier_eq_noaccel_right(a, b, c, d, dy, l, 0);
if (!solve_last) {
sub_v2_v2v2(tmp, bezt_last->vec[1], bezt_last->vec[0]);
last_handle_adj = bezier_calc_handle_adj(tmp, dx[count-1]);
bezier_lock_unknown(a, b, c, d, count-1, tmp[1]);
}
else
bezier_eq_noaccel_left(a, b, c, d, dy, l, count-1);
}
/* main tridiagonal system of equations */
for (int i = 1; i < count-1; i++) {
bezier_eq_continuous(a, b, c, d, dy, l, i);
}
/* apply correction for user-defined handles with nonstandard x positions */
if (!full_cycle) {
if (count > 2 || solve_last)
b[1] += l[1]*first_handle_adj;
if (count > 2 || solve_first)
b[count-2] += last_handle_adj;
}
/* solve and output results */
if (tridiagonal_solve_with_limits(a, b, c, d, h, hmin, hmax, solve_count)) {
if (full_cycle)
h[count-1] = h[0];
for (int i = 1, j = start+1; i < count-1; i++, j++) {
bool end = (j == total-1);
bezier_output_handle(&bezt[j], false, - h[i] / l[i], end);
if (end)
j = 0;
bezier_output_handle(&bezt[j], true, h[i], end);
}
if (solve_first)
bezier_output_handle(bezt_first, true, h[0], start == 0);
if (solve_last)
bezier_output_handle(bezt_last, false, - h[count-1] / l[count-1], start+count == total);
}
/* free all */
MEM_freeN(tmp_buffer);
}
static bool is_auto_point(BezTriple *bezt)
{
return ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) && ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM);
}
static bool is_free_auto_point(BezTriple *bezt)
{
return is_auto_point(bezt) && bezt->f5 == HD_AUTOTYPE_NORMAL;
}
static void bezier_handle_smooth_curve(void (*smooth_fn)(BezTriple*,int,int,int,bool), bool (*check_fn)(BezTriple*), BezTriple *bezt, int total, bool cycle)
{
/* ignore cyclic extrapolation if end points are locked */
cycle = cycle && check_fn(&bezt[0]) && check_fn(&bezt[total-1]);
/* if cyclic, try to find a sequence break point */
int search_base = 0;
if (cycle) {
for (int i = 1; i < total-1; i++) {
if (!check_fn(&bezt[i])) {
search_base = i;
break;
}
}
/* all points of the curve are freely changeable auto handles - solve as full cycle */
if (search_base == 0) {
smooth_fn(bezt, total, 0, total, cycle);
return;
}
}
/* Find continuous subsequences of free auto handles and smooth them, starting at
* search_base. In cyclic mode these subsequences can span the cycle boundary. */
int start = search_base, count = 1;
for (int i = 1, j = start+1; i < total; i++, j++) {
/* in cyclic mode: jump from last to first point when necessary */
if (j == total-1 && cycle)
j = 0;
/* non auto handle closes the list (we come here at least for the last handle, see above) */
if (!check_fn(&bezt[j])) {
smooth_fn(bezt, total, start, count+1, cycle);
start = j;
count = 1;
}
else
count++;
}
if (count > 1)
smooth_fn(bezt, total, start, count, cycle);
}
void BKE_nurb_handle_smooth_fcurve(BezTriple *bezt, int total, bool cycle)
{
bezier_handle_smooth_curve(bezier_handle_calc_smooth_fcurve, is_free_auto_point, bezt, total, cycle);
}
void BKE_nurb_handle_calc(BezTriple *bezt, BezTriple *prev, BezTriple *next, const bool is_fcurve, const char smoothing)
{
calchandleNurb_intern(bezt, prev, next, is_fcurve, false, smoothing);
}
void BKE_nurb_handles_calc(Nurb *nu) /* first, if needed, set handle flags */
@@ -3454,7 +3978,7 @@ void BKE_nurb_handle_calc_simple(Nurb *nu, BezTriple *bezt)
if (nu->pntsu > 1) {
BezTriple *prev = BKE_nurb_bezt_get_prev(nu, bezt);
BezTriple *next = BKE_nurb_bezt_get_next(nu, bezt);
BKE_nurb_handle_calc(bezt, prev, next, 0);
BKE_nurb_handle_calc(bezt, prev, next, 0, 0);
}
}

View File

@@ -882,6 +882,44 @@ void fcurve_store_samples(FCurve *fcu, void *data, int start, int end, FcuSample
* that the handles are correctly
*/
/* Checks if the F-Curve has a Cycles modifier with simple settings that warrant transition smoothing */
static bool detect_cycle_modifier(FCurve *fcu)
{
FModifier *fcm = fcu->modifiers.first;
if (!fcm || fcm->type != FMODIFIER_TYPE_CYCLES)
return false;
if (fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED))
return false;
if (fcm->flag & (FMODIFIER_FLAG_RANGERESTRICT | FMODIFIER_FLAG_USEINFLUENCE))
return false;
FMod_Cycles *data = (FMod_Cycles*)fcm->data;
return data && data->after_cycles == 0 && data->before_cycles == 0 &&
ELEM(data->before_mode, FCM_EXTRAPOLATE_CYCLIC, FCM_EXTRAPOLATE_CYCLIC_OFFSET) &&
ELEM(data->after_mode, FCM_EXTRAPOLATE_CYCLIC, FCM_EXTRAPOLATE_CYCLIC_OFFSET);
}
/* If cyclic, set out by shifting in by the difference in position between from and to. */
static BezTriple *cycle_offset_triple(bool cycle, BezTriple *out, const BezTriple *in, const BezTriple *from, const BezTriple *to)
{
if (!cycle)
return NULL;
memcpy(out, in, sizeof(BezTriple));
float delta[3];
sub_v3_v3v3(delta, to->vec[1], from->vec[1]);
for (int i = 0; i < 3; i++)
add_v3_v3(out->vec[i], delta);
return out;
}
/* This function recalculates the handles of an F-Curve
* If the BezTriples have been rearranged, sort them first before using this.
*/
@@ -897,10 +935,16 @@ void calchandles_fcurve(FCurve *fcu)
*/
if (ELEM(NULL, fcu, fcu->bezt) || (a < 2) /*|| ELEM(fcu->ipo, BEZT_IPO_CONST, BEZT_IPO_LIN)*/)
return;
/* if the first modifier is Cycles, smooth the curve through the cycle */
BezTriple *first = &fcu->bezt[0], *last = &fcu->bezt[fcu->totvert-1];
BezTriple tmp;
bool cycle = detect_cycle_modifier(fcu) && BEZT_IS_AUTOH(first) && BEZT_IS_AUTOH(last);
/* get initial pointers */
bezt = fcu->bezt;
prev = NULL;
prev = cycle_offset_triple(cycle, &tmp, &fcu->bezt[fcu->totvert-2], last, first);
next = (bezt + 1);
/* loop over all beztriples, adjusting handles */
@@ -910,25 +954,45 @@ void calchandles_fcurve(FCurve *fcu)
if (bezt->vec[2][0] < bezt->vec[1][0]) bezt->vec[2][0] = bezt->vec[1][0];
/* calculate auto-handles */
BKE_nurb_handle_calc(bezt, prev, next, true);
BKE_nurb_handle_calc(bezt, prev, next, true, fcu->auto_smoothing);
/* for automatic ease in and out */
if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) && ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) {
if (BEZT_IS_AUTOH(bezt) && !cycle) {
/* only do this on first or last beztriple */
if ((a == 0) || (a == fcu->totvert - 1)) {
/* set both handles to have same horizontal value as keyframe */
if (fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) {
bezt->vec[0][1] = bezt->vec[2][1] = bezt->vec[1][1];
/* remember that these keyframes are special, they don't need to be adjusted */
bezt->f5 = HD_AUTOTYPE_SPECIAL;
}
}
}
/* avoid total smoothing failure on duplicate keyframes (can happen during grab) */
if (prev && prev->vec[1][0] >= bezt->vec[1][0]) {
prev->f5 = bezt->f5 = HD_AUTOTYPE_SPECIAL;
}
/* advance pointers for next iteration */
prev = bezt;
if (a == 1) next = NULL;
if (a == 1)
next = cycle_offset_triple(cycle, &tmp, &fcu->bezt[1], first, last);
else next++;
bezt++;
}
/* if cyclic extrapolation and Auto Clamp has triggered, ensure it is symmetric */
if (cycle && (first->f5 != HD_AUTOTYPE_NORMAL || last->f5 != HD_AUTOTYPE_NORMAL)) {
first->vec[0][1] = first->vec[2][1] = first->vec[1][1];
last->vec[0][1] = last->vec[2][1] = last->vec[1][1];
first->f5 = last->f5 = HD_AUTOTYPE_SPECIAL;
}
/* do a second pass for auto handle: compute the handle to have 0 accelaration step */
if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) {
BKE_nurb_handle_smooth_fcurve(fcu->bezt, fcu->totvert, cycle);
}
}
void testhandles_fcurve(FCurve *fcu, const bool use_handle)

View File

@@ -1077,7 +1077,7 @@ const FModifierTypeInfo *fmodifier_get_typeinfo(const FModifier *fcm)
/* API --------------------------- */
/* Add a new F-Curve Modifier to the given F-Curve of a certain type */
FModifier *add_fmodifier(ListBase *modifiers, int type)
FModifier *add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu)
{
const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(type);
FModifier *fcm;
@@ -1098,6 +1098,7 @@ FModifier *add_fmodifier(ListBase *modifiers, int type)
fcm = MEM_callocN(sizeof(FModifier), "F-Curve Modifier");
fcm->type = type;
fcm->flag = FMODIFIER_FLAG_EXPANDED;
fcm->curve = owner_fcu;
fcm->influence = 1.0f;
BLI_addtail(modifiers, fcm);
@@ -1129,6 +1130,7 @@ FModifier *copy_fmodifier(const FModifier *src)
/* copy the base data, clearing the links */
dst = MEM_dupallocN(src);
dst->next = dst->prev = NULL;
dst->curve = NULL;
/* make a new copy of the F-Modifier's data */
dst->data = MEM_dupallocN(src->data);
@@ -1157,6 +1159,7 @@ void copy_fmodifiers(ListBase *dst, const ListBase *src)
/* make a new copy of the F-Modifier's data */
fcm->data = MEM_dupallocN(fcm->data);
fcm->curve = NULL;
/* only do specific constraints if required */
if (fmi && fmi->copy_data)

View File

@@ -1192,7 +1192,7 @@ static void icu_to_fcurves(ID *id, ListBase *groups, ListBase *list, IpoCurve *i
/* Add a new FModifier (Cyclic) instead of setting extend value
* as that's the new equivalent of that option.
*/
FModifier *fcm = add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES);
FModifier *fcm = add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, fcu);
FMod_Cycles *data = (FMod_Cycles *)fcm->data;
/* if 'offset' one is in use, set appropriate settings */

View File

@@ -1204,14 +1204,14 @@ static void mask_calc_point_handle(MaskSplinePoint *point, MaskSplinePoint *poin
#if 1
if (bezt_prev || bezt_next) {
BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0);
BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0, 0);
}
#else
if (handle_type == HD_VECT) {
BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0);
BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0, 0);
}
else if (handle_type == HD_AUTO) {
BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0);
BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0, 0);
}
else if (handle_type == HD_ALIGN || handle_type == HD_ALIGN_DOUBLESIDE) {
float v1[3], v2[3];

View File

@@ -1387,6 +1387,7 @@ void BKE_nlastrip_validate_fcurves(NlaStrip *strip)
/* set default flags */
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL;
/* store path - make copy, and store that */
fcu->rna_path = BLI_strdupn("influence", 9);
@@ -1408,6 +1409,7 @@ void BKE_nlastrip_validate_fcurves(NlaStrip *strip)
/* set default flags */
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL;
/* store path - make copy, and store that */
fcu->rna_path = BLI_strdupn("strip_time", 10);

View File

@@ -48,6 +48,11 @@ bool BLI_eigen_solve_selfadjoint_m3(const float m3[3][3], float r_eigen_values[3
void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[], float r_V[3][3]);
/***************************** Simple Solvers ************************************/
bool BLI_tridiagonal_solve(const float *a, const float *b, const float *c, const float *d, float *x, const int count);
bool BLI_tridiagonal_solve_cyclic(const float *a, const float *b, const float *c, const float *d, float *x, const int count);
/**************************** Inline Definitions ******************************/
#if 0 /* None so far. */
# if BLI_MATH_DO_INLINE

View File

@@ -72,3 +72,115 @@ void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[3], float r_V[3
{
EIG_svd_square_matrix(3, (const float *)m3, (float *)r_U, (float *)r_S, (float *)r_V);
}
/***************************** Simple Solvers ************************************/
/**
* \brief Solve a tridiagonal system of equations:
*
* a[i] * x[i-1] + b[i] * x[i] + c[i] * x[i+1] = d[i]
*
* Ignores a[0] and c[count-1]. Uses Thomas algorithm, e.g. see wiki.
*
* \param x output vector, may be shared with any of the input ones
* \return true if success
*/
bool BLI_tridiagonal_solve(const float *a, const float *b, const float *c, const float *d, float *x, const int count)
{
if (count < 0)
return false;
size_t bytes = sizeof(float)*(unsigned)count;
float *c1 = (float *)MEM_mallocN(bytes*2, "tridiagonal_c1d1");
float *d1 = c1 + count;
if (!c1)
return false;
int i;
double c_prev, d_prev, x_prev;
/* forward pass */
c_prev = ((double)c[0]) / b[0];
d_prev = ((double)d[0]) / b[0];
c1[0] = ((float)c_prev);
d1[0] = ((float)d_prev);
for (i = 1; i < count; i++) {
double denum = b[i] - a[i]*c_prev;
c_prev = c[i] / denum;
d_prev = (d[i] - a[i]*d_prev) / denum;
c1[i] = ((float)c_prev);
d1[i] = ((float)d_prev);
}
/* back pass */
x_prev = d_prev;
x[--i] = ((float)x_prev);
while (--i >= 0) {
x_prev = d1[i] - c1[i] * x_prev;
x[i] = ((float)x_prev);
}
MEM_freeN(c1);
return isfinite(x_prev);
}
/**
* \brief Solve a possibly cyclic tridiagonal system using the Sherman-Morrison formula.
*
* \param x output vector, may be shared with any of the input ones
* \return true if success
*/
bool BLI_tridiagonal_solve_cyclic(const float *a, const float *b, const float *c, const float *d, float *x, const int count)
{
if (count < 1)
return false;
float a0 = a[0], cN = c[count-1];
if (a0 == 0.0f && cN == 0.0f) {
return BLI_tridiagonal_solve(a, b, c, d, x, count);
}
size_t bytes = sizeof(float)*(unsigned)count;
float *tmp = (float*)MEM_mallocN(bytes*2, "tridiagonal_ex");
float *b2 = tmp + count;
if (!tmp)
return false;
/* prepare the noncyclic system; relies on tridiagonal_solve ignoring values */
memcpy(b2, b, bytes);
b2[0] -= a0;
b2[count-1] -= cN;
memset(tmp, 0, bytes);
tmp[0] = a0;
tmp[count-1] = cN;
/* solve for partial solution and adjustment vector */
bool success =
BLI_tridiagonal_solve(a, b2, c, tmp, tmp, count) &&
BLI_tridiagonal_solve(a, b2, c, d, x, count);
/* apply adjustment */
if (success) {
float coeff = (x[0] + x[count-1]) / (1.0f + tmp[0] + tmp[count-1]);
for (int i = 0; i < count; i++)
x[i] -= coeff * tmp[i];
}
MEM_freeN(tmp);
return success;
}

View File

@@ -2389,11 +2389,13 @@ static void lib_link_constraint_channels(FileData *fd, ID *id, ListBase *chanbas
/* Data Linking ----------------------------- */
static void lib_link_fmodifiers(FileData *fd, ID *id, ListBase *list)
static void lib_link_fmodifiers(FileData *fd, ID *id, ListBase *list, FCurve *curve)
{
FModifier *fcm;
for (fcm = list->first; fcm; fcm = fcm->next) {
fcm->curve = curve;
/* data for specific modifiers */
switch (fcm->type) {
case FMODIFIER_TYPE_PYTHON:
@@ -2435,19 +2437,20 @@ static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list)
}
/* modifiers */
lib_link_fmodifiers(fd, id, &fcu->modifiers);
lib_link_fmodifiers(fd, id, &fcu->modifiers, fcu);
}
}
/* NOTE: this assumes that link_list has already been called on the list */
static void direct_link_fmodifiers(FileData *fd, ListBase *list)
static void direct_link_fmodifiers(FileData *fd, ListBase *list, FCurve *curve)
{
FModifier *fcm;
for (fcm = list->first; fcm; fcm = fcm->next) {
/* relink general data */
fcm->data = newdataadr(fd, fcm->data);
fcm->curve = curve;
/* do relinking of data for specific types */
switch (fcm->type) {
@@ -2506,6 +2509,12 @@ static void direct_link_fcurves(FileData *fd, ListBase *list)
*/
fcu->flag &= ~FCURVE_DISABLED;
/* TEMPORARY HACK */
if (fcu->flag & (1<<13)) {
fcu->flag &= ~(1<<13);
fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL;
}
/* driver */
fcu->driver= newdataadr(fd, fcu->driver);
if (fcu->driver) {
@@ -2537,7 +2546,7 @@ static void direct_link_fcurves(FileData *fd, ListBase *list)
/* modifiers */
link_list(fd, &fcu->modifiers);
direct_link_fmodifiers(fd, &fcu->modifiers);
direct_link_fmodifiers(fd, &fcu->modifiers, fcu);
}
}
@@ -2642,7 +2651,7 @@ static void direct_link_nladata_strips(FileData *fd, ListBase *list)
/* strip's F-Modifiers */
link_list(fd, &strip->modifiers);
direct_link_fmodifiers(fd, &strip->modifiers);
direct_link_fmodifiers(fd, &strip->modifiers, NULL);
}
}

View File

@@ -101,6 +101,7 @@ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_inde
fcu = MEM_callocN(sizeof(FCurve), "FCurve");
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL;
/* store path - make copy, and store that */
fcu->rna_path = BLI_strdup(rna_path);
@@ -122,7 +123,7 @@ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_inde
* Create FModifier so that old scripts won't break
* for now before 2.7 series -- (September 4, 2013)
*/
add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR);
add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR, fcu);
}
else {
/* add 2 keyframes so that user has something to work with

View File

@@ -90,7 +90,9 @@ static void delete_fmodifier_cb(bContext *C, void *fmods_v, void *fcm_v)
{
ListBase *modifiers = (ListBase *)fmods_v;
FModifier *fcm = (FModifier *)fcm_v;
FCurve *update_fcu = fcm->type == FMODIFIER_TYPE_CYCLES ? fcm->curve : NULL;
/* remove the given F-Modifier from the active modifier-stack */
remove_fmodifier(modifiers, fcm);
@@ -99,6 +101,9 @@ static void delete_fmodifier_cb(bContext *C, void *fmods_v, void *fcm_v)
/* send notifiers */
// XXX for now, this is the only way to get updates in all the right places... but would be nice to have a special one in this case
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
if (update_fcu)
calchandles_fcurve(update_fcu);
}
/* --------------- */
@@ -736,7 +741,7 @@ bool ANIM_fmodifiers_copy_to_buf(ListBase *modifiers, bool active)
/* 'Paste' the F-Modifier(s) from the buffer to the specified list
* - replace: free all the existing modifiers to leave only the pasted ones
*/
bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace)
bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace, FCurve *curve)
{
FModifier *fcm;
bool ok = false;
@@ -753,6 +758,8 @@ bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace)
for (fcm = fmodifier_copypaste_buf.first; fcm; fcm = fcm->next) {
/* make a copy of it */
FModifier *fcmN = copy_fmodifier(fcm);
fcmN->curve = curve;
/* make sure the new one isn't active, otherwise the list may get several actives */
fcmN->flag &= ~FMODIFIER_FLAG_ACTIVE;

View File

@@ -186,6 +186,7 @@ FCurve *verify_fcurve(bAction *act, const char group[], PointerRNA *ptr,
fcu = MEM_callocN(sizeof(FCurve), "FCurve");
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL;
if (BLI_listbase_is_empty(&act->curves))
fcu->flag |= FCURVE_ACTIVE; /* first one added active */

View File

@@ -592,7 +592,7 @@ static void calc_keyHandles(ListBase *nurb, float *key)
if (nextp) key_to_bezt(nextfp, nextp, &next);
if (prevp) key_to_bezt(prevfp, prevp, &prev);
BKE_nurb_handle_calc(&cur, prevp ? &prev : NULL, nextp ? &next : NULL, 0);
BKE_nurb_handle_calc(&cur, prevp ? &prev : NULL, nextp ? &next : NULL, 0, 0);
bezt_to_key(&cur, fp);
prevp = bezt;

View File

@@ -563,7 +563,7 @@ bool ANIM_fmodifiers_copy_to_buf(ListBase *modifiers, bool active);
/* 'Paste' the F-Modifier(s) from the buffer to the specified list
* - replace: free all the existing modifiers to leave only the pasted ones
*/
bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace);
bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace, struct FCurve *curve);
/* ************************************************* */
/* ASSORTED TOOLS */

View File

@@ -360,6 +360,9 @@ bool UI_context_copy_to_selected_list(
else if (RNA_struct_is_a(ptr->type, &RNA_Sequence)) {
*r_lb = CTX_data_collection_get(C, "selected_editable_sequences");
}
else if (RNA_struct_is_a(ptr->type, &RNA_FCurve)) {
*r_lb = CTX_data_collection_get(C, "selected_editable_fcurves");
}
else if (RNA_struct_is_a(ptr->type, &RNA_Node) ||
RNA_struct_is_a(ptr->type, &RNA_NodeSocket))
{

View File

@@ -1025,7 +1025,7 @@ static int followpath_path_animate_exec(bContext *C, wmOperator *op)
* and define basic slope of this curve based on the properties
*/
if (!fcu->bezt && !fcu->fpt && !fcu->modifiers.first) {
FModifier *fcm = add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR);
FModifier *fcm = add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR, fcu);
FMod_Generator *gen = fcm->data;
/* Assume that we have the following equation:

View File

@@ -637,7 +637,7 @@ bool ED_object_parent_set(ReportList *reports, Main *bmain, Scene *scene, Object
/* setup dummy 'generator' modifier here to get 1-1 correspondence still working */
if (!fcu->bezt && !fcu->fpt && !fcu->modifiers.first)
add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR);
add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR, fcu);
}
/* fall back on regular parenting now (for follow only) */

View File

@@ -54,6 +54,7 @@
#include "ED_armature.h"
#include "ED_gpencil.h"
#include "ED_anim_api.h"
#include "WM_api.h"
#include "UI_interface.h"
@@ -87,7 +88,7 @@ const char *screen_context_dir[] = {
"visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes",
"active_gpencil_layer", "active_gpencil_frame", "active_gpencil_palette",
"active_gpencil_palettecolor", "active_gpencil_brush",
"active_operator",
"active_operator", "selected_editable_fcurves",
NULL};
int ed_screen_context(const bContext *C, const char *member, bContextDataResult *result)
@@ -608,6 +609,30 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
return 1;
}
}
else if (CTX_data_equals(member, "selected_editable_fcurves"))
{
bAnimContext ac;
if (ANIM_animdata_get_context(C, &ac) && ELEM(ac.spacetype, SPACE_ACTION, SPACE_IPO)) {
bAnimListElem *ale;
ListBase anim_data = {NULL, NULL};
int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS | ANIMFILTER_SEL) |
(ac.spacetype == SPACE_IPO ? ANIMFILTER_CURVE_VISIBLE : ANIMFILTER_LIST_VISIBLE);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
if (ale->type == ANIMTYPE_FCURVE)
CTX_data_list_add(result, ale->id, &RNA_FCurve, ale->data);
}
ANIM_animdata_freelist(&anim_data);
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return 1;
}
}
else {
return 0; /* not found */
}

View File

@@ -1146,7 +1146,7 @@ static void setexpo_action_keys(bAnimContext *ac, short mode)
/* only add if one doesn't exist */
if (list_has_suitable_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, -1) == 0) {
/* TODO: add some more preset versions which set different extrapolation options? */
add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES);
add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, fcu);
}
}
else if (mode == CLEAR_CYCLIC_EXPO) {

View File

@@ -208,7 +208,12 @@ static void graph_panel_properties(const bContext *C, Panel *pa)
sub = uiLayoutRow(row, true);
uiLayoutSetEnabled(sub, (fcu->color_mode == FCURVE_COLOR_CUSTOM));
uiItemR(sub, &fcu_ptr, "color", 0, "", ICON_NONE);
/* smoothing setting */
col = uiLayoutColumn(layout, true);
uiItemL(col, IFACE_("Auto Handle Smoothing:"), ICON_NONE);
uiItemR(col, &fcu_ptr, "auto_smoothing", 0, "", ICON_NONE);
MEM_freeN(ale);
}

View File

@@ -1504,7 +1504,7 @@ static void setexpo_graph_keys(bAnimContext *ac, short mode)
/* only add if one doesn't exist */
if (list_has_suitable_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, -1) == 0) {
// TODO: add some more preset versions which set different extrapolation options?
add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES);
add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, fcu);
}
}
else if (mode == CLEAR_CYCLIC_EXPO) {
@@ -1520,7 +1520,7 @@ static void setexpo_graph_keys(bAnimContext *ac, short mode)
}
}
ale->update |= ANIM_UPDATE_DEPS;
ale->update |= ANIM_UPDATE_DEFAULT;
}
ANIM_animdata_update(ac, &anim_data);
@@ -2446,7 +2446,7 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op)
FModifier *fcm;
/* add F-Modifier of specified type to active F-Curve, and make it the active one */
fcm = add_fmodifier(&fcu->modifiers, type);
fcm = add_fmodifier(&fcu->modifiers, type, fcu);
if (fcm) {
set_active_fmodifier(&fcu->modifiers, fcm);
}
@@ -2455,7 +2455,7 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op)
break;
}
ale->update |= ANIM_UPDATE_DEPS;
ale->update |= ANIM_UPDATE_DEPS | ANIM_UPDATE_HANDLES;
}
ANIM_animdata_update(&ac, &anim_data);
@@ -2582,10 +2582,10 @@ static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op)
FCurve *fcu = (FCurve *)ale->data;
int tot;
tot = ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, replace);
tot = ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, replace, fcu);
if (tot) {
ale->update |= ANIM_UPDATE_DEPS;
ale->update |= ANIM_UPDATE_DEPS | ANIM_UPDATE_HANDLES;
ok = true;
}
}

View File

@@ -2316,7 +2316,7 @@ static int nla_fmodifier_add_exec(bContext *C, wmOperator *op)
continue;
/* add F-Modifier of specified type to selected, and make it the active one */
fcm = add_fmodifier(&strip->modifiers, type);
fcm = add_fmodifier(&strip->modifiers, type, NULL);
if (fcm) {
set_active_fmodifier(&strip->modifiers, fcm);
@@ -2470,8 +2470,8 @@ static int nla_fmodifier_paste_exec(bContext *C, wmOperator *op)
}
/* paste FModifiers from buffer */
ok += ANIM_fmodifiers_paste_from_buf(&strip->modifiers, replace);
ale->update |= ANIM_UPDATE_DEPS;
ok += ANIM_fmodifiers_paste_from_buf(&strip->modifiers, replace, NULL);
ale->update |= ANIM_UPDATE_DEPS | ANIM_UPDATE_HANDLES;
}
}

View File

@@ -52,6 +52,7 @@ extern "C" {
typedef struct FModifier {
struct FModifier *next, *prev;
struct FCurve *curve; /* containing curve, only used for updates to CYCLES */
void *data; /* pointer to modifier data */
char name[64]; /* user-defined description for the modifier - MAX_ID_NAME-2 */
@@ -489,7 +490,10 @@ typedef struct FCurve {
float curval; /* value stored from last time curve was evaluated (not threadsafe, debug display only!) */
short flag; /* user-editable settings for this curve */
short extend; /* value-extending mode for this curve (does not cover */
char auto_smoothing; /* auto-handle smoothing mode */
char pad[7];
/* RNA - data link */
int array_index; /* if applicable, the index of the RNA-array item to get */
char *rna_path; /* RNA-path to resolve data-access */
@@ -544,6 +548,12 @@ typedef enum eFCurve_Coloring {
FCURVE_COLOR_CUSTOM = 2, /* custom color */
} eFCurve_Coloring;
/* curve smoothing modes */
typedef enum eFCurve_Smoothing {
FCURVE_SMOOTH_NONE = 0, /* legacy mode: auto handles only consider adjacent points */
FCURVE_SMOOTH_CONT_ACCEL = 1, /* maintain continuity of the acceleration */
} eFCurve_Smoothing;
/* ************************************************ */
/* 'Action' Datatypes */

View File

@@ -124,7 +124,8 @@ typedef struct BezTriple {
float back; /* BEZT_IPO_BACK */
float amplitude, period; /* BEZT_IPO_ELASTIC */
char pad[4];
char f5; /* f5: used for auto handle to distinguish between normal handle and exception (extrema) */
char pad[3];
} BezTriple;
/* note; alfa location in struct is abused by Key system */
@@ -389,6 +390,12 @@ typedef enum eBezTriple_Handle {
HD_ALIGN_DOUBLESIDE = 5, /* align handles, displayed both of them. used for masks */
} eBezTriple_Handle;
/* f5 (beztriple) */
typedef enum eBezTriple_Auto_Type {
HD_AUTOTYPE_NORMAL = 0,
HD_AUTOTYPE_SPECIAL = 1
} eBezTriple_Auto_Type;
/* interpolation modes (used only for BezTriple->ipo) */
typedef enum eBezTriple_Interpolation {
/* traditional interpolation */
@@ -436,6 +443,8 @@ typedef enum eBezTriple_KeyframeType {
#define BEZT_SEL_ALL(bezt) { (bezt)->f1 |= SELECT; (bezt)->f2 |= SELECT; (bezt)->f3 |= SELECT; } ((void)0)
#define BEZT_DESEL_ALL(bezt) { (bezt)->f1 &= ~SELECT; (bezt)->f2 &= ~SELECT; (bezt)->f3 &= ~SELECT; } ((void)0)
#define BEZT_IS_AUTOH(bezt) (ELEM((bezt)->h1, HD_AUTO, HD_AUTO_ANIM) && ELEM((bezt)->h2, HD_AUTO, HD_AUTO_ANIM))
/* *************** CHARINFO **************** */
/* CharInfo.flag */

View File

@@ -495,7 +495,10 @@ static void rna_FCurve_active_modifier_set(PointerRNA *ptr, PointerRNA value)
static FModifier *rna_FCurve_modifiers_new(FCurve *fcu, int type)
{
return add_fmodifier(&fcu->modifiers, type);
FModifier *fcm = add_fmodifier(&fcu->modifiers, type, fcu);
if (type == FMODIFIER_TYPE_CYCLES)
calchandles_fcurve(fcu);
return fcm;
}
static void rna_FCurve_modifiers_remove(FCurve *fcu, ReportList *reports, PointerRNA *fcm_ptr)
@@ -506,8 +509,13 @@ static void rna_FCurve_modifiers_remove(FCurve *fcu, ReportList *reports, Pointe
return;
}
bool update = (fcm->type == FMODIFIER_TYPE_CYCLES);
remove_fmodifier(&fcu->modifiers, fcm);
RNA_POINTER_INVALIDATE(fcm_ptr);
if (update)
calchandles_fcurve(fcu);
}
static void rna_FModifier_active_set(PointerRNA *ptr, int UNUSED(value))
@@ -586,11 +594,15 @@ static void rna_FModifier_blending_range(PointerRNA *ptr, float *min, float *max
static void rna_FModifier_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
ID *id = ptr->id.data;
FModifier *fcm = (FModifier *)ptr->data;
AnimData *adt = BKE_animdata_from_id(id);
DAG_id_tag_update(id, (GS(id->name) == ID_OB) ? OB_RECALC_OB : OB_RECALC_DATA);
if (adt != NULL) {
adt->recalc |= ADT_RECALC_ANIM;
}
if (fcm->curve && fcm->type == FMODIFIER_TYPE_CYCLES) {
calchandles_fcurve(fcm->curve);
}
}
static void rna_FModifier_verify_data_update(Main *bmain, Scene *scene, PointerRNA *ptr)
@@ -1891,6 +1903,11 @@ static void rna_def_fcurve(BlenderRNA *brna)
"Use custom hand-picked color for F-Curve"},
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem prop_mode_smoothing_items[] = {
{FCURVE_SMOOTH_NONE, "NONE", 0, "None", "Auto handles only take adjacent keys into account (legacy mode)"},
{FCURVE_SMOOTH_CONT_ACCEL, "CONT_ACCEL", 0, "Continuous Acceleration", "Auto handles are placed to avoid jumps in acceleration"},
{0, NULL, 0, NULL, NULL}
};
srna = RNA_def_struct(brna, "FCurve", NULL);
RNA_def_struct_ui_text(srna, "F-Curve", "F-Curve defining values of a period of time");
@@ -1964,6 +1981,11 @@ static void rna_def_fcurve(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Hide", "F-Curve and its keyframes are hidden in the Graph Editor graphs");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL);
prop = RNA_def_property(srna, "auto_smoothing", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, prop_mode_smoothing_items);
RNA_def_property_ui_text(prop, "Auto Handle Smoothing", "Algorithm used to compute automatic handles");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FCurve_update_data");
/* State Info (for Debugging) */
prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FCURVE_DISABLED);