Compare commits
4 Commits
temp-sprea
...
smooth-fcu
Author | SHA1 | Date | |
---|---|---|---|
eea32ffa4d | |||
644a5ed7ef | |||
7a765e7afa | |||
0d814a2e49 |
@@ -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_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,
|
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(struct Nurb *nu, struct BezTriple *bezt);
|
||||||
void BKE_nurb_handle_calc_simple_auto(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_calc(struct Nurb *nu);
|
||||||
void BKE_nurb_handles_autocalc(struct Nurb *nu, int flag);
|
void BKE_nurb_handles_autocalc(struct Nurb *nu, int flag);
|
||||||
void BKE_nurb_bezt_handle_test(struct BezTriple *bezt, const bool use_handle);
|
void BKE_nurb_bezt_handle_test(struct BezTriple *bezt, const bool use_handle);
|
||||||
|
@@ -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);
|
struct FModifier *copy_fmodifier(const struct FModifier *src);
|
||||||
void copy_fmodifiers(ListBase *dst, const ListBase *src);
|
void copy_fmodifiers(ListBase *dst, const ListBase *src);
|
||||||
bool remove_fmodifier(ListBase *modifiers, struct FModifier *fcm);
|
bool remove_fmodifier(ListBase *modifiers, struct FModifier *fcm);
|
||||||
|
@@ -41,6 +41,7 @@
|
|||||||
#include "BLI_utildefines.h"
|
#include "BLI_utildefines.h"
|
||||||
#include "BLI_ghash.h"
|
#include "BLI_ghash.h"
|
||||||
|
|
||||||
|
#include "DNA_anim_types.h"
|
||||||
#include "DNA_curve_types.h"
|
#include "DNA_curve_types.h"
|
||||||
#include "DNA_material_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(
|
static void calchandleNurb_intern(
|
||||||
BezTriple *bezt, const BezTriple *prev, const BezTriple *next,
|
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 */
|
/* defines to avoid confusion */
|
||||||
#define p2_h1 ((p2) - 3)
|
#define p2_h1 ((p2) - 3)
|
||||||
@@ -3148,6 +3149,9 @@ static void calchandleNurb_intern(
|
|||||||
float len_ratio;
|
float len_ratio;
|
||||||
const float eps = 1e-5;
|
const float eps = 1e-5;
|
||||||
|
|
||||||
|
/* assume normal handle until we check */
|
||||||
|
bezt->f5 = HD_AUTOTYPE_NORMAL;
|
||||||
|
|
||||||
if (bezt->h1 == 0 && bezt->h2 == 0) {
|
if (bezt->h1 == 0 && bezt->h2 == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3199,7 +3203,13 @@ static void calchandleNurb_intern(
|
|||||||
tvec[2] = dvec_b[2] / len_b + dvec_a[2] / len_a;
|
tvec[2] = dvec_b[2] / len_b + dvec_a[2] / len_a;
|
||||||
|
|
||||||
if (is_fcurve) {
|
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 {
|
else {
|
||||||
len = len_v3(tvec);
|
len = len_v3(tvec);
|
||||||
@@ -3210,10 +3220,12 @@ static void calchandleNurb_intern(
|
|||||||
/* only for fcurves */
|
/* only for fcurves */
|
||||||
bool leftviolate = false, rightviolate = false;
|
bool leftviolate = false, rightviolate = false;
|
||||||
|
|
||||||
if (len_a > 5.0f * len_b)
|
if (!is_fcurve || fcurve_smoothing == FCURVE_SMOOTH_NONE) {
|
||||||
len_a = 5.0f * len_b;
|
if (len_a > 5.0f * len_b)
|
||||||
if (len_b > 5.0f * len_a)
|
len_a = 5.0f * len_b;
|
||||||
len_b = 5.0f * len_a;
|
if (len_b > 5.0f * len_a)
|
||||||
|
len_b = 5.0f * len_a;
|
||||||
|
}
|
||||||
|
|
||||||
if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM)) {
|
if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM)) {
|
||||||
len_a /= len;
|
len_a /= len;
|
||||||
@@ -3224,6 +3236,7 @@ static void calchandleNurb_intern(
|
|||||||
float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
|
float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
|
||||||
if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) {
|
if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) {
|
||||||
bezt->vec[0][1] = bezt->vec[1][1];
|
bezt->vec[0][1] = bezt->vec[1][1];
|
||||||
|
bezt->f5 = HD_AUTOTYPE_SPECIAL;
|
||||||
}
|
}
|
||||||
else { /* handles should not be beyond y coord of two others */
|
else { /* handles should not be beyond y coord of two others */
|
||||||
if (ydiff1 <= 0.0f) {
|
if (ydiff1 <= 0.0f) {
|
||||||
@@ -3250,6 +3263,7 @@ static void calchandleNurb_intern(
|
|||||||
float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
|
float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
|
||||||
if ( (ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f) ) {
|
if ( (ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f) ) {
|
||||||
bezt->vec[2][1] = bezt->vec[1][1];
|
bezt->vec[2][1] = bezt->vec[1][1];
|
||||||
|
bezt->f5 = HD_AUTOTYPE_SPECIAL;
|
||||||
}
|
}
|
||||||
else { /* handles should not be beyond y coord of two others */
|
else { /* handles should not be beyond y coord of two others */
|
||||||
if (ydiff1 <= 0.0f) {
|
if (ydiff1 <= 0.0f) {
|
||||||
@@ -3397,7 +3411,7 @@ static void calchandlesNurb_intern(Nurb *nu, bool skip_align)
|
|||||||
next = bezt + 1;
|
next = bezt + 1;
|
||||||
|
|
||||||
while (a--) {
|
while (a--) {
|
||||||
calchandleNurb_intern(bezt, prev, next, 0, skip_align);
|
calchandleNurb_intern(bezt, prev, next, 0, skip_align, 0);
|
||||||
prev = bezt;
|
prev = bezt;
|
||||||
if (a == 1) {
|
if (a == 1) {
|
||||||
if (nu->flagu & CU_NURB_CYCLIC)
|
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 */
|
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) {
|
if (nu->pntsu > 1) {
|
||||||
BezTriple *prev = BKE_nurb_bezt_get_prev(nu, bezt);
|
BezTriple *prev = BKE_nurb_bezt_get_prev(nu, bezt);
|
||||||
BezTriple *next = BKE_nurb_bezt_get_next(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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -882,6 +882,44 @@ void fcurve_store_samples(FCurve *fcu, void *data, int start, int end, FcuSample
|
|||||||
* that the handles are correctly
|
* 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
|
/* This function recalculates the handles of an F-Curve
|
||||||
* If the BezTriples have been rearranged, sort them first before using this.
|
* 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)*/)
|
if (ELEM(NULL, fcu, fcu->bezt) || (a < 2) /*|| ELEM(fcu->ipo, BEZT_IPO_CONST, BEZT_IPO_LIN)*/)
|
||||||
return;
|
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 */
|
/* get initial pointers */
|
||||||
bezt = fcu->bezt;
|
bezt = fcu->bezt;
|
||||||
prev = NULL;
|
prev = cycle_offset_triple(cycle, &tmp, &fcu->bezt[fcu->totvert-2], last, first);
|
||||||
next = (bezt + 1);
|
next = (bezt + 1);
|
||||||
|
|
||||||
/* loop over all beztriples, adjusting handles */
|
/* 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];
|
if (bezt->vec[2][0] < bezt->vec[1][0]) bezt->vec[2][0] = bezt->vec[1][0];
|
||||||
|
|
||||||
/* calculate auto-handles */
|
/* 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 */
|
/* 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 */
|
/* only do this on first or last beztriple */
|
||||||
if ((a == 0) || (a == fcu->totvert - 1)) {
|
if ((a == 0) || (a == fcu->totvert - 1)) {
|
||||||
/* set both handles to have same horizontal value as keyframe */
|
/* set both handles to have same horizontal value as keyframe */
|
||||||
if (fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) {
|
if (fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) {
|
||||||
bezt->vec[0][1] = bezt->vec[2][1] = bezt->vec[1][1];
|
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 */
|
/* advance pointers for next iteration */
|
||||||
prev = bezt;
|
prev = bezt;
|
||||||
if (a == 1) next = NULL;
|
if (a == 1)
|
||||||
|
next = cycle_offset_triple(cycle, &tmp, &fcu->bezt[1], first, last);
|
||||||
else next++;
|
else next++;
|
||||||
bezt++;
|
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)
|
void testhandles_fcurve(FCurve *fcu, const bool use_handle)
|
||||||
|
@@ -1077,7 +1077,7 @@ const FModifierTypeInfo *fmodifier_get_typeinfo(const FModifier *fcm)
|
|||||||
/* API --------------------------- */
|
/* API --------------------------- */
|
||||||
|
|
||||||
/* Add a new F-Curve Modifier to the given F-Curve of a certain type */
|
/* 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);
|
const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(type);
|
||||||
FModifier *fcm;
|
FModifier *fcm;
|
||||||
@@ -1098,6 +1098,7 @@ FModifier *add_fmodifier(ListBase *modifiers, int type)
|
|||||||
fcm = MEM_callocN(sizeof(FModifier), "F-Curve Modifier");
|
fcm = MEM_callocN(sizeof(FModifier), "F-Curve Modifier");
|
||||||
fcm->type = type;
|
fcm->type = type;
|
||||||
fcm->flag = FMODIFIER_FLAG_EXPANDED;
|
fcm->flag = FMODIFIER_FLAG_EXPANDED;
|
||||||
|
fcm->curve = owner_fcu;
|
||||||
fcm->influence = 1.0f;
|
fcm->influence = 1.0f;
|
||||||
BLI_addtail(modifiers, fcm);
|
BLI_addtail(modifiers, fcm);
|
||||||
|
|
||||||
@@ -1129,6 +1130,7 @@ FModifier *copy_fmodifier(const FModifier *src)
|
|||||||
/* copy the base data, clearing the links */
|
/* copy the base data, clearing the links */
|
||||||
dst = MEM_dupallocN(src);
|
dst = MEM_dupallocN(src);
|
||||||
dst->next = dst->prev = NULL;
|
dst->next = dst->prev = NULL;
|
||||||
|
dst->curve = NULL;
|
||||||
|
|
||||||
/* make a new copy of the F-Modifier's data */
|
/* make a new copy of the F-Modifier's data */
|
||||||
dst->data = MEM_dupallocN(src->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 */
|
/* make a new copy of the F-Modifier's data */
|
||||||
fcm->data = MEM_dupallocN(fcm->data);
|
fcm->data = MEM_dupallocN(fcm->data);
|
||||||
|
fcm->curve = NULL;
|
||||||
|
|
||||||
/* only do specific constraints if required */
|
/* only do specific constraints if required */
|
||||||
if (fmi && fmi->copy_data)
|
if (fmi && fmi->copy_data)
|
||||||
|
@@ -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
|
/* Add a new FModifier (Cyclic) instead of setting extend value
|
||||||
* as that's the new equivalent of that option.
|
* 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;
|
FMod_Cycles *data = (FMod_Cycles *)fcm->data;
|
||||||
|
|
||||||
/* if 'offset' one is in use, set appropriate settings */
|
/* if 'offset' one is in use, set appropriate settings */
|
||||||
|
@@ -1204,14 +1204,14 @@ static void mask_calc_point_handle(MaskSplinePoint *point, MaskSplinePoint *poin
|
|||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
if (bezt_prev || bezt_next) {
|
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
|
#else
|
||||||
if (handle_type == HD_VECT) {
|
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) {
|
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) {
|
else if (handle_type == HD_ALIGN || handle_type == HD_ALIGN_DOUBLESIDE) {
|
||||||
float v1[3], v2[3];
|
float v1[3], v2[3];
|
||||||
|
@@ -1387,6 +1387,7 @@ void BKE_nlastrip_validate_fcurves(NlaStrip *strip)
|
|||||||
|
|
||||||
/* set default flags */
|
/* set default flags */
|
||||||
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
|
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
|
||||||
|
fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL;
|
||||||
|
|
||||||
/* store path - make copy, and store that */
|
/* store path - make copy, and store that */
|
||||||
fcu->rna_path = BLI_strdupn("influence", 9);
|
fcu->rna_path = BLI_strdupn("influence", 9);
|
||||||
@@ -1408,6 +1409,7 @@ void BKE_nlastrip_validate_fcurves(NlaStrip *strip)
|
|||||||
|
|
||||||
/* set default flags */
|
/* set default flags */
|
||||||
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
|
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
|
||||||
|
fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL;
|
||||||
|
|
||||||
/* store path - make copy, and store that */
|
/* store path - make copy, and store that */
|
||||||
fcu->rna_path = BLI_strdupn("strip_time", 10);
|
fcu->rna_path = BLI_strdupn("strip_time", 10);
|
||||||
|
@@ -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]);
|
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 ******************************/
|
/**************************** Inline Definitions ******************************/
|
||||||
#if 0 /* None so far. */
|
#if 0 /* None so far. */
|
||||||
# if BLI_MATH_DO_INLINE
|
# if BLI_MATH_DO_INLINE
|
||||||
|
@@ -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);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -2389,11 +2389,13 @@ static void lib_link_constraint_channels(FileData *fd, ID *id, ListBase *chanbas
|
|||||||
|
|
||||||
/* Data Linking ----------------------------- */
|
/* 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;
|
FModifier *fcm;
|
||||||
|
|
||||||
for (fcm = list->first; fcm; fcm = fcm->next) {
|
for (fcm = list->first; fcm; fcm = fcm->next) {
|
||||||
|
fcm->curve = curve;
|
||||||
|
|
||||||
/* data for specific modifiers */
|
/* data for specific modifiers */
|
||||||
switch (fcm->type) {
|
switch (fcm->type) {
|
||||||
case FMODIFIER_TYPE_PYTHON:
|
case FMODIFIER_TYPE_PYTHON:
|
||||||
@@ -2435,19 +2437,20 @@ static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* modifiers */
|
/* 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 */
|
/* 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;
|
FModifier *fcm;
|
||||||
|
|
||||||
for (fcm = list->first; fcm; fcm = fcm->next) {
|
for (fcm = list->first; fcm; fcm = fcm->next) {
|
||||||
/* relink general data */
|
/* relink general data */
|
||||||
fcm->data = newdataadr(fd, fcm->data);
|
fcm->data = newdataadr(fd, fcm->data);
|
||||||
|
fcm->curve = curve;
|
||||||
|
|
||||||
/* do relinking of data for specific types */
|
/* do relinking of data for specific types */
|
||||||
switch (fcm->type) {
|
switch (fcm->type) {
|
||||||
@@ -2506,6 +2509,12 @@ static void direct_link_fcurves(FileData *fd, ListBase *list)
|
|||||||
*/
|
*/
|
||||||
fcu->flag &= ~FCURVE_DISABLED;
|
fcu->flag &= ~FCURVE_DISABLED;
|
||||||
|
|
||||||
|
/* TEMPORARY HACK */
|
||||||
|
if (fcu->flag & (1<<13)) {
|
||||||
|
fcu->flag &= ~(1<<13);
|
||||||
|
fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL;
|
||||||
|
}
|
||||||
|
|
||||||
/* driver */
|
/* driver */
|
||||||
fcu->driver= newdataadr(fd, fcu->driver);
|
fcu->driver= newdataadr(fd, fcu->driver);
|
||||||
if (fcu->driver) {
|
if (fcu->driver) {
|
||||||
@@ -2537,7 +2546,7 @@ static void direct_link_fcurves(FileData *fd, ListBase *list)
|
|||||||
|
|
||||||
/* modifiers */
|
/* modifiers */
|
||||||
link_list(fd, &fcu->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 */
|
/* strip's F-Modifiers */
|
||||||
link_list(fd, &strip->modifiers);
|
link_list(fd, &strip->modifiers);
|
||||||
direct_link_fmodifiers(fd, &strip->modifiers);
|
direct_link_fmodifiers(fd, &strip->modifiers, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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 = MEM_callocN(sizeof(FCurve), "FCurve");
|
||||||
|
|
||||||
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
|
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
|
||||||
|
fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL;
|
||||||
|
|
||||||
/* store path - make copy, and store that */
|
/* store path - make copy, and store that */
|
||||||
fcu->rna_path = BLI_strdup(rna_path);
|
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
|
* Create FModifier so that old scripts won't break
|
||||||
* for now before 2.7 series -- (September 4, 2013)
|
* 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 {
|
else {
|
||||||
/* add 2 keyframes so that user has something to work with
|
/* add 2 keyframes so that user has something to work with
|
||||||
|
@@ -90,7 +90,9 @@ static void delete_fmodifier_cb(bContext *C, void *fmods_v, void *fcm_v)
|
|||||||
{
|
{
|
||||||
ListBase *modifiers = (ListBase *)fmods_v;
|
ListBase *modifiers = (ListBase *)fmods_v;
|
||||||
FModifier *fcm = (FModifier *)fcm_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 the given F-Modifier from the active modifier-stack */
|
||||||
remove_fmodifier(modifiers, fcm);
|
remove_fmodifier(modifiers, fcm);
|
||||||
|
|
||||||
@@ -99,6 +101,9 @@ static void delete_fmodifier_cb(bContext *C, void *fmods_v, void *fcm_v)
|
|||||||
/* send notifiers */
|
/* 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
|
// 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);
|
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
|
/* 'Paste' the F-Modifier(s) from the buffer to the specified list
|
||||||
* - replace: free all the existing modifiers to leave only the pasted ones
|
* - 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;
|
FModifier *fcm;
|
||||||
bool ok = false;
|
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) {
|
for (fcm = fmodifier_copypaste_buf.first; fcm; fcm = fcm->next) {
|
||||||
/* make a copy of it */
|
/* make a copy of it */
|
||||||
FModifier *fcmN = copy_fmodifier(fcm);
|
FModifier *fcmN = copy_fmodifier(fcm);
|
||||||
|
|
||||||
|
fcmN->curve = curve;
|
||||||
|
|
||||||
/* make sure the new one isn't active, otherwise the list may get several actives */
|
/* make sure the new one isn't active, otherwise the list may get several actives */
|
||||||
fcmN->flag &= ~FMODIFIER_FLAG_ACTIVE;
|
fcmN->flag &= ~FMODIFIER_FLAG_ACTIVE;
|
||||||
|
@@ -186,6 +186,7 @@ FCurve *verify_fcurve(bAction *act, const char group[], PointerRNA *ptr,
|
|||||||
fcu = MEM_callocN(sizeof(FCurve), "FCurve");
|
fcu = MEM_callocN(sizeof(FCurve), "FCurve");
|
||||||
|
|
||||||
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
|
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
|
||||||
|
fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL;
|
||||||
if (BLI_listbase_is_empty(&act->curves))
|
if (BLI_listbase_is_empty(&act->curves))
|
||||||
fcu->flag |= FCURVE_ACTIVE; /* first one added active */
|
fcu->flag |= FCURVE_ACTIVE; /* first one added active */
|
||||||
|
|
||||||
|
@@ -592,7 +592,7 @@ static void calc_keyHandles(ListBase *nurb, float *key)
|
|||||||
if (nextp) key_to_bezt(nextfp, nextp, &next);
|
if (nextp) key_to_bezt(nextfp, nextp, &next);
|
||||||
if (prevp) key_to_bezt(prevfp, prevp, &prev);
|
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);
|
bezt_to_key(&cur, fp);
|
||||||
|
|
||||||
prevp = bezt;
|
prevp = bezt;
|
||||||
|
@@ -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
|
/* 'Paste' the F-Modifier(s) from the buffer to the specified list
|
||||||
* - replace: free all the existing modifiers to leave only the pasted ones
|
* - 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 */
|
/* ASSORTED TOOLS */
|
||||||
|
@@ -360,6 +360,9 @@ bool UI_context_copy_to_selected_list(
|
|||||||
else if (RNA_struct_is_a(ptr->type, &RNA_Sequence)) {
|
else if (RNA_struct_is_a(ptr->type, &RNA_Sequence)) {
|
||||||
*r_lb = CTX_data_collection_get(C, "selected_editable_sequences");
|
*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) ||
|
else if (RNA_struct_is_a(ptr->type, &RNA_Node) ||
|
||||||
RNA_struct_is_a(ptr->type, &RNA_NodeSocket))
|
RNA_struct_is_a(ptr->type, &RNA_NodeSocket))
|
||||||
{
|
{
|
||||||
|
@@ -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
|
* and define basic slope of this curve based on the properties
|
||||||
*/
|
*/
|
||||||
if (!fcu->bezt && !fcu->fpt && !fcu->modifiers.first) {
|
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;
|
FMod_Generator *gen = fcm->data;
|
||||||
|
|
||||||
/* Assume that we have the following equation:
|
/* Assume that we have the following equation:
|
||||||
|
@@ -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 */
|
/* setup dummy 'generator' modifier here to get 1-1 correspondence still working */
|
||||||
if (!fcu->bezt && !fcu->fpt && !fcu->modifiers.first)
|
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) */
|
/* fall back on regular parenting now (for follow only) */
|
||||||
|
@@ -54,6 +54,7 @@
|
|||||||
|
|
||||||
#include "ED_armature.h"
|
#include "ED_armature.h"
|
||||||
#include "ED_gpencil.h"
|
#include "ED_gpencil.h"
|
||||||
|
#include "ED_anim_api.h"
|
||||||
|
|
||||||
#include "WM_api.h"
|
#include "WM_api.h"
|
||||||
#include "UI_interface.h"
|
#include "UI_interface.h"
|
||||||
@@ -87,7 +88,7 @@ const char *screen_context_dir[] = {
|
|||||||
"visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes",
|
"visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes",
|
||||||
"active_gpencil_layer", "active_gpencil_frame", "active_gpencil_palette",
|
"active_gpencil_layer", "active_gpencil_frame", "active_gpencil_palette",
|
||||||
"active_gpencil_palettecolor", "active_gpencil_brush",
|
"active_gpencil_palettecolor", "active_gpencil_brush",
|
||||||
"active_operator",
|
"active_operator", "selected_editable_fcurves",
|
||||||
NULL};
|
NULL};
|
||||||
|
|
||||||
int ed_screen_context(const bContext *C, const char *member, bContextDataResult *result)
|
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;
|
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 {
|
else {
|
||||||
return 0; /* not found */
|
return 0; /* not found */
|
||||||
}
|
}
|
||||||
|
@@ -1146,7 +1146,7 @@ static void setexpo_action_keys(bAnimContext *ac, short mode)
|
|||||||
/* only add if one doesn't exist */
|
/* only add if one doesn't exist */
|
||||||
if (list_has_suitable_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, -1) == 0) {
|
if (list_has_suitable_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, -1) == 0) {
|
||||||
/* TODO: add some more preset versions which set different extrapolation options? */
|
/* 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) {
|
else if (mode == CLEAR_CYCLIC_EXPO) {
|
||||||
|
@@ -208,7 +208,12 @@ static void graph_panel_properties(const bContext *C, Panel *pa)
|
|||||||
sub = uiLayoutRow(row, true);
|
sub = uiLayoutRow(row, true);
|
||||||
uiLayoutSetEnabled(sub, (fcu->color_mode == FCURVE_COLOR_CUSTOM));
|
uiLayoutSetEnabled(sub, (fcu->color_mode == FCURVE_COLOR_CUSTOM));
|
||||||
uiItemR(sub, &fcu_ptr, "color", 0, "", ICON_NONE);
|
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);
|
MEM_freeN(ale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1504,7 +1504,7 @@ static void setexpo_graph_keys(bAnimContext *ac, short mode)
|
|||||||
/* only add if one doesn't exist */
|
/* only add if one doesn't exist */
|
||||||
if (list_has_suitable_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, -1) == 0) {
|
if (list_has_suitable_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, -1) == 0) {
|
||||||
// TODO: add some more preset versions which set different extrapolation options?
|
// 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) {
|
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);
|
ANIM_animdata_update(ac, &anim_data);
|
||||||
@@ -2446,7 +2446,7 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op)
|
|||||||
FModifier *fcm;
|
FModifier *fcm;
|
||||||
|
|
||||||
/* add F-Modifier of specified type to active F-Curve, and make it the active one */
|
/* 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) {
|
if (fcm) {
|
||||||
set_active_fmodifier(&fcu->modifiers, fcm);
|
set_active_fmodifier(&fcu->modifiers, fcm);
|
||||||
}
|
}
|
||||||
@@ -2455,7 +2455,7 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ale->update |= ANIM_UPDATE_DEPS;
|
ale->update |= ANIM_UPDATE_DEPS | ANIM_UPDATE_HANDLES;
|
||||||
}
|
}
|
||||||
|
|
||||||
ANIM_animdata_update(&ac, &anim_data);
|
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;
|
FCurve *fcu = (FCurve *)ale->data;
|
||||||
int tot;
|
int tot;
|
||||||
|
|
||||||
tot = ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, replace);
|
tot = ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, replace, fcu);
|
||||||
|
|
||||||
if (tot) {
|
if (tot) {
|
||||||
ale->update |= ANIM_UPDATE_DEPS;
|
ale->update |= ANIM_UPDATE_DEPS | ANIM_UPDATE_HANDLES;
|
||||||
ok = true;
|
ok = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2316,7 +2316,7 @@ static int nla_fmodifier_add_exec(bContext *C, wmOperator *op)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* add F-Modifier of specified type to selected, and make it the active one */
|
/* 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) {
|
if (fcm) {
|
||||||
set_active_fmodifier(&strip->modifiers, 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 */
|
/* paste FModifiers from buffer */
|
||||||
ok += ANIM_fmodifiers_paste_from_buf(&strip->modifiers, replace);
|
ok += ANIM_fmodifiers_paste_from_buf(&strip->modifiers, replace, NULL);
|
||||||
ale->update |= ANIM_UPDATE_DEPS;
|
ale->update |= ANIM_UPDATE_DEPS | ANIM_UPDATE_HANDLES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -52,6 +52,7 @@ extern "C" {
|
|||||||
typedef struct FModifier {
|
typedef struct FModifier {
|
||||||
struct FModifier *next, *prev;
|
struct FModifier *next, *prev;
|
||||||
|
|
||||||
|
struct FCurve *curve; /* containing curve, only used for updates to CYCLES */
|
||||||
void *data; /* pointer to modifier data */
|
void *data; /* pointer to modifier data */
|
||||||
|
|
||||||
char name[64]; /* user-defined description for the modifier - MAX_ID_NAME-2 */
|
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!) */
|
float curval; /* value stored from last time curve was evaluated (not threadsafe, debug display only!) */
|
||||||
short flag; /* user-editable settings for this curve */
|
short flag; /* user-editable settings for this curve */
|
||||||
short extend; /* value-extending mode for this curve (does not cover */
|
short extend; /* value-extending mode for this curve (does not cover */
|
||||||
|
char auto_smoothing; /* auto-handle smoothing mode */
|
||||||
|
|
||||||
|
char pad[7];
|
||||||
|
|
||||||
/* RNA - data link */
|
/* RNA - data link */
|
||||||
int array_index; /* if applicable, the index of the RNA-array item to get */
|
int array_index; /* if applicable, the index of the RNA-array item to get */
|
||||||
char *rna_path; /* RNA-path to resolve data-access */
|
char *rna_path; /* RNA-path to resolve data-access */
|
||||||
@@ -544,6 +548,12 @@ typedef enum eFCurve_Coloring {
|
|||||||
FCURVE_COLOR_CUSTOM = 2, /* custom color */
|
FCURVE_COLOR_CUSTOM = 2, /* custom color */
|
||||||
} eFCurve_Coloring;
|
} 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 */
|
/* 'Action' Datatypes */
|
||||||
|
|
||||||
|
@@ -124,7 +124,8 @@ typedef struct BezTriple {
|
|||||||
float back; /* BEZT_IPO_BACK */
|
float back; /* BEZT_IPO_BACK */
|
||||||
float amplitude, period; /* BEZT_IPO_ELASTIC */
|
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;
|
} BezTriple;
|
||||||
|
|
||||||
/* note; alfa location in struct is abused by Key system */
|
/* 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 */
|
HD_ALIGN_DOUBLESIDE = 5, /* align handles, displayed both of them. used for masks */
|
||||||
} eBezTriple_Handle;
|
} 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) */
|
/* interpolation modes (used only for BezTriple->ipo) */
|
||||||
typedef enum eBezTriple_Interpolation {
|
typedef enum eBezTriple_Interpolation {
|
||||||
/* traditional 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_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_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 **************** */
|
||||||
|
|
||||||
/* CharInfo.flag */
|
/* CharInfo.flag */
|
||||||
|
@@ -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)
|
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)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool update = (fcm->type == FMODIFIER_TYPE_CYCLES);
|
||||||
|
|
||||||
remove_fmodifier(&fcu->modifiers, fcm);
|
remove_fmodifier(&fcu->modifiers, fcm);
|
||||||
RNA_POINTER_INVALIDATE(fcm_ptr);
|
RNA_POINTER_INVALIDATE(fcm_ptr);
|
||||||
|
|
||||||
|
if (update)
|
||||||
|
calchandles_fcurve(fcu);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_FModifier_active_set(PointerRNA *ptr, int UNUSED(value))
|
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)
|
static void rna_FModifier_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
|
||||||
{
|
{
|
||||||
ID *id = ptr->id.data;
|
ID *id = ptr->id.data;
|
||||||
|
FModifier *fcm = (FModifier *)ptr->data;
|
||||||
AnimData *adt = BKE_animdata_from_id(id);
|
AnimData *adt = BKE_animdata_from_id(id);
|
||||||
DAG_id_tag_update(id, (GS(id->name) == ID_OB) ? OB_RECALC_OB : OB_RECALC_DATA);
|
DAG_id_tag_update(id, (GS(id->name) == ID_OB) ? OB_RECALC_OB : OB_RECALC_DATA);
|
||||||
if (adt != NULL) {
|
if (adt != NULL) {
|
||||||
adt->recalc |= ADT_RECALC_ANIM;
|
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)
|
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"},
|
"Use custom hand-picked color for F-Curve"},
|
||||||
{0, NULL, 0, NULL, NULL}
|
{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);
|
srna = RNA_def_struct(brna, "FCurve", NULL);
|
||||||
RNA_def_struct_ui_text(srna, "F-Curve", "F-Curve defining values of a period of time");
|
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_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);
|
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) */
|
/* State Info (for Debugging) */
|
||||||
prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE);
|
prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE);
|
||||||
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FCURVE_DISABLED);
|
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FCURVE_DISABLED);
|
||||||
|
Reference in New Issue
Block a user