diff options
author | Campbell Barton <ideasman42@gmail.com> | 2017-11-02 07:44:33 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2017-11-02 07:45:19 +0300 |
commit | 4a85089abe6d5abb9f6bb0d8255eba33dc030fcb (patch) | |
tree | 25459e54af9cf6712ca349b924319939ba853909 /source/blender/blenkernel | |
parent | cf6e45b5224d16263d7c87411a2ff71ed928410f (diff) | |
parent | 765e28948e706d8fce97c23d4d050a607971488b (diff) |
Merge branch 'master' into blender2.8
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_curve.h | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/action.c | 5 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/armature.c | 6 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/curve.c | 605 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/fcurve.c | 27 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/mask.c | 6 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/nla.c | 2 |
7 files changed, 617 insertions, 38 deletions
diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index fd5b20f3e38..fbd05552255 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -204,10 +204,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); diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 6f7b3286e40..88c0aa6f35a 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -876,6 +876,8 @@ static void copy_pose_channel_data(bPoseChannel *pchan, const bPoseChannel *chan pchan->curveInY = chan->curveInY; pchan->curveOutX = chan->curveOutX; pchan->curveOutY = chan->curveOutY; + pchan->ease1 = chan->ease1; + pchan->ease2 = chan->ease2; pchan->scaleIn = chan->scaleIn; pchan->scaleOut = chan->scaleOut; @@ -1367,6 +1369,7 @@ void BKE_pose_rest(bPose *pose) pchan->roll1 = pchan->roll2 = 0.0f; pchan->curveInX = pchan->curveInY = 0.0f; pchan->curveOutX = pchan->curveOutY = 0.0f; + pchan->ease1 = pchan->ease2 = 0.0f; pchan->scaleIn = pchan->scaleOut = 1.0f; pchan->flag &= ~(POSE_LOC | POSE_ROT | POSE_SIZE | POSE_BBONE_SHAPE); @@ -1410,6 +1413,8 @@ bool BKE_pose_copy_result(bPose *to, bPose *from) pchanto->curveInY = pchanfrom->curveInY; pchanto->curveOutX = pchanfrom->curveOutX; pchanto->curveOutY = pchanfrom->curveOutY; + pchanto->ease1 = pchanfrom->ease1; + pchanto->ease2 = pchanfrom->ease2; pchanto->scaleIn = pchanfrom->scaleIn; pchanto->scaleOut = pchanfrom->scaleOut; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 7dfc4df114c..e45e0596f3a 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -613,8 +613,10 @@ void b_bone_spline_setup(bPoseChannel *pchan, int rest, Mat4 result_array[MAX_BB { const float circle_factor = length * (cubic_tangent_factor_circle_v3(h1, h2) / 0.75f); - const float hlength1 = bone->ease1 * circle_factor; - const float hlength2 = bone->ease2 * circle_factor; + const float combined_ease1 = bone->ease1 + (!rest ? pchan->ease1 : 0.0f); + const float combined_ease2 = bone->ease2 + (!rest ? pchan->ease2 : 0.0f); + const float hlength1 = combined_ease1 * circle_factor; + const float hlength2 = combined_ease2 * circle_factor; /* and only now negate h2 */ mul_v3_fl(h1, hlength1); diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index a93bda215a0..88a15d1fe3b 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -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" @@ -1241,8 +1242,9 @@ void BKE_nurb_makeFaces(Nurb *nu, float *coord_array, int rowstride, int resolu, basis += KNOTSV(nu); } u += ustep; - if (rowstride != 0) - in = (float *) (((unsigned char *) in) + (rowstride - 3 * totv * sizeof(*in))); + if (rowstride != 0) { + in = (float *)(((unsigned char *)in) + (rowstride - 3 * totv * sizeof(*in))); + } } /* free */ @@ -1685,7 +1687,7 @@ float *BKE_curve_make_orco(const EvaluationContext *eval_ctx, Scene *scene, Obje if (dl->flag & DL_CYCL_V) sizev++; } - else if (dl->flag & DL_CYCL_V) { + else if (dl->flag & DL_CYCL_V) { sizev++; } @@ -1802,7 +1804,7 @@ void BKE_curve_bevel_make( fp[3] = fp[4] = 0.0; fp[5] = cu->ext1; } - else if ( (cu->flag & (CU_FRONT | CU_BACK)) == 0 && cu->ext1 == 0.0f) { // we make a full round bevel in that case + else if ((cu->flag & (CU_FRONT | CU_BACK)) == 0 && cu->ext1 == 0.0f) { /* we make a full round bevel in that case */ nr = 4 + 2 * cu->bevresol; dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1"); @@ -1834,9 +1836,9 @@ void BKE_curve_bevel_make( if ((cu->flag & CU_BACK) || !(cu->flag & CU_FRONT)) { dnr = nr = 2 + cu->bevresol; - if ( (cu->flag & (CU_FRONT | CU_BACK)) == 0) + if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { nr = 3 + 2 * cu->bevresol; - + } dl = MEM_callocN(sizeof(DispList), "makebevelcurve p1"); dl->verts = MEM_mallocN(nr * sizeof(float[3]), "makebevelcurve p1"); BLI_addtail(disp, dl); @@ -1876,7 +1878,7 @@ void BKE_curve_bevel_make( fp[4] = cu->ext2; fp[5] = cu->ext1; - if ( (cu->flag & (CU_FRONT | CU_BACK)) == 0) { + if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { dl = MEM_dupallocN(dl); dl->verts = MEM_dupallocN(dl->verts); BLI_addtail(disp, dl); @@ -1892,9 +1894,9 @@ void BKE_curve_bevel_make( /* part 3, front */ if ((cu->flag & CU_FRONT) || !(cu->flag & CU_BACK)) { dnr = nr = 2 + cu->bevresol; - if ( (cu->flag & (CU_FRONT | CU_BACK)) == 0) + if ((cu->flag & (CU_FRONT | CU_BACK)) == 0) { nr = 3 + 2 * cu->bevresol; - + } dl = MEM_callocN(sizeof(DispList), "makebevelcurve p3"); dl->verts = MEM_mallocN(nr * sizeof(float[3]), "makebevelcurve p3"); BLI_addtail(disp, dl); @@ -2112,7 +2114,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * if (tilt_array) { if (nu->tilt_interp == KEY_CU_EASE) { /* May as well support for tilt also 2.47 ease interp */ *tilt_array = prevbezt->alfa + - (bezt->alfa - prevbezt->alfa) * (3.0f * fac * fac - 2.0f * fac * fac * fac); + (bezt->alfa - prevbezt->alfa) * (3.0f * fac * fac - 2.0f * fac * fac * fac); } else { key_curve_position_weights(fac, t, nu->tilt_interp); @@ -2128,7 +2130,7 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * * Note! - this only takes the 2 points into account, * giving much more localized results to changes in radius, sometimes you want that */ *radius_array = prevbezt->radius + - (bezt->radius - prevbezt->radius) * (3.0f * fac * fac - 2.0f * fac * fac * fac); + (bezt->radius - prevbezt->radius) * (3.0f * fac * fac - 2.0f * fac * fac * fac); } else { @@ -2136,8 +2138,9 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * if (tilt_array == NULL || nu->tilt_interp != nu->radius_interp) { key_curve_position_weights(fac, t, nu->radius_interp); } - *radius_array = t[0] * pprev->radius + t[1] * prevbezt->radius + - t[2] * bezt->radius + t[3] * next->radius; + *radius_array = + t[0] * pprev->radius + t[1] * prevbezt->radius + + t[2] * bezt->radius + t[3] * next->radius; } radius_array = POINTER_OFFSET(radius_array, stride); @@ -2145,8 +2148,9 @@ static void alfa_bezpart(BezTriple *prevbezt, BezTriple *bezt, Nurb *nu, float * if (weight_array) { /* basic interpolation for now, could copy tilt interp too */ - *weight_array = prevbezt->weight + - (bezt->weight - prevbezt->weight) * (3.0f * fac * fac - 2.0f * fac * fac * fac); + *weight_array = + prevbezt->weight + + (bezt->weight - prevbezt->weight) * (3.0f * fac * fac - 2.0f * fac * fac * fac); weight_array = POINTER_OFFSET(weight_array, stride); } @@ -3139,7 +3143,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) @@ -3153,6 +3157,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; } @@ -3204,7 +3211,14 @@ 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 horizontal handle size to be 1/3 of the key interval so that + * the X component of the parametric bezier curve is a linear spline */ + len = 6.0f / 2.5614f; + } + else { + len = tvec[0]; + } } else { len = len_v3(tvec); @@ -3215,10 +3229,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; @@ -3229,6 +3245,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) { @@ -3253,8 +3270,9 @@ static void calchandleNurb_intern( if ((bezt->h2 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */ float ydiff1 = prev->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->f5 = HD_AUTOTYPE_SPECIAL; } else { /* handles should not be beyond y coord of two others */ if (ydiff1 <= 0.0f) { @@ -3402,7 +3420,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) @@ -3417,9 +3435,540 @@ static void calchandlesNurb_intern(Nurb *nu, bool skip_align) } } -void BKE_nurb_handle_calc(BezTriple *bezt, BezTriple *prev, BezTriple *next, const bool is_fcurve) +/* A utility function for allocating a number of arrays of the same length + * with easy error checking and deallocation, and an easy way to add or remove + * arrays that are processed in this way when changing code. + * + * floats, chars: NULL-terminated arrays of pointers to array pointers that need to be allocated. + * + * Returns: pointer to the buffer that contains all of the arrays. + */ +static void *allocate_arrays(int count, float ***floats, char ***chars, const char *name) +{ + 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; +} + +static void free_arrays(void *buffer) +{ + MEM_freeN(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 void bezier_restore_equation(float *a, float *b, float *c, float *d, float *a0, float *b0, float *c0, float *d0, int i) +{ + a[i] = a0[i]; + b[i] = b0[i]; + c[i] = c0[i]; + d[i] = d0[i]; +} + +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)) { + free_arrays(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])) { + bezier_restore_equation(a, b, c, d, a0, b0, c0, d0, i); + + is_locked[i] = 0; + num_unlocks[i]++; + unlocked = true; + } + } + } + } while (overshoot || unlocked); + + free_arrays(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 */ + + free_arrays(tmp_buffer); +} + +static bool is_free_auto_point(BezTriple *bezt) +{ + return BEZT_IS_AUTOH(bezt) && bezt->f5 == HD_AUTOTYPE_NORMAL; +} + +void BKE_nurb_handle_smooth_fcurve(BezTriple *bezt, int total, bool cycle) +{ + /* ignore cyclic extrapolation if end points are locked */ + cycle = cycle && is_free_auto_point(&bezt[0]) && is_free_auto_point(&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 (!is_free_auto_point(&bezt[i])) { + search_base = i; + break; + } + } + + /* all points of the curve are freely changeable auto handles - solve as full cycle */ + if (search_base == 0) { + bezier_handle_calc_smooth_fcurve(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 (!is_free_auto_point(&bezt[j])) { + bezier_handle_calc_smooth_fcurve(bezt, total, start, count + 1, cycle); + start = j; + count = 1; + } + else { + count++; + } + } + + if (count > 1) { + bezier_handle_calc_smooth_fcurve(bezt, total, start, count, 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); + calchandleNurb_intern(bezt, prev, next, is_fcurve, false, smoothing); } void BKE_nurb_handles_calc(Nurb *nu) /* first, if needed, set handle flags */ @@ -3459,7 +4008,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); } } @@ -3566,7 +4115,7 @@ void BKE_nurb_handles_autocalc(Nurb *nu, int flag) bool align = false, leftsmall = false, rightsmall = false; /* left handle: */ - if (flag == 0 || (bezt1->f1 & flag) ) { + if (flag == 0 || (bezt1->f1 & flag)) { bezt1->h1 = HD_FREE; /* distance too short: vectorhandle */ if (len_squared_v3v3(bezt1->vec[1], bezt0->vec[1]) < eps_sq) { @@ -3585,7 +4134,7 @@ void BKE_nurb_handles_autocalc(Nurb *nu, int flag) } } /* right handle: */ - if (flag == 0 || (bezt1->f3 & flag) ) { + if (flag == 0 || (bezt1->f3 & flag)) { bezt1->h2 = HD_FREE; /* distance too short: vectorhandle */ if (len_squared_v3v3(bezt1->vec[1], bezt2->vec[1]) < eps_sq) { diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 1a93031034b..382b26abbc6 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -896,7 +896,7 @@ bool BKE_fcurve_is_cyclic(FCurve *fcu) if (fcm->flag & (FMODIFIER_FLAG_RANGERESTRICT | FMODIFIER_FLAG_USEINFLUENCE)) return false; - FMod_Cycles *data = (FMod_Cycles*)fcm->data; + 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) && @@ -939,14 +939,14 @@ void calchandles_fcurve(FCurve *fcu) 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 *first = &fcu->bezt[0], *last = &fcu->bezt[fcu->totvert - 1]; BezTriple tmp; bool cycle = BKE_fcurve_is_cyclic(fcu) && BEZT_IS_AUTOH(first) && BEZT_IS_AUTOH(last); /* get initial pointers */ bezt = fcu->bezt; - prev = cycle_offset_triple(cycle, &tmp, &fcu->bezt[fcu->totvert-2], last, first); + prev = cycle_offset_triple(cycle, &tmp, &fcu->bezt[fcu->totvert - 2], last, first); next = (bezt + 1); /* loop over all beztriples, adjusting handles */ @@ -956,7 +956,7 @@ 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 (BEZT_IS_AUTOH(bezt) && !cycle) { @@ -965,9 +965,16 @@ void calchandles_fcurve(FCurve *fcu) /* 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; @@ -981,6 +988,18 @@ void calchandles_fcurve(FCurve *fcu) 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) diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 1e9447c4f09..eaa2f89ab82 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -1197,14 +1197,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]; diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index d4943b1b566..cbb7a766dfe 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -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); |