diff options
author | Campbell Barton <ideasman42@gmail.com> | 2019-04-17 07:17:24 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2019-04-17 07:21:24 +0300 |
commit | e12c08e8d170b7ca40f204a5b0423c23a9fbc2c1 (patch) | |
tree | 8cf3453d12edb177a218ef8009357518ec6cab6a /source/blender/blenkernel/intern/fcurve.c | |
parent | b3dabc200a4b0399ec6b81f2ff2730d07b44fcaa (diff) |
ClangFormat: apply to source, most of intern
Apply clang format as proposed in T53211.
For details on usage and instructions for migrating branches
without conflicts, see:
https://wiki.blender.org/wiki/Tools/ClangFormat
Diffstat (limited to 'source/blender/blenkernel/intern/fcurve.c')
-rw-r--r-- | source/blender/blenkernel/intern/fcurve.c | 4745 |
1 files changed, 2420 insertions, 2325 deletions
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 78282ddb2b5..4a2a610918c 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -62,7 +62,7 @@ #include "CLG_log.h" #ifdef WITH_PYTHON -#include "BPY_extern.h" +# include "BPY_extern.h" #endif #define SMALL -1.0e-10 @@ -81,44 +81,44 @@ static CLG_LogRef LOG = {"bke.fcurve"}; /* Frees the F-Curve itself too, so make sure BLI_remlink is called before calling this... */ void free_fcurve(FCurve *fcu) { - if (fcu == NULL) - return; + if (fcu == NULL) + return; - /* free curve data */ - MEM_SAFE_FREE(fcu->bezt); - MEM_SAFE_FREE(fcu->fpt); + /* free curve data */ + MEM_SAFE_FREE(fcu->bezt); + MEM_SAFE_FREE(fcu->fpt); - /* free RNA-path, as this were allocated when getting the path string */ - MEM_SAFE_FREE(fcu->rna_path); + /* free RNA-path, as this were allocated when getting the path string */ + MEM_SAFE_FREE(fcu->rna_path); - /* free extra data - i.e. modifiers, and driver */ - fcurve_free_driver(fcu); - free_fmodifiers(&fcu->modifiers); + /* free extra data - i.e. modifiers, and driver */ + fcurve_free_driver(fcu); + free_fmodifiers(&fcu->modifiers); - /* free f-curve itself */ - MEM_freeN(fcu); + /* free f-curve itself */ + MEM_freeN(fcu); } /* Frees a list of F-Curves */ void free_fcurves(ListBase *list) { - FCurve *fcu, *fcn; - - /* sanity check */ - if (list == NULL) - return; - - /* free data - no need to call remlink before freeing each curve, - * as we store reference to next, and freeing only touches the curve - * it's given - */ - for (fcu = list->first; fcu; fcu = fcn) { - fcn = fcu->next; - free_fcurve(fcu); - } - - /* clear pointers just in case */ - BLI_listbase_clear(list); + FCurve *fcu, *fcn; + + /* sanity check */ + if (list == NULL) + return; + + /* free data - no need to call remlink before freeing each curve, + * as we store reference to next, and freeing only touches the curve + * it's given + */ + for (fcu = list->first; fcu; fcu = fcn) { + fcn = fcu->next; + free_fcurve(fcu); + } + + /* clear pointers just in case */ + BLI_listbase_clear(list); } /* ---------------------- Copy --------------------------- */ @@ -126,144 +126,144 @@ void free_fcurves(ListBase *list) /* duplicate an F-Curve */ FCurve *copy_fcurve(const FCurve *fcu) { - FCurve *fcu_d; + FCurve *fcu_d; - /* sanity check */ - if (fcu == NULL) - return NULL; + /* sanity check */ + if (fcu == NULL) + return NULL; - /* make a copy */ - fcu_d = MEM_dupallocN(fcu); + /* make a copy */ + fcu_d = MEM_dupallocN(fcu); - fcu_d->next = fcu_d->prev = NULL; - fcu_d->grp = NULL; + fcu_d->next = fcu_d->prev = NULL; + fcu_d->grp = NULL; - /* copy curve data */ - fcu_d->bezt = MEM_dupallocN(fcu_d->bezt); - fcu_d->fpt = MEM_dupallocN(fcu_d->fpt); + /* copy curve data */ + fcu_d->bezt = MEM_dupallocN(fcu_d->bezt); + fcu_d->fpt = MEM_dupallocN(fcu_d->fpt); - /* copy rna-path */ - fcu_d->rna_path = MEM_dupallocN(fcu_d->rna_path); + /* copy rna-path */ + fcu_d->rna_path = MEM_dupallocN(fcu_d->rna_path); - /* copy driver */ - fcu_d->driver = fcurve_copy_driver(fcu_d->driver); + /* copy driver */ + fcu_d->driver = fcurve_copy_driver(fcu_d->driver); - /* copy modifiers */ - copy_fmodifiers(&fcu_d->modifiers, &fcu->modifiers); + /* copy modifiers */ + copy_fmodifiers(&fcu_d->modifiers, &fcu->modifiers); - /* return new data */ - return fcu_d; + /* return new data */ + return fcu_d; } /* duplicate a list of F-Curves */ void copy_fcurves(ListBase *dst, ListBase *src) { - FCurve *dfcu, *sfcu; + FCurve *dfcu, *sfcu; - /* sanity checks */ - if (ELEM(NULL, dst, src)) - return; + /* sanity checks */ + if (ELEM(NULL, dst, src)) + return; - /* clear destination list first */ - BLI_listbase_clear(dst); + /* clear destination list first */ + BLI_listbase_clear(dst); - /* copy one-by-one */ - for (sfcu = src->first; sfcu; sfcu = sfcu->next) { - dfcu = copy_fcurve(sfcu); - BLI_addtail(dst, dfcu); - } + /* copy one-by-one */ + for (sfcu = src->first; sfcu; sfcu = sfcu->next) { + dfcu = copy_fcurve(sfcu); + BLI_addtail(dst, dfcu); + } } /* ----------------- Finding F-Curves -------------------------- */ /* high level function to get an fcurve from C without having the rna */ -FCurve *id_data_find_fcurve(ID *id, void *data, StructRNA *type, const char *prop_name, int index, bool *r_driven) +FCurve *id_data_find_fcurve( + ID *id, void *data, StructRNA *type, const char *prop_name, int index, bool *r_driven) { - /* anim vars */ - AnimData *adt = BKE_animdata_from_id(id); - FCurve *fcu = NULL; - - /* rna vars */ - PointerRNA ptr; - PropertyRNA *prop; - char *path; - - if (r_driven) - *r_driven = false; - - /* only use the current action ??? */ - if (ELEM(NULL, adt, adt->action)) - return NULL; - - RNA_pointer_create(id, type, data, &ptr); - prop = RNA_struct_find_property(&ptr, prop_name); - - if (prop) { - path = RNA_path_from_ID_to_property(&ptr, prop); - - if (path) { - /* animation takes priority over drivers */ - if ((adt->action) && (adt->action->curves.first)) - fcu = list_find_fcurve(&adt->action->curves, path, index); - - /* if not animated, check if driven */ - if ((fcu == NULL) && (adt->drivers.first)) { - fcu = list_find_fcurve(&adt->drivers, path, index); - if (fcu && r_driven) - *r_driven = true; - fcu = NULL; - } - - MEM_freeN(path); - } - } - - return fcu; + /* anim vars */ + AnimData *adt = BKE_animdata_from_id(id); + FCurve *fcu = NULL; + + /* rna vars */ + PointerRNA ptr; + PropertyRNA *prop; + char *path; + + if (r_driven) + *r_driven = false; + + /* only use the current action ??? */ + if (ELEM(NULL, adt, adt->action)) + return NULL; + + RNA_pointer_create(id, type, data, &ptr); + prop = RNA_struct_find_property(&ptr, prop_name); + + if (prop) { + path = RNA_path_from_ID_to_property(&ptr, prop); + + if (path) { + /* animation takes priority over drivers */ + if ((adt->action) && (adt->action->curves.first)) + fcu = list_find_fcurve(&adt->action->curves, path, index); + + /* if not animated, check if driven */ + if ((fcu == NULL) && (adt->drivers.first)) { + fcu = list_find_fcurve(&adt->drivers, path, index); + if (fcu && r_driven) + *r_driven = true; + fcu = NULL; + } + + MEM_freeN(path); + } + } + + return fcu; } - /* Find the F-Curve affecting the given RNA-access path + index, in the list of F-Curves provided */ FCurve *list_find_fcurve(ListBase *list, const char rna_path[], const int array_index) { - FCurve *fcu; - - /* sanity checks */ - if (ELEM(NULL, list, rna_path) || (array_index < 0) ) - return NULL; - - /* check paths of curves, then array indices... */ - for (fcu = list->first; fcu; fcu = fcu->next) { - /* simple string-compare (this assumes that they have the same root...) */ - if (fcu->rna_path && STREQ(fcu->rna_path, rna_path)) { - /* now check indices */ - if (fcu->array_index == array_index) - return fcu; - } - } - - /* return */ - return NULL; + FCurve *fcu; + + /* sanity checks */ + if (ELEM(NULL, list, rna_path) || (array_index < 0)) + return NULL; + + /* check paths of curves, then array indices... */ + for (fcu = list->first; fcu; fcu = fcu->next) { + /* simple string-compare (this assumes that they have the same root...) */ + if (fcu->rna_path && STREQ(fcu->rna_path, rna_path)) { + /* now check indices */ + if (fcu->array_index == array_index) + return fcu; + } + } + + /* return */ + return NULL; } /* quick way to loop over all fcurves of a given 'path' */ FCurve *iter_step_fcurve(FCurve *fcu_iter, const char rna_path[]) { - FCurve *fcu; - - /* sanity checks */ - if (ELEM(NULL, fcu_iter, rna_path)) - return NULL; - - /* check paths of curves, then array indices... */ - for (fcu = fcu_iter; fcu; fcu = fcu->next) { - /* simple string-compare (this assumes that they have the same root...) */ - if (fcu->rna_path && STREQ(fcu->rna_path, rna_path)) { - return fcu; - } - } - - /* return */ - return NULL; + FCurve *fcu; + + /* sanity checks */ + if (ELEM(NULL, fcu_iter, rna_path)) + return NULL; + + /* check paths of curves, then array indices... */ + for (fcu = fcu_iter; fcu; fcu = fcu->next) { + /* simple string-compare (this assumes that they have the same root...) */ + if (fcu->rna_path && STREQ(fcu->rna_path, rna_path)) { + return fcu; + } + } + + /* return */ + return NULL; } /* Get list of LinkData's containing pointers to the F-Curves which control the types of data indicated @@ -275,141 +275,160 @@ FCurve *iter_step_fcurve(FCurve *fcu_iter, const char rna_path[]) * - dataPrefix: i.e. 'pose.bones[' or 'nodes[' * - dataName: name of entity within "" immediately following the prefix */ -int list_find_data_fcurves(ListBase *dst, ListBase *src, const char *dataPrefix, const char *dataName) +int list_find_data_fcurves(ListBase *dst, + ListBase *src, + const char *dataPrefix, + const char *dataName) { - FCurve *fcu; - int matches = 0; - - /* sanity checks */ - if (ELEM(NULL, dst, src, dataPrefix, dataName)) - return 0; - else if ((dataPrefix[0] == 0) || (dataName[0] == 0)) - return 0; - - /* search each F-Curve one by one */ - for (fcu = src->first; fcu; fcu = fcu->next) { - /* check if quoted string matches the path */ - if ((fcu->rna_path) && strstr(fcu->rna_path, dataPrefix)) { - char *quotedName = BLI_str_quoted_substrN(fcu->rna_path, dataPrefix); - - if (quotedName) { - /* check if the quoted name matches the required name */ - if (STREQ(quotedName, dataName)) { - LinkData *ld = MEM_callocN(sizeof(LinkData), __func__); - - ld->data = fcu; - BLI_addtail(dst, ld); - - matches++; - } - - /* always free the quoted string, since it needs freeing */ - MEM_freeN(quotedName); - } - } - } - - /* return the number of matches */ - return matches; + FCurve *fcu; + int matches = 0; + + /* sanity checks */ + if (ELEM(NULL, dst, src, dataPrefix, dataName)) + return 0; + else if ((dataPrefix[0] == 0) || (dataName[0] == 0)) + return 0; + + /* search each F-Curve one by one */ + for (fcu = src->first; fcu; fcu = fcu->next) { + /* check if quoted string matches the path */ + if ((fcu->rna_path) && strstr(fcu->rna_path, dataPrefix)) { + char *quotedName = BLI_str_quoted_substrN(fcu->rna_path, dataPrefix); + + if (quotedName) { + /* check if the quoted name matches the required name */ + if (STREQ(quotedName, dataName)) { + LinkData *ld = MEM_callocN(sizeof(LinkData), __func__); + + ld->data = fcu; + BLI_addtail(dst, ld); + + matches++; + } + + /* always free the quoted string, since it needs freeing */ + MEM_freeN(quotedName); + } + } + } + + /* return the number of matches */ + return matches; } -FCurve *rna_get_fcurve( - PointerRNA *ptr, PropertyRNA *prop, int rnaindex, - AnimData **r_adt, bAction **r_action, bool *r_driven, bool *r_special) +FCurve *rna_get_fcurve(PointerRNA *ptr, + PropertyRNA *prop, + int rnaindex, + AnimData **r_adt, + bAction **r_action, + bool *r_driven, + bool *r_special) { - return rna_get_fcurve_context_ui(NULL, ptr, prop, rnaindex, r_adt, r_action, r_driven, r_special); + return rna_get_fcurve_context_ui( + NULL, ptr, prop, rnaindex, r_adt, r_action, r_driven, r_special); } -FCurve *rna_get_fcurve_context_ui( - bContext *C, PointerRNA *ptr, PropertyRNA *prop, int rnaindex, - AnimData **r_animdata, bAction **r_action, bool *r_driven, bool *r_special) +FCurve *rna_get_fcurve_context_ui(bContext *C, + PointerRNA *ptr, + PropertyRNA *prop, + int rnaindex, + AnimData **r_animdata, + bAction **r_action, + bool *r_driven, + bool *r_special) { - FCurve *fcu = NULL; - PointerRNA tptr = *ptr; - - *r_driven = false; - *r_special = false; - - if (r_animdata) *r_animdata = NULL; - if (r_action) *r_action = NULL; - - /* Special case for NLA Control Curves... */ - if (BKE_nlastrip_has_curves_for_property(ptr, prop)) { - NlaStrip *strip = (NlaStrip *)ptr->data; - - /* Set the special flag, since it cannot be a normal action/driver - * if we've been told to start looking here... - */ - *r_special = true; - - /* The F-Curve either exists or it doesn't here... */ - fcu = list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), rnaindex); - return fcu; - } - - /* there must be some RNA-pointer + property combon */ - if (prop && tptr.id.data && RNA_property_animateable(&tptr, prop)) { - AnimData *adt = BKE_animdata_from_id(tptr.id.data); - int step = C ? 2 : 1; /* Always 1 in case we have no context (can't check in 'ancestors' of given RNA ptr). */ - char *path = NULL; - - if (!adt && C) { - path = BKE_animdata_driver_path_hack(C, &tptr, prop, NULL); - adt = BKE_animdata_from_id(tptr.id.data); - step--; - } - - /* Standard F-Curve - Animation (Action) or Drivers */ - while (adt && step--) { - if ((adt->action && adt->action->curves.first) || (adt->drivers.first)) { - /* XXX this function call can become a performance bottleneck */ - if (step) { - path = RNA_path_from_ID_to_property(&tptr, prop); - } - - // XXX: the logic here is duplicated with a function up above - if (path) { - /* animation takes priority over drivers */ - if (adt->action && adt->action->curves.first) { - fcu = list_find_fcurve(&adt->action->curves, path, rnaindex); - - if (fcu && r_action) - *r_action = adt->action; - } - - /* if not animated, check if driven */ - if (!fcu && (adt->drivers.first)) { - fcu = list_find_fcurve(&adt->drivers, path, rnaindex); - - if (fcu) { - if (r_animdata) *r_animdata = adt; - *r_driven = true; - } - } - - if (fcu && r_action) { - if (r_animdata) *r_animdata = adt; - *r_action = adt->action; - break; - } - else if (step) { - char *tpath = BKE_animdata_driver_path_hack(C, &tptr, prop, path); - if (tpath && tpath != path) { - MEM_freeN(path); - path = tpath; - adt = BKE_animdata_from_id(tptr.id.data); - } - else { - adt = NULL; - } - } - } - } - } - MEM_SAFE_FREE(path); - } - - return fcu; + FCurve *fcu = NULL; + PointerRNA tptr = *ptr; + + *r_driven = false; + *r_special = false; + + if (r_animdata) + *r_animdata = NULL; + if (r_action) + *r_action = NULL; + + /* Special case for NLA Control Curves... */ + if (BKE_nlastrip_has_curves_for_property(ptr, prop)) { + NlaStrip *strip = (NlaStrip *)ptr->data; + + /* Set the special flag, since it cannot be a normal action/driver + * if we've been told to start looking here... + */ + *r_special = true; + + /* The F-Curve either exists or it doesn't here... */ + fcu = list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), rnaindex); + return fcu; + } + + /* there must be some RNA-pointer + property combon */ + if (prop && tptr.id.data && RNA_property_animateable(&tptr, prop)) { + AnimData *adt = BKE_animdata_from_id(tptr.id.data); + int step = + C ? 2 : + 1; /* Always 1 in case we have no context (can't check in 'ancestors' of given RNA ptr). */ + char *path = NULL; + + if (!adt && C) { + path = BKE_animdata_driver_path_hack(C, &tptr, prop, NULL); + adt = BKE_animdata_from_id(tptr.id.data); + step--; + } + + /* Standard F-Curve - Animation (Action) or Drivers */ + while (adt && step--) { + if ((adt->action && adt->action->curves.first) || (adt->drivers.first)) { + /* XXX this function call can become a performance bottleneck */ + if (step) { + path = RNA_path_from_ID_to_property(&tptr, prop); + } + + // XXX: the logic here is duplicated with a function up above + if (path) { + /* animation takes priority over drivers */ + if (adt->action && adt->action->curves.first) { + fcu = list_find_fcurve(&adt->action->curves, path, rnaindex); + + if (fcu && r_action) + *r_action = adt->action; + } + + /* if not animated, check if driven */ + if (!fcu && (adt->drivers.first)) { + fcu = list_find_fcurve(&adt->drivers, path, rnaindex); + + if (fcu) { + if (r_animdata) + *r_animdata = adt; + *r_driven = true; + } + } + + if (fcu && r_action) { + if (r_animdata) + *r_animdata = adt; + *r_action = adt->action; + break; + } + else if (step) { + char *tpath = BKE_animdata_driver_path_hack(C, &tptr, prop, path); + if (tpath && tpath != path) { + MEM_freeN(path); + path = tpath; + adt = BKE_animdata_from_id(tptr.id.data); + } + else { + adt = NULL; + } + } + } + } + } + MEM_SAFE_FREE(path); + } + + return fcu; } /* ----------------- Finding Keyframes/Extents -------------------------- */ @@ -417,294 +436,312 @@ FCurve *rna_get_fcurve_context_ui( /* Binary search algorithm for finding where to insert BezTriple, with optional argument for precision required. * Returns the index to insert at (data already at that index will be offset if replace is 0) */ -static int binarysearch_bezt_index_ex(BezTriple array[], float frame, int arraylen, float threshold, bool *r_replace) +static int binarysearch_bezt_index_ex( + BezTriple array[], float frame, int arraylen, float threshold, bool *r_replace) { - int start = 0, end = arraylen; - int loopbreaker = 0, maxloop = arraylen * 2; - - /* initialize replace-flag first */ - *r_replace = false; - - /* sneaky optimizations (don't go through searching process if...): - * - keyframe to be added is to be added out of current bounds - * - keyframe to be added would replace one of the existing ones on bounds - */ - if ((arraylen <= 0) || (array == NULL)) { - CLOG_WARN(&LOG, "encountered invalid array"); - return 0; - } - else { - /* check whether to add before/after/on */ - float framenum; - - /* 'First' Keyframe (when only one keyframe, this case is used) */ - framenum = array[0].vec[1][0]; - if (IS_EQT(frame, framenum, threshold)) { - *r_replace = true; - return 0; - } - else if (frame < framenum) - return 0; - - /* 'Last' Keyframe */ - framenum = array[(arraylen - 1)].vec[1][0]; - if (IS_EQT(frame, framenum, threshold)) { - *r_replace = true; - return (arraylen - 1); - } - else if (frame > framenum) - return arraylen; - } - - - /* most of the time, this loop is just to find where to put it - * 'loopbreaker' is just here to prevent infinite loops - */ - for (loopbreaker = 0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) { - /* compute and get midpoint */ - int mid = start + ((end - start) / 2); /* we calculate the midpoint this way to avoid int overflows... */ - float midfra = array[mid].vec[1][0]; - - /* check if exactly equal to midpoint */ - if (IS_EQT(frame, midfra, threshold)) { - *r_replace = true; - return mid; - } - - /* repeat in upper/lower half */ - if (frame > midfra) - start = mid + 1; - else if (frame < midfra) - end = mid - 1; - } - - /* print error if loop-limit exceeded */ - if (loopbreaker == (maxloop - 1)) { - CLOG_ERROR(&LOG, "search taking too long"); - - /* include debug info */ - CLOG_ERROR(&LOG, "\tround = %d: start = %d, end = %d, arraylen = %d", loopbreaker, start, end, arraylen); - } - - /* not found, so return where to place it */ - return start; + int start = 0, end = arraylen; + int loopbreaker = 0, maxloop = arraylen * 2; + + /* initialize replace-flag first */ + *r_replace = false; + + /* sneaky optimizations (don't go through searching process if...): + * - keyframe to be added is to be added out of current bounds + * - keyframe to be added would replace one of the existing ones on bounds + */ + if ((arraylen <= 0) || (array == NULL)) { + CLOG_WARN(&LOG, "encountered invalid array"); + return 0; + } + else { + /* check whether to add before/after/on */ + float framenum; + + /* 'First' Keyframe (when only one keyframe, this case is used) */ + framenum = array[0].vec[1][0]; + if (IS_EQT(frame, framenum, threshold)) { + *r_replace = true; + return 0; + } + else if (frame < framenum) + return 0; + + /* 'Last' Keyframe */ + framenum = array[(arraylen - 1)].vec[1][0]; + if (IS_EQT(frame, framenum, threshold)) { + *r_replace = true; + return (arraylen - 1); + } + else if (frame > framenum) + return arraylen; + } + + /* most of the time, this loop is just to find where to put it + * 'loopbreaker' is just here to prevent infinite loops + */ + for (loopbreaker = 0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) { + /* compute and get midpoint */ + int mid = start + ((end - start) / + 2); /* we calculate the midpoint this way to avoid int overflows... */ + float midfra = array[mid].vec[1][0]; + + /* check if exactly equal to midpoint */ + if (IS_EQT(frame, midfra, threshold)) { + *r_replace = true; + return mid; + } + + /* repeat in upper/lower half */ + if (frame > midfra) + start = mid + 1; + else if (frame < midfra) + end = mid - 1; + } + + /* print error if loop-limit exceeded */ + if (loopbreaker == (maxloop - 1)) { + CLOG_ERROR(&LOG, "search taking too long"); + + /* include debug info */ + CLOG_ERROR(&LOG, + "\tround = %d: start = %d, end = %d, arraylen = %d", + loopbreaker, + start, + end, + arraylen); + } + + /* not found, so return where to place it */ + return start; } - /* Binary search algorithm for finding where to insert BezTriple. (for use by insert_bezt_fcurve) * Returns the index to insert at (data already at that index will be offset if replace is 0) */ int binarysearch_bezt_index(BezTriple array[], float frame, int arraylen, bool *r_replace) { - /* this is just a wrapper which uses the default threshold */ - return binarysearch_bezt_index_ex(array, frame, arraylen, BEZT_BINARYSEARCH_THRESH, r_replace); + /* this is just a wrapper which uses the default threshold */ + return binarysearch_bezt_index_ex(array, frame, arraylen, BEZT_BINARYSEARCH_THRESH, r_replace); } /* ...................................... */ /* helper for calc_fcurve_* functions -> find first and last BezTriple to be used */ -static short get_fcurve_end_keyframes(FCurve *fcu, BezTriple **first, BezTriple **last, +static short get_fcurve_end_keyframes(FCurve *fcu, + BezTriple **first, + BezTriple **last, const bool do_sel_only) { - bool found = false; - - /* init outputs */ - *first = NULL; - *last = NULL; - - /* sanity checks */ - if (fcu->bezt == NULL) - return found; - - /* only include selected items? */ - if (do_sel_only) { - BezTriple *bezt; - unsigned int i; - - /* find first selected */ - bezt = fcu->bezt; - for (i = 0; i < fcu->totvert; bezt++, i++) { - if (BEZT_ISSEL_ANY(bezt)) { - *first = bezt; - found = true; - break; - } - } - - /* find last selected */ - bezt = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert); - for (i = 0; i < fcu->totvert; bezt--, i++) { - if (BEZT_ISSEL_ANY(bezt)) { - *last = bezt; - found = true; - break; - } - } - } - else { - /* just full array */ - *first = fcu->bezt; - *last = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert); - found = true; - } - - return found; + bool found = false; + + /* init outputs */ + *first = NULL; + *last = NULL; + + /* sanity checks */ + if (fcu->bezt == NULL) + return found; + + /* only include selected items? */ + if (do_sel_only) { + BezTriple *bezt; + unsigned int i; + + /* find first selected */ + bezt = fcu->bezt; + for (i = 0; i < fcu->totvert; bezt++, i++) { + if (BEZT_ISSEL_ANY(bezt)) { + *first = bezt; + found = true; + break; + } + } + + /* find last selected */ + bezt = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert); + for (i = 0; i < fcu->totvert; bezt--, i++) { + if (BEZT_ISSEL_ANY(bezt)) { + *last = bezt; + found = true; + break; + } + } + } + else { + /* just full array */ + *first = fcu->bezt; + *last = ARRAY_LAST_ITEM(fcu->bezt, BezTriple, fcu->totvert); + found = true; + } + + return found; } - /* Calculate the extents of F-Curve's data */ -bool calc_fcurve_bounds(FCurve *fcu, float *xmin, float *xmax, float *ymin, float *ymax, - const bool do_sel_only, const bool include_handles) +bool calc_fcurve_bounds(FCurve *fcu, + float *xmin, + float *xmax, + float *ymin, + float *ymax, + const bool do_sel_only, + const bool include_handles) { - float xminv = 999999999.0f, xmaxv = -999999999.0f; - float yminv = 999999999.0f, ymaxv = -999999999.0f; - bool foundvert = false; - unsigned int i; - - if (fcu->totvert) { - if (fcu->bezt) { - BezTriple *bezt_first = NULL, *bezt_last = NULL; - - if (xmin || xmax) { - /* get endpoint keyframes */ - foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only); - - if (bezt_first) { - BLI_assert(bezt_last != NULL); - - if (include_handles) { - xminv = min_fff(xminv, bezt_first->vec[0][0], bezt_first->vec[1][0]); - xmaxv = max_fff(xmaxv, bezt_last->vec[1][0], bezt_last->vec[2][0]); - } - else { - xminv = min_ff(xminv, bezt_first->vec[1][0]); - xmaxv = max_ff(xmaxv, bezt_last->vec[1][0]); - } - } - } - - /* only loop over keyframes to find extents for values if needed */ - if (ymin || ymax) { - BezTriple *bezt, *prevbezt = NULL; - - for (bezt = fcu->bezt, i = 0; i < fcu->totvert; prevbezt = bezt, bezt++, i++) { - if ((do_sel_only == false) || BEZT_ISSEL_ANY(bezt)) { - /* keyframe itself */ - yminv = min_ff(yminv, bezt->vec[1][1]); - ymaxv = max_ff(ymaxv, bezt->vec[1][1]); - - if (include_handles) { - /* left handle - only if applicable - * NOTE: for the very first keyframe, the left handle actually has no bearings on anything - */ - if (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)) { - yminv = min_ff(yminv, bezt->vec[0][1]); - ymaxv = max_ff(ymaxv, bezt->vec[0][1]); - } - - /* right handle - only if applicable */ - if (bezt->ipo == BEZT_IPO_BEZ) { - yminv = min_ff(yminv, bezt->vec[2][1]); - ymaxv = max_ff(ymaxv, bezt->vec[2][1]); - } - } - - foundvert = true; - } - } - } - } - else if (fcu->fpt) { - /* frame range can be directly calculated from end verts */ - if (xmin || xmax) { - xminv = min_ff(xminv, fcu->fpt[0].vec[0]); - xmaxv = max_ff(xmaxv, fcu->fpt[fcu->totvert - 1].vec[0]); - } - - /* only loop over keyframes to find extents for values if needed */ - if (ymin || ymax) { - FPoint *fpt; - - for (fpt = fcu->fpt, i = 0; i < fcu->totvert; fpt++, i++) { - if (fpt->vec[1] < yminv) - yminv = fpt->vec[1]; - if (fpt->vec[1] > ymaxv) - ymaxv = fpt->vec[1]; - - foundvert = true; - } - } - } - } - - if (foundvert) { - if (xmin) *xmin = xminv; - if (xmax) *xmax = xmaxv; - - if (ymin) *ymin = yminv; - if (ymax) *ymax = ymaxv; - } - else { - if (G.debug & G_DEBUG) - printf("F-Curve calc bounds didn't find anything, so assuming minimum bounds of 1.0\n"); - - if (xmin) *xmin = 0.0f; - if (xmax) *xmax = 1.0f; - - if (ymin) *ymin = 0.0f; - if (ymax) *ymax = 1.0f; - } - - return foundvert; + float xminv = 999999999.0f, xmaxv = -999999999.0f; + float yminv = 999999999.0f, ymaxv = -999999999.0f; + bool foundvert = false; + unsigned int i; + + if (fcu->totvert) { + if (fcu->bezt) { + BezTriple *bezt_first = NULL, *bezt_last = NULL; + + if (xmin || xmax) { + /* get endpoint keyframes */ + foundvert = get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only); + + if (bezt_first) { + BLI_assert(bezt_last != NULL); + + if (include_handles) { + xminv = min_fff(xminv, bezt_first->vec[0][0], bezt_first->vec[1][0]); + xmaxv = max_fff(xmaxv, bezt_last->vec[1][0], bezt_last->vec[2][0]); + } + else { + xminv = min_ff(xminv, bezt_first->vec[1][0]); + xmaxv = max_ff(xmaxv, bezt_last->vec[1][0]); + } + } + } + + /* only loop over keyframes to find extents for values if needed */ + if (ymin || ymax) { + BezTriple *bezt, *prevbezt = NULL; + + for (bezt = fcu->bezt, i = 0; i < fcu->totvert; prevbezt = bezt, bezt++, i++) { + if ((do_sel_only == false) || BEZT_ISSEL_ANY(bezt)) { + /* keyframe itself */ + yminv = min_ff(yminv, bezt->vec[1][1]); + ymaxv = max_ff(ymaxv, bezt->vec[1][1]); + + if (include_handles) { + /* left handle - only if applicable + * NOTE: for the very first keyframe, the left handle actually has no bearings on anything + */ + if (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)) { + yminv = min_ff(yminv, bezt->vec[0][1]); + ymaxv = max_ff(ymaxv, bezt->vec[0][1]); + } + + /* right handle - only if applicable */ + if (bezt->ipo == BEZT_IPO_BEZ) { + yminv = min_ff(yminv, bezt->vec[2][1]); + ymaxv = max_ff(ymaxv, bezt->vec[2][1]); + } + } + + foundvert = true; + } + } + } + } + else if (fcu->fpt) { + /* frame range can be directly calculated from end verts */ + if (xmin || xmax) { + xminv = min_ff(xminv, fcu->fpt[0].vec[0]); + xmaxv = max_ff(xmaxv, fcu->fpt[fcu->totvert - 1].vec[0]); + } + + /* only loop over keyframes to find extents for values if needed */ + if (ymin || ymax) { + FPoint *fpt; + + for (fpt = fcu->fpt, i = 0; i < fcu->totvert; fpt++, i++) { + if (fpt->vec[1] < yminv) + yminv = fpt->vec[1]; + if (fpt->vec[1] > ymaxv) + ymaxv = fpt->vec[1]; + + foundvert = true; + } + } + } + } + + if (foundvert) { + if (xmin) + *xmin = xminv; + if (xmax) + *xmax = xmaxv; + + if (ymin) + *ymin = yminv; + if (ymax) + *ymax = ymaxv; + } + else { + if (G.debug & G_DEBUG) + printf("F-Curve calc bounds didn't find anything, so assuming minimum bounds of 1.0\n"); + + if (xmin) + *xmin = 0.0f; + if (xmax) + *xmax = 1.0f; + + if (ymin) + *ymin = 0.0f; + if (ymax) + *ymax = 1.0f; + } + + return foundvert; } /* Calculate the extents of F-Curve's keyframes */ -bool calc_fcurve_range(FCurve *fcu, float *start, float *end, - const bool do_sel_only, const bool do_min_length) +bool calc_fcurve_range( + FCurve *fcu, float *start, float *end, const bool do_sel_only, const bool do_min_length) { - float min = 999999999.0f, max = -999999999.0f; - bool foundvert = false; - - if (fcu->totvert) { - if (fcu->bezt) { - BezTriple *bezt_first = NULL, *bezt_last = NULL; + float min = 999999999.0f, max = -999999999.0f; + bool foundvert = false; - /* get endpoint keyframes */ - get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only); + if (fcu->totvert) { + if (fcu->bezt) { + BezTriple *bezt_first = NULL, *bezt_last = NULL; - if (bezt_first) { - BLI_assert(bezt_last != NULL); + /* get endpoint keyframes */ + get_fcurve_end_keyframes(fcu, &bezt_first, &bezt_last, do_sel_only); - min = min_ff(min, bezt_first->vec[1][0]); - max = max_ff(max, bezt_last->vec[1][0]); + if (bezt_first) { + BLI_assert(bezt_last != NULL); - foundvert = true; - } - } - else if (fcu->fpt) { - min = min_ff(min, fcu->fpt[0].vec[0]); - max = max_ff(max, fcu->fpt[fcu->totvert - 1].vec[0]); + min = min_ff(min, bezt_first->vec[1][0]); + max = max_ff(max, bezt_last->vec[1][0]); - foundvert = true; - } + foundvert = true; + } + } + else if (fcu->fpt) { + min = min_ff(min, fcu->fpt[0].vec[0]); + max = max_ff(max, fcu->fpt[fcu->totvert - 1].vec[0]); - } + foundvert = true; + } + } - if (foundvert == false) { - min = max = 0.0f; - } + if (foundvert == false) { + min = max = 0.0f; + } - if (do_min_length) { - /* minimum length is 1 frame */ - if (min == max) { - max += 1.0f; - } - } + if (do_min_length) { + /* minimum length is 1 frame */ + if (min == max) { + max += 1.0f; + } + } - *start = min; - *end = max; + *start = min; + *end = max; - return foundvert; + return foundvert; } /* ----------------- Status Checks -------------------------- */ @@ -715,65 +752,62 @@ bool calc_fcurve_range(FCurve *fcu, float *start, float *end, */ bool fcurve_are_keyframes_usable(FCurve *fcu) { - /* F-Curve must exist */ - if (fcu == NULL) - return false; - - /* F-Curve must not have samples - samples are mutually exclusive of keyframes */ - if (fcu->fpt) - return false; - - /* if it has modifiers, none of these should "drastically" alter the curve */ - if (fcu->modifiers.first) { - FModifier *fcm; - - /* check modifiers from last to first, as last will be more influential */ - /* TODO: optionally, only check modifier if it is the active one... */ - for (fcm = fcu->modifiers.last; fcm; fcm = fcm->prev) { - /* ignore if muted/disabled */ - if (fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) - continue; - - /* type checks */ - switch (fcm->type) { - /* clearly harmless - do nothing */ - case FMODIFIER_TYPE_CYCLES: - case FMODIFIER_TYPE_STEPPED: - case FMODIFIER_TYPE_NOISE: - break; - - /* sometimes harmful - depending on whether they're "additive" or not */ - case FMODIFIER_TYPE_GENERATOR: - { - FMod_Generator *data = (FMod_Generator *)fcm->data; - - if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) - return false; - break; - } - case FMODIFIER_TYPE_FN_GENERATOR: - { - FMod_FunctionGenerator *data = (FMod_FunctionGenerator *)fcm->data; - - if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) - return false; - break; - } - /* always harmful - cannot allow */ - default: - return false; - } - } - } - - /* keyframes are usable */ - return true; + /* F-Curve must exist */ + if (fcu == NULL) + return false; + + /* F-Curve must not have samples - samples are mutually exclusive of keyframes */ + if (fcu->fpt) + return false; + + /* if it has modifiers, none of these should "drastically" alter the curve */ + if (fcu->modifiers.first) { + FModifier *fcm; + + /* check modifiers from last to first, as last will be more influential */ + /* TODO: optionally, only check modifier if it is the active one... */ + for (fcm = fcu->modifiers.last; fcm; fcm = fcm->prev) { + /* ignore if muted/disabled */ + if (fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) + continue; + + /* type checks */ + switch (fcm->type) { + /* clearly harmless - do nothing */ + case FMODIFIER_TYPE_CYCLES: + case FMODIFIER_TYPE_STEPPED: + case FMODIFIER_TYPE_NOISE: + break; + + /* sometimes harmful - depending on whether they're "additive" or not */ + case FMODIFIER_TYPE_GENERATOR: { + FMod_Generator *data = (FMod_Generator *)fcm->data; + + if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) + return false; + break; + } + case FMODIFIER_TYPE_FN_GENERATOR: { + FMod_FunctionGenerator *data = (FMod_FunctionGenerator *)fcm->data; + + if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0) + return false; + break; + } + /* always harmful - cannot allow */ + default: + return false; + } + } + } + + /* keyframes are usable */ + return true; } bool BKE_fcurve_is_protected(FCurve *fcu) { - return ((fcu->flag & FCURVE_PROTECTED) || - ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED))); + return ((fcu->flag & FCURVE_PROTECTED) || ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED))); } /* Can keyframes be added to F-Curve? @@ -781,16 +815,16 @@ bool BKE_fcurve_is_protected(FCurve *fcu) */ bool fcurve_is_keyframable(FCurve *fcu) { - /* F-Curve's keyframes must be "usable" (i.e. visible + have an effect on final result) */ - if (fcurve_are_keyframes_usable(fcu) == 0) - return false; + /* F-Curve's keyframes must be "usable" (i.e. visible + have an effect on final result) */ + if (fcurve_are_keyframes_usable(fcu) == 0) + return false; - /* F-Curve must currently be editable too */ - if (BKE_fcurve_is_protected(fcu)) - return false; + /* F-Curve must currently be editable too */ + if (BKE_fcurve_is_protected(fcu)) + return false; - /* F-Curve is keyframable */ - return true; + /* F-Curve is keyframable */ + return true; } /* ***************************** Keyframe Column Tools ********************************* */ @@ -798,25 +832,29 @@ bool fcurve_is_keyframable(FCurve *fcu) /* add a BezTriple to a column */ void bezt_add_to_cfra_elem(ListBase *lb, BezTriple *bezt) { - CfraElem *ce, *cen; - - for (ce = lb->first; ce; ce = ce->next) { - /* double key? */ - if (IS_EQT(ce->cfra, bezt->vec[1][0], BEZT_BINARYSEARCH_THRESH)) { - if (bezt->f2 & SELECT) ce->sel = bezt->f2; - return; - } - /* should key be inserted before this column? */ - else if (ce->cfra > bezt->vec[1][0]) break; - } - - /* create a new column */ - cen = MEM_callocN(sizeof(CfraElem), "add_to_cfra_elem"); - if (ce) BLI_insertlinkbefore(lb, ce, cen); - else BLI_addtail(lb, cen); - - cen->cfra = bezt->vec[1][0]; - cen->sel = bezt->f2; + CfraElem *ce, *cen; + + for (ce = lb->first; ce; ce = ce->next) { + /* double key? */ + if (IS_EQT(ce->cfra, bezt->vec[1][0], BEZT_BINARYSEARCH_THRESH)) { + if (bezt->f2 & SELECT) + ce->sel = bezt->f2; + return; + } + /* should key be inserted before this column? */ + else if (ce->cfra > bezt->vec[1][0]) + break; + } + + /* create a new column */ + cen = MEM_callocN(sizeof(CfraElem), "add_to_cfra_elem"); + if (ce) + BLI_insertlinkbefore(lb, ce, cen); + else + BLI_addtail(lb, cen); + + cen->cfra = bezt->vec[1][0]; + cen->sel = bezt->f2; } /* ***************************** Samples Utilities ******************************* */ @@ -825,53 +863,53 @@ void bezt_add_to_cfra_elem(ListBase *lb, BezTriple *bezt) * which BezTriples/Keyframe data are ill equipped to do. */ - /* Basic sampling callback which acts as a wrapper for evaluate_fcurve() * 'data' arg here is unneeded here... */ float fcurve_samplingcb_evalcurve(FCurve *fcu, void *UNUSED(data), float evaltime) { - /* assume any interference from drivers on the curve is intended... */ - return evaluate_fcurve(fcu, evaltime); + /* assume any interference from drivers on the curve is intended... */ + return evaluate_fcurve(fcu, evaltime); } - /* Main API function for creating a set of sampled curve data, given some callback function * used to retrieve the values to store. */ void fcurve_store_samples(FCurve *fcu, void *data, int start, int end, FcuSampleFunc sample_cb) { - FPoint *fpt, *new_fpt; - int cfra; - - /* sanity checks */ - /* TODO: make these tests report errors using reports not CLOG's */ - if (ELEM(NULL, fcu, sample_cb)) { - CLOG_ERROR(&LOG, "No F-Curve with F-Curve Modifiers to Bake"); - return; - } - if (start > end) { - CLOG_ERROR(&LOG, "Error: Frame range for Sampled F-Curve creation is inappropriate"); - return; - } - - /* set up sample data */ - fpt = new_fpt = MEM_callocN(sizeof(FPoint) * (end - start + 1), "FPoint Samples"); - - /* use the sampling callback at 1-frame intervals from start to end frames */ - for (cfra = start; cfra <= end; cfra++, fpt++) { - fpt->vec[0] = (float)cfra; - fpt->vec[1] = sample_cb(fcu, data, (float)cfra); - } - - /* free any existing sample/keyframe data on curve */ - if (fcu->bezt) MEM_freeN(fcu->bezt); - if (fcu->fpt) MEM_freeN(fcu->fpt); - - /* store the samples */ - fcu->bezt = NULL; - fcu->fpt = new_fpt; - fcu->totvert = end - start + 1; + FPoint *fpt, *new_fpt; + int cfra; + + /* sanity checks */ + /* TODO: make these tests report errors using reports not CLOG's */ + if (ELEM(NULL, fcu, sample_cb)) { + CLOG_ERROR(&LOG, "No F-Curve with F-Curve Modifiers to Bake"); + return; + } + if (start > end) { + CLOG_ERROR(&LOG, "Error: Frame range for Sampled F-Curve creation is inappropriate"); + return; + } + + /* set up sample data */ + fpt = new_fpt = MEM_callocN(sizeof(FPoint) * (end - start + 1), "FPoint Samples"); + + /* use the sampling callback at 1-frame intervals from start to end frames */ + for (cfra = start; cfra <= end; cfra++, fpt++) { + fpt->vec[0] = (float)cfra; + fpt->vec[1] = sample_cb(fcu, data, (float)cfra); + } + + /* free any existing sample/keyframe data on curve */ + if (fcu->bezt) + MEM_freeN(fcu->bezt); + if (fcu->fpt) + MEM_freeN(fcu->fpt); + + /* store the samples */ + fcu->bezt = NULL; + fcu->fpt = new_fpt; + fcu->totvert = end - start + 1; } /* ***************************** F-Curve Sanity ********************************* */ @@ -883,60 +921,61 @@ void fcurve_store_samples(FCurve *fcu, void *data, int start, int end, FcuSample /* Checks if the F-Curve has a Cycles modifier, and returns the type of the cycle behavior. */ eFCU_Cycle_Type BKE_fcurve_get_cycle_type(FCurve *fcu) { - FModifier *fcm = fcu->modifiers.first; + FModifier *fcm = fcu->modifiers.first; - if (!fcm || fcm->type != FMODIFIER_TYPE_CYCLES) { - return FCU_CYCLE_NONE; - } + if (!fcm || fcm->type != FMODIFIER_TYPE_CYCLES) { + return FCU_CYCLE_NONE; + } - if (fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) { - return FCU_CYCLE_NONE; - } + if (fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) { + return FCU_CYCLE_NONE; + } - if (fcm->flag & (FMODIFIER_FLAG_RANGERESTRICT | FMODIFIER_FLAG_USEINFLUENCE)) { - return FCU_CYCLE_NONE; - } + if (fcm->flag & (FMODIFIER_FLAG_RANGERESTRICT | FMODIFIER_FLAG_USEINFLUENCE)) { + return FCU_CYCLE_NONE; + } - FMod_Cycles *data = (FMod_Cycles *)fcm->data; + FMod_Cycles *data = (FMod_Cycles *)fcm->data; - if (data && data->after_cycles == 0 && data->before_cycles == 0) { - if (data->before_mode == FCM_EXTRAPOLATE_CYCLIC && data->after_mode == FCM_EXTRAPOLATE_CYCLIC) { - return FCU_CYCLE_PERFECT; - } + if (data && data->after_cycles == 0 && data->before_cycles == 0) { + if (data->before_mode == FCM_EXTRAPOLATE_CYCLIC && + data->after_mode == FCM_EXTRAPOLATE_CYCLIC) { + return FCU_CYCLE_PERFECT; + } - if (ELEM(data->before_mode, FCM_EXTRAPOLATE_CYCLIC, FCM_EXTRAPOLATE_CYCLIC_OFFSET) && - ELEM(data->after_mode, FCM_EXTRAPOLATE_CYCLIC, FCM_EXTRAPOLATE_CYCLIC_OFFSET)) - { - return FCU_CYCLE_OFFSET; - } - } + if (ELEM(data->before_mode, FCM_EXTRAPOLATE_CYCLIC, FCM_EXTRAPOLATE_CYCLIC_OFFSET) && + ELEM(data->after_mode, FCM_EXTRAPOLATE_CYCLIC, FCM_EXTRAPOLATE_CYCLIC_OFFSET)) { + return FCU_CYCLE_OFFSET; + } + } - return FCU_CYCLE_NONE; + return FCU_CYCLE_NONE; } /* Checks if the F-Curve has a Cycles modifier with simple settings that warrant transition smoothing */ bool BKE_fcurve_is_cyclic(FCurve *fcu) { - return BKE_fcurve_get_cycle_type(fcu) != FCU_CYCLE_NONE; + return BKE_fcurve_get_cycle_type(fcu) != FCU_CYCLE_NONE; } /* Shifts 'in' by the difference in coordinates between 'to' and 'from', using 'out' as the output buffer. * When 'to' and 'from' are end points of the loop, this moves the 'in' point one loop cycle. */ -static BezTriple *cycle_offset_triple(bool cycle, BezTriple *out, const BezTriple *in, const BezTriple *from, const BezTriple *to) +static BezTriple *cycle_offset_triple( + bool cycle, BezTriple *out, const BezTriple *in, const BezTriple *from, const BezTriple *to) { - if (!cycle) - return NULL; + if (!cycle) + return NULL; - memcpy(out, in, sizeof(BezTriple)); + memcpy(out, in, sizeof(BezTriple)); - float delta[3]; - sub_v3_v3v3(delta, to->vec[1], from->vec[1]); + 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); + for (int i = 0; i < 3; i++) + add_v3_v3(out->vec[i], delta); - return out; + return out; } /* This function recalculates the handles of an F-Curve @@ -944,97 +983,99 @@ static BezTriple *cycle_offset_triple(bool cycle, BezTriple *out, const BezTripl */ void calchandles_fcurve(FCurve *fcu) { - BezTriple *bezt, *prev, *next; - int a = fcu->totvert; - - /* Error checking: - * - need at least two points - * - need bezier keys - * - only bezier-interpolation has handles (for now) - */ - if (ELEM(NULL, fcu, fcu->bezt) || (a < 2) /*|| ELEM(fcu->ipo, BEZT_IPO_CONST, BEZT_IPO_LIN)*/) - return; - - /* if the first modifier is Cycles, smooth the curve through the cycle */ - BezTriple *first = &fcu->bezt[0], *last = &fcu->bezt[fcu->totvert - 1]; - BezTriple tmp; - - bool cycle = 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); - next = (bezt + 1); - - /* loop over all beztriples, adjusting handles */ - while (a--) { - /* clamp timing of handles to be on either side of beztriple */ - if (bezt->vec[0][0] > bezt->vec[1][0]) bezt->vec[0][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 */ - BKE_nurb_handle_calc(bezt, prev, next, true, fcu->auto_smoothing); - - /* for automatic ease in and out */ - if (BEZT_IS_AUTOH(bezt) && !cycle) { - /* only do this on first or last beztriple */ - if ((a == 0) || (a == fcu->totvert - 1)) { - /* set both handles to have same horizontal value as keyframe */ - if (fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) { - bezt->vec[0][1] = bezt->vec[2][1] = bezt->vec[1][1]; - /* remember that these keyframes are special, they don't need to be adjusted */ - bezt->f5 = HD_AUTOTYPE_SPECIAL; - } - } - } - - /* avoid total smoothing failure on duplicate keyframes (can happen during grab) */ - if (prev && prev->vec[1][0] >= bezt->vec[1][0]) { - prev->f5 = bezt->f5 = HD_AUTOTYPE_SPECIAL; - } - - /* advance pointers for next iteration */ - prev = bezt; - - if (a == 1) { - next = cycle_offset_triple(cycle, &tmp, &fcu->bezt[1], first, last); - } - else { - next++; - } - - bezt++; - } - - /* if cyclic extrapolation and Auto Clamp has triggered, ensure it is symmetric */ - if (cycle && (first->f5 != HD_AUTOTYPE_NORMAL || last->f5 != HD_AUTOTYPE_NORMAL)) { - first->vec[0][1] = first->vec[2][1] = first->vec[1][1]; - last->vec[0][1] = last->vec[2][1] = last->vec[1][1]; - first->f5 = last->f5 = HD_AUTOTYPE_SPECIAL; - } - - /* do a second pass for auto handle: compute the handle to have 0 accelaration step */ - if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) { - BKE_nurb_handle_smooth_fcurve(fcu->bezt, fcu->totvert, cycle); - } + BezTriple *bezt, *prev, *next; + int a = fcu->totvert; + + /* Error checking: + * - need at least two points + * - need bezier keys + * - only bezier-interpolation has handles (for now) + */ + if (ELEM(NULL, fcu, fcu->bezt) || (a < 2) /*|| ELEM(fcu->ipo, BEZT_IPO_CONST, BEZT_IPO_LIN)*/) + return; + + /* if the first modifier is Cycles, smooth the curve through the cycle */ + BezTriple *first = &fcu->bezt[0], *last = &fcu->bezt[fcu->totvert - 1]; + BezTriple tmp; + + bool cycle = 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); + next = (bezt + 1); + + /* loop over all beztriples, adjusting handles */ + while (a--) { + /* clamp timing of handles to be on either side of beztriple */ + if (bezt->vec[0][0] > bezt->vec[1][0]) + bezt->vec[0][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 */ + BKE_nurb_handle_calc(bezt, prev, next, true, fcu->auto_smoothing); + + /* for automatic ease in and out */ + if (BEZT_IS_AUTOH(bezt) && !cycle) { + /* only do this on first or last beztriple */ + if ((a == 0) || (a == fcu->totvert - 1)) { + /* set both handles to have same horizontal value as keyframe */ + if (fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) { + bezt->vec[0][1] = bezt->vec[2][1] = bezt->vec[1][1]; + /* remember that these keyframes are special, they don't need to be adjusted */ + bezt->f5 = HD_AUTOTYPE_SPECIAL; + } + } + } + + /* avoid total smoothing failure on duplicate keyframes (can happen during grab) */ + if (prev && prev->vec[1][0] >= bezt->vec[1][0]) { + prev->f5 = bezt->f5 = HD_AUTOTYPE_SPECIAL; + } + + /* advance pointers for next iteration */ + prev = bezt; + + if (a == 1) { + next = cycle_offset_triple(cycle, &tmp, &fcu->bezt[1], first, last); + } + else { + next++; + } + + bezt++; + } + + /* if cyclic extrapolation and Auto Clamp has triggered, ensure it is symmetric */ + if (cycle && (first->f5 != HD_AUTOTYPE_NORMAL || last->f5 != HD_AUTOTYPE_NORMAL)) { + first->vec[0][1] = first->vec[2][1] = first->vec[1][1]; + last->vec[0][1] = last->vec[2][1] = last->vec[1][1]; + first->f5 = last->f5 = HD_AUTOTYPE_SPECIAL; + } + + /* do a second pass for auto handle: compute the handle to have 0 accelaration step */ + if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) { + BKE_nurb_handle_smooth_fcurve(fcu->bezt, fcu->totvert, cycle); + } } void testhandles_fcurve(FCurve *fcu, const bool use_handle) { - BezTriple *bezt; - unsigned int a; + BezTriple *bezt; + unsigned int a; - /* only beztriples have handles (bpoints don't though) */ - if (ELEM(NULL, fcu, fcu->bezt)) - return; + /* only beztriples have handles (bpoints don't though) */ + if (ELEM(NULL, fcu, fcu->bezt)) + return; - /* loop over beztriples */ - for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { - BKE_nurb_bezt_handle_test(bezt, use_handle); - } + /* loop over beztriples */ + for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { + BKE_nurb_bezt_handle_test(bezt, use_handle); + } - /* recalculate handles */ - calchandles_fcurve(fcu); + /* recalculate handles */ + calchandles_fcurve(fcu); } /* This function sorts BezTriples so that they are arranged in chronological order, @@ -1042,74 +1083,74 @@ void testhandles_fcurve(FCurve *fcu, const bool use_handle) */ void sort_time_fcurve(FCurve *fcu) { - bool ok = true; - - /* keep adjusting order of beztriples until nothing moves (bubble-sort) */ - while (ok) { - ok = 0; - - /* currently, will only be needed when there are beztriples */ - if (fcu->bezt) { - BezTriple *bezt; - unsigned int a; - - /* loop over ALL points to adjust position in array and recalculate handles */ - for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { - /* check if thee's a next beztriple which we could try to swap with current */ - if (a < (fcu->totvert - 1)) { - /* swap if one is after the other (and indicate that order has changed) */ - if (bezt->vec[1][0] > (bezt + 1)->vec[1][0]) { - SWAP(BezTriple, *bezt, *(bezt + 1)); - ok = 1; - } - - /* if either one of both of the points exceeds crosses over the keyframe time... */ - if ( (bezt->vec[0][0] > bezt->vec[1][0]) && (bezt->vec[2][0] < bezt->vec[1][0]) ) { - /* swap handles if they have switched sides for some reason */ - swap_v2_v2(bezt->vec[0], bezt->vec[2]); - } - else { - /* clamp handles */ - CLAMP_MAX(bezt->vec[0][0], bezt->vec[1][0]); - CLAMP_MIN(bezt->vec[2][0], bezt->vec[1][0]); - } - } - } - } - } + bool ok = true; + + /* keep adjusting order of beztriples until nothing moves (bubble-sort) */ + while (ok) { + ok = 0; + + /* currently, will only be needed when there are beztriples */ + if (fcu->bezt) { + BezTriple *bezt; + unsigned int a; + + /* loop over ALL points to adjust position in array and recalculate handles */ + for (a = 0, bezt = fcu->bezt; a < fcu->totvert; a++, bezt++) { + /* check if thee's a next beztriple which we could try to swap with current */ + if (a < (fcu->totvert - 1)) { + /* swap if one is after the other (and indicate that order has changed) */ + if (bezt->vec[1][0] > (bezt + 1)->vec[1][0]) { + SWAP(BezTriple, *bezt, *(bezt + 1)); + ok = 1; + } + + /* if either one of both of the points exceeds crosses over the keyframe time... */ + if ((bezt->vec[0][0] > bezt->vec[1][0]) && (bezt->vec[2][0] < bezt->vec[1][0])) { + /* swap handles if they have switched sides for some reason */ + swap_v2_v2(bezt->vec[0], bezt->vec[2]); + } + else { + /* clamp handles */ + CLAMP_MAX(bezt->vec[0][0], bezt->vec[1][0]); + CLAMP_MIN(bezt->vec[2][0], bezt->vec[1][0]); + } + } + } + } + } } /* This function tests if any BezTriples are out of order, thus requiring a sort */ short test_time_fcurve(FCurve *fcu) { - unsigned int a; - - /* sanity checks */ - if (fcu == NULL) - return 0; - - /* currently, only need to test beztriples */ - if (fcu->bezt) { - BezTriple *bezt; - - /* loop through all BezTriples, stopping when one exceeds the one after it */ - for (a = 0, bezt = fcu->bezt; a < (fcu->totvert - 1); a++, bezt++) { - if (bezt->vec[1][0] > (bezt + 1)->vec[1][0]) - return 1; - } - } - else if (fcu->fpt) { - FPoint *fpt; - - /* loop through all FPoints, stopping when one exceeds the one after it */ - for (a = 0, fpt = fcu->fpt; a < (fcu->totvert - 1); a++, fpt++) { - if (fpt->vec[0] > (fpt + 1)->vec[0]) - return 1; - } - } - - /* none need any swapping */ - return 0; + unsigned int a; + + /* sanity checks */ + if (fcu == NULL) + return 0; + + /* currently, only need to test beztriples */ + if (fcu->bezt) { + BezTriple *bezt; + + /* loop through all BezTriples, stopping when one exceeds the one after it */ + for (a = 0, bezt = fcu->bezt; a < (fcu->totvert - 1); a++, bezt++) { + if (bezt->vec[1][0] > (bezt + 1)->vec[1][0]) + return 1; + } + } + else if (fcu->fpt) { + FPoint *fpt; + + /* loop through all FPoints, stopping when one exceeds the one after it */ + for (a = 0, fpt = fcu->fpt; a < (fcu->totvert - 1); a++, fpt++) { + if (fpt->vec[0] > (fpt + 1)->vec[0]) + return 1; + } + } + + /* none need any swapping */ + return 0; } /* ***************************** Drivers ********************************* */ @@ -1118,216 +1159,224 @@ short test_time_fcurve(FCurve *fcu) /* TypeInfo for Driver Variables (dvti) */ typedef struct DriverVarTypeInfo { - /* evaluation callback */ - float (*get_value)(ChannelDriver *driver, DriverVar *dvar); + /* evaluation callback */ + float (*get_value)(ChannelDriver *driver, DriverVar *dvar); - /* allocation of target slots */ - int num_targets; /* number of target slots required */ - const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots */ - short target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */ + /* allocation of target slots */ + int num_targets; /* number of target slots required */ + const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots */ + short target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */ } DriverVarTypeInfo; /* Macro to begin definitions */ -#define BEGIN_DVAR_TYPEDEF(type) \ - { +#define BEGIN_DVAR_TYPEDEF(type) { /* Macro to end definitions */ -#define END_DVAR_TYPEDEF \ - } +#define END_DVAR_TYPEDEF } /* ......... */ static ID *dtar_id_ensure_proxy_from(ID *id) { - if (id && GS(id->name) == ID_OB && ((Object *)id)->proxy_from) - return (ID *)(((Object *)id)->proxy_from); - return id; + if (id && GS(id->name) == ID_OB && ((Object *)id)->proxy_from) + return (ID *)(((Object *)id)->proxy_from); + return id; } /* Helper function to obtain a value using RNA from the specified source (for evaluating drivers) */ static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) { - PointerRNA id_ptr, ptr; - PropertyRNA *prop; - ID *id; - int index = -1; - float value = 0.0f; - - /* sanity check */ - if (ELEM(NULL, driver, dtar)) - return 0.0f; - - id = dtar_id_ensure_proxy_from(dtar->id); - - /* error check for missing pointer... */ - if (id == NULL) { - if (G.debug & G_DEBUG) { - CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); - } - - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - return 0.0f; - } - - /* get RNA-pointer for the ID-block given in target */ - RNA_id_pointer_create(id, &id_ptr); - - /* get property to read from, and get value as appropriate */ - if (RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { - if (RNA_property_array_check(prop)) { - /* array */ - if ((index >= 0) && (index < RNA_property_array_length(&ptr, prop))) { - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - value = (float)RNA_property_boolean_get_index(&ptr, prop, index); - break; - case PROP_INT: - value = (float)RNA_property_int_get_index(&ptr, prop, index); - break; - case PROP_FLOAT: - value = RNA_property_float_get_index(&ptr, prop, index); - break; - default: - break; - } - } - else { - /* out of bounds */ - if (G.debug & G_DEBUG) { - CLOG_ERROR(&LOG, "Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)", - id->name, dtar->rna_path, index); - } - - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - return 0.0f; - } - } - else { - /* not an array */ - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - value = (float)RNA_property_boolean_get(&ptr, prop); - break; - case PROP_INT: - value = (float)RNA_property_int_get(&ptr, prop); - break; - case PROP_FLOAT: - value = RNA_property_float_get(&ptr, prop); - break; - case PROP_ENUM: - value = (float)RNA_property_enum_get(&ptr, prop); - break; - default: - break; - } - } - } - else { - /* path couldn't be resolved */ - if (G.debug & G_DEBUG) { - CLOG_ERROR(&LOG, "Driver Evaluation Error: cannot resolve target for %s -> %s", id->name, dtar->rna_path); - } - - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - return 0.0f; - } - - /* if we're still here, we should be ok... */ - dtar->flag &= ~DTAR_FLAG_INVALID; - return value; + PointerRNA id_ptr, ptr; + PropertyRNA *prop; + ID *id; + int index = -1; + float value = 0.0f; + + /* sanity check */ + if (ELEM(NULL, driver, dtar)) + return 0.0f; + + id = dtar_id_ensure_proxy_from(dtar->id); + + /* error check for missing pointer... */ + if (id == NULL) { + if (G.debug & G_DEBUG) { + CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); + } + + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return 0.0f; + } + + /* get RNA-pointer for the ID-block given in target */ + RNA_id_pointer_create(id, &id_ptr); + + /* get property to read from, and get value as appropriate */ + if (RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { + if (RNA_property_array_check(prop)) { + /* array */ + if ((index >= 0) && (index < RNA_property_array_length(&ptr, prop))) { + switch (RNA_property_type(prop)) { + case PROP_BOOLEAN: + value = (float)RNA_property_boolean_get_index(&ptr, prop, index); + break; + case PROP_INT: + value = (float)RNA_property_int_get_index(&ptr, prop, index); + break; + case PROP_FLOAT: + value = RNA_property_float_get_index(&ptr, prop, index); + break; + default: + break; + } + } + else { + /* out of bounds */ + if (G.debug & G_DEBUG) { + CLOG_ERROR(&LOG, + "Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)", + id->name, + dtar->rna_path, + index); + } + + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return 0.0f; + } + } + else { + /* not an array */ + switch (RNA_property_type(prop)) { + case PROP_BOOLEAN: + value = (float)RNA_property_boolean_get(&ptr, prop); + break; + case PROP_INT: + value = (float)RNA_property_int_get(&ptr, prop); + break; + case PROP_FLOAT: + value = RNA_property_float_get(&ptr, prop); + break; + case PROP_ENUM: + value = (float)RNA_property_enum_get(&ptr, prop); + break; + default: + break; + } + } + } + else { + /* path couldn't be resolved */ + if (G.debug & G_DEBUG) { + CLOG_ERROR(&LOG, + "Driver Evaluation Error: cannot resolve target for %s -> %s", + id->name, + dtar->rna_path); + } + + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return 0.0f; + } + + /* if we're still here, we should be ok... */ + dtar->flag &= ~DTAR_FLAG_INVALID; + return value; } /** * Same as 'dtar_get_prop_val'. but get the RNA property. */ -bool driver_get_variable_property( - ChannelDriver *driver, DriverTarget *dtar, - PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) +bool driver_get_variable_property(ChannelDriver *driver, + DriverTarget *dtar, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index) { - PointerRNA id_ptr; - PointerRNA ptr; - PropertyRNA *prop; - ID *id; - int index = -1; - - /* sanity check */ - if (ELEM(NULL, driver, dtar)) - return false; - - id = dtar_id_ensure_proxy_from(dtar->id); - - /* error check for missing pointer... */ - if (id == NULL) { - if (G.debug & G_DEBUG) { - CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); - } - - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - return false; - } - - /* get RNA-pointer for the ID-block given in target */ - RNA_id_pointer_create(id, &id_ptr); - - /* get property to read from, and get value as appropriate */ - if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') { - ptr = PointerRNA_NULL; - prop = NULL; /* ok */ - } - else if (RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { - /* ok */ - } - else { - /* path couldn't be resolved */ - if (G.debug & G_DEBUG) { - CLOG_ERROR(&LOG, "Driver Evaluation Error: cannot resolve target for %s -> %s", id->name, dtar->rna_path); - } - - ptr = PointerRNA_NULL; - *r_prop = NULL; - *r_index = -1; - - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - return false; - } - - *r_ptr = ptr; - *r_prop = prop; - *r_index = index; - - /* if we're still here, we should be ok... */ - dtar->flag &= ~DTAR_FLAG_INVALID; - return true; + PointerRNA id_ptr; + PointerRNA ptr; + PropertyRNA *prop; + ID *id; + int index = -1; + + /* sanity check */ + if (ELEM(NULL, driver, dtar)) + return false; + + id = dtar_id_ensure_proxy_from(dtar->id); + + /* error check for missing pointer... */ + if (id == NULL) { + if (G.debug & G_DEBUG) { + CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); + } + + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return false; + } + + /* get RNA-pointer for the ID-block given in target */ + RNA_id_pointer_create(id, &id_ptr); + + /* get property to read from, and get value as appropriate */ + if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') { + ptr = PointerRNA_NULL; + prop = NULL; /* ok */ + } + else if (RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { + /* ok */ + } + else { + /* path couldn't be resolved */ + if (G.debug & G_DEBUG) { + CLOG_ERROR(&LOG, + "Driver Evaluation Error: cannot resolve target for %s -> %s", + id->name, + dtar->rna_path); + } + + ptr = PointerRNA_NULL; + *r_prop = NULL; + *r_index = -1; + + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return false; + } + + *r_ptr = ptr; + *r_prop = prop; + *r_index = index; + + /* if we're still here, we should be ok... */ + dtar->flag &= ~DTAR_FLAG_INVALID; + return true; } static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) { - short valid_targets = 0; - - DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar) - { - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - - /* check if this target has valid data */ - if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { - /* invalid target, so will not have enough targets */ - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - } - else { - /* target seems to be OK now... */ - dtar->flag &= ~DTAR_FLAG_INVALID; - valid_targets++; - } - } - DRIVER_TARGETS_LOOPER_END; - - return valid_targets; + short valid_targets = 0; + + DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { + Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + + /* check if this target has valid data */ + if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { + /* invalid target, so will not have enough targets */ + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + } + else { + /* target seems to be OK now... */ + dtar->flag &= ~DTAR_FLAG_INVALID; + valid_targets++; + } + } + DRIVER_TARGETS_LOOPER_END; + + return valid_targets; } /* ......... */ @@ -1335,323 +1384,329 @@ static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) /* evaluate 'single prop' driver variable */ static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar) { - /* just evaluate the first target slot */ - return dtar_get_prop_val(driver, &dvar->targets[0]); + /* just evaluate the first target slot */ + return dtar_get_prop_val(driver, &dvar->targets[0]); } /* evaluate 'rotation difference' driver variable */ static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) { - short valid_targets = driver_check_valid_targets(driver, dvar); - - /* make sure we have enough valid targets to use - all or nothing for now... */ - if (driver_check_valid_targets(driver, dvar) != 2) { - if (G.debug & G_DEBUG) { - CLOG_WARN(&LOG, "RotDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)", - valid_targets, dvar->targets[0].id, dvar->targets[1].id); - } - return 0.0f; - } - - float (*mat[2])[4]; - - /* NOTE: for now, these are all just worldspace */ - for (int i = 0; i < 2; i++) { - /* get pointer to loc values to store in */ - DriverTarget *dtar = &dvar->targets[i]; - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - bPoseChannel *pchan; - - /* after the checks above, the targets should be valid here... */ - BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); - - /* try to get posechannel */ - pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); - - /* check if object or bone */ - if (pchan) { - /* bone */ - mat[i] = pchan->pose_mat; - } - else { - /* object */ - mat[i] = ob->obmat; - } - } - - float q1[4], q2[4], quat[4], angle; - - /* use the final posed locations */ - mat4_to_quat(q1, mat[0]); - mat4_to_quat(q2, mat[1]); - - invert_qt_normalized(q1); - mul_qt_qtqt(quat, q1, q2); - angle = 2.0f * (saacos(quat[0])); - angle = ABS(angle); - - return (angle > (float)M_PI) ? (float)((2.0f * (float)M_PI) - angle) : (float)(angle); + short valid_targets = driver_check_valid_targets(driver, dvar); + + /* make sure we have enough valid targets to use - all or nothing for now... */ + if (driver_check_valid_targets(driver, dvar) != 2) { + if (G.debug & G_DEBUG) { + CLOG_WARN(&LOG, + "RotDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)", + valid_targets, + dvar->targets[0].id, + dvar->targets[1].id); + } + return 0.0f; + } + + float(*mat[2])[4]; + + /* NOTE: for now, these are all just worldspace */ + for (int i = 0; i < 2; i++) { + /* get pointer to loc values to store in */ + DriverTarget *dtar = &dvar->targets[i]; + Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + bPoseChannel *pchan; + + /* after the checks above, the targets should be valid here... */ + BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); + + /* try to get posechannel */ + pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); + + /* check if object or bone */ + if (pchan) { + /* bone */ + mat[i] = pchan->pose_mat; + } + else { + /* object */ + mat[i] = ob->obmat; + } + } + + float q1[4], q2[4], quat[4], angle; + + /* use the final posed locations */ + mat4_to_quat(q1, mat[0]); + mat4_to_quat(q2, mat[1]); + + invert_qt_normalized(q1); + mul_qt_qtqt(quat, q1, q2); + angle = 2.0f * (saacos(quat[0])); + angle = ABS(angle); + + return (angle > (float)M_PI) ? (float)((2.0f * (float)M_PI) - angle) : (float)(angle); } /* evaluate 'location difference' driver variable */ /* TODO: this needs to take into account space conversions... */ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) { - float loc1[3] = {0.0f, 0.0f, 0.0f}; - float loc2[3] = {0.0f, 0.0f, 0.0f}; - short valid_targets = driver_check_valid_targets(driver, dvar); - - /* make sure we have enough valid targets to use - all or nothing for now... */ - if (valid_targets < dvar->num_targets) { - if (G.debug & G_DEBUG) { - CLOG_WARN(&LOG, "LocDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)", - valid_targets, dvar->targets[0].id, dvar->targets[1].id); - } - return 0.0f; - } - - /* SECOND PASS: get two location values */ - /* NOTE: for now, these are all just worldspace */ - DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar) - { - /* get pointer to loc values to store in */ - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - bPoseChannel *pchan; - float tmp_loc[3]; - - /* after the checks above, the targets should be valid here... */ - BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); - - /* try to get posechannel */ - pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); - - /* check if object or bone */ - if (pchan) { - /* bone */ - if (dtar->flag & DTAR_FLAG_LOCALSPACE) { - if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - float mat[4][4]; - - /* extract transform just like how the constraints do it! */ - copy_m4_m4(mat, pchan->pose_mat); - BKE_constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); - - /* ... and from that, we get our transform */ - copy_v3_v3(tmp_loc, mat[3]); - } - else { - /* transform space (use transform values directly) */ - copy_v3_v3(tmp_loc, pchan->loc); - } - } - else { - /* convert to worldspace */ - copy_v3_v3(tmp_loc, pchan->pose_head); - mul_m4_v3(ob->obmat, tmp_loc); - } - } - else { - /* object */ - if (dtar->flag & DTAR_FLAG_LOCALSPACE) { - if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* XXX: this should practically be the same as transform space... */ - float mat[4][4]; - - /* extract transform just like how the constraints do it! */ - copy_m4_m4(mat, ob->obmat); - BKE_constraint_mat_convertspace(ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); - - /* ... and from that, we get our transform */ - copy_v3_v3(tmp_loc, mat[3]); - } - else { - /* transform space (use transform values directly) */ - copy_v3_v3(tmp_loc, ob->loc); - } - } - else { - /* worldspace */ - copy_v3_v3(tmp_loc, ob->obmat[3]); - } - } - - /* copy the location to the right place */ - if (tarIndex) { - copy_v3_v3(loc2, tmp_loc); - } - else { - copy_v3_v3(loc1, tmp_loc); - } - } - DRIVER_TARGETS_LOOPER_END; - - - /* if we're still here, there should now be two targets to use, - * so just take the length of the vector between these points - */ - return len_v3v3(loc1, loc2); + float loc1[3] = {0.0f, 0.0f, 0.0f}; + float loc2[3] = {0.0f, 0.0f, 0.0f}; + short valid_targets = driver_check_valid_targets(driver, dvar); + + /* make sure we have enough valid targets to use - all or nothing for now... */ + if (valid_targets < dvar->num_targets) { + if (G.debug & G_DEBUG) { + CLOG_WARN(&LOG, + "LocDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)", + valid_targets, + dvar->targets[0].id, + dvar->targets[1].id); + } + return 0.0f; + } + + /* SECOND PASS: get two location values */ + /* NOTE: for now, these are all just worldspace */ + DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { + /* get pointer to loc values to store in */ + Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + bPoseChannel *pchan; + float tmp_loc[3]; + + /* after the checks above, the targets should be valid here... */ + BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); + + /* try to get posechannel */ + pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); + + /* check if object or bone */ + if (pchan) { + /* bone */ + if (dtar->flag & DTAR_FLAG_LOCALSPACE) { + if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { + float mat[4][4]; + + /* extract transform just like how the constraints do it! */ + copy_m4_m4(mat, pchan->pose_mat); + BKE_constraint_mat_convertspace( + ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); + + /* ... and from that, we get our transform */ + copy_v3_v3(tmp_loc, mat[3]); + } + else { + /* transform space (use transform values directly) */ + copy_v3_v3(tmp_loc, pchan->loc); + } + } + else { + /* convert to worldspace */ + copy_v3_v3(tmp_loc, pchan->pose_head); + mul_m4_v3(ob->obmat, tmp_loc); + } + } + else { + /* object */ + if (dtar->flag & DTAR_FLAG_LOCALSPACE) { + if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { + /* XXX: this should practically be the same as transform space... */ + float mat[4][4]; + + /* extract transform just like how the constraints do it! */ + copy_m4_m4(mat, ob->obmat); + BKE_constraint_mat_convertspace( + ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); + + /* ... and from that, we get our transform */ + copy_v3_v3(tmp_loc, mat[3]); + } + else { + /* transform space (use transform values directly) */ + copy_v3_v3(tmp_loc, ob->loc); + } + } + else { + /* worldspace */ + copy_v3_v3(tmp_loc, ob->obmat[3]); + } + } + + /* copy the location to the right place */ + if (tarIndex) { + copy_v3_v3(loc2, tmp_loc); + } + else { + copy_v3_v3(loc1, tmp_loc); + } + } + DRIVER_TARGETS_LOOPER_END; + + /* if we're still here, there should now be two targets to use, + * so just take the length of the vector between these points + */ + return len_v3v3(loc1, loc2); } /* evaluate 'transform channel' driver variable */ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) { - DriverTarget *dtar = &dvar->targets[0]; - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - bPoseChannel *pchan; - float mat[4][4]; - float oldEul[3] = {0.0f, 0.0f, 0.0f}; - bool use_eulers = false; - short rot_order = ROT_MODE_EUL; - - /* check if this target has valid data */ - if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { - /* invalid target, so will not have enough targets */ - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - return 0.0f; - } - else { - /* target should be valid now */ - dtar->flag &= ~DTAR_FLAG_INVALID; - } - - /* try to get posechannel */ - pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); - - /* check if object or bone, and get transform matrix accordingly - * - "useEulers" code is used to prevent the problems associated with non-uniqueness - * of euler decomposition from matrices [#20870] - * - localspace is for [#21384], where parent results are not wanted - * but local-consts is for all the common "corrective-shapes-for-limbs" situations - */ - if (pchan) { - /* bone */ - if (pchan->rotmode > 0) { - copy_v3_v3(oldEul, pchan->eul); - rot_order = pchan->rotmode; - use_eulers = true; - } - - if (dtar->flag & DTAR_FLAG_LOCALSPACE) { - if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* just like how the constraints do it! */ - copy_m4_m4(mat, pchan->pose_mat); - BKE_constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); - } - else { - /* specially calculate local matrix, since chan_mat is not valid - * since it stores delta transform of pose_mat so that deforms work - * so it cannot be used here for "transform" space - */ - BKE_pchan_to_mat4(pchan, mat); - } - } - else { - /* worldspace matrix */ - mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat); - } - } - else { - /* object */ - if (ob->rotmode > 0) { - copy_v3_v3(oldEul, ob->rot); - rot_order = ob->rotmode; - use_eulers = true; - } - - if (dtar->flag & DTAR_FLAG_LOCALSPACE) { - if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* just like how the constraints do it! */ - copy_m4_m4(mat, ob->obmat); - BKE_constraint_mat_convertspace(ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); - } - else { - /* transforms to matrix */ - BKE_object_to_mat4(ob, mat); - } - } - else { - /* worldspace matrix - just the good-old one */ - copy_m4_m4(mat, ob->obmat); - } - } - - /* check which transform */ - if (dtar->transChan >= MAX_DTAR_TRANSCHAN_TYPES) { - /* not valid channel */ - return 0.0f; - } - else if (dtar->transChan >= DTAR_TRANSCHAN_SCALEX) { - /* Extract scale, and choose the right axis, - * inline 'mat4_to_size'. */ - return len_v3(mat[dtar->transChan - DTAR_TRANSCHAN_SCALEX]); - } - else if (dtar->transChan >= DTAR_TRANSCHAN_ROTX) { - /* extract rotation as eulers (if needed) - * - definitely if rotation order isn't eulers already - * - if eulers, then we have 2 options: - * a) decompose transform matrix as required, then try to make eulers from - * there compatible with original values - * b) [NOT USED] directly use the original values (no decomposition) - * - only an option for "transform space", if quality is really bad with a) - */ - float eul[3]; - - mat4_to_eulO(eul, rot_order, mat); - - if (use_eulers) { - compatible_eul(eul, oldEul); - } - - return eul[dtar->transChan - DTAR_TRANSCHAN_ROTX]; - } - else { - /* extract location and choose right axis */ - return mat[3][dtar->transChan]; - } + DriverTarget *dtar = &dvar->targets[0]; + Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + bPoseChannel *pchan; + float mat[4][4]; + float oldEul[3] = {0.0f, 0.0f, 0.0f}; + bool use_eulers = false; + short rot_order = ROT_MODE_EUL; + + /* check if this target has valid data */ + if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { + /* invalid target, so will not have enough targets */ + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return 0.0f; + } + else { + /* target should be valid now */ + dtar->flag &= ~DTAR_FLAG_INVALID; + } + + /* try to get posechannel */ + pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); + + /* check if object or bone, and get transform matrix accordingly + * - "useEulers" code is used to prevent the problems associated with non-uniqueness + * of euler decomposition from matrices [#20870] + * - localspace is for [#21384], where parent results are not wanted + * but local-consts is for all the common "corrective-shapes-for-limbs" situations + */ + if (pchan) { + /* bone */ + if (pchan->rotmode > 0) { + copy_v3_v3(oldEul, pchan->eul); + rot_order = pchan->rotmode; + use_eulers = true; + } + + if (dtar->flag & DTAR_FLAG_LOCALSPACE) { + if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { + /* just like how the constraints do it! */ + copy_m4_m4(mat, pchan->pose_mat); + BKE_constraint_mat_convertspace( + ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); + } + else { + /* specially calculate local matrix, since chan_mat is not valid + * since it stores delta transform of pose_mat so that deforms work + * so it cannot be used here for "transform" space + */ + BKE_pchan_to_mat4(pchan, mat); + } + } + else { + /* worldspace matrix */ + mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat); + } + } + else { + /* object */ + if (ob->rotmode > 0) { + copy_v3_v3(oldEul, ob->rot); + rot_order = ob->rotmode; + use_eulers = true; + } + + if (dtar->flag & DTAR_FLAG_LOCALSPACE) { + if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { + /* just like how the constraints do it! */ + copy_m4_m4(mat, ob->obmat); + BKE_constraint_mat_convertspace( + ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); + } + else { + /* transforms to matrix */ + BKE_object_to_mat4(ob, mat); + } + } + else { + /* worldspace matrix - just the good-old one */ + copy_m4_m4(mat, ob->obmat); + } + } + + /* check which transform */ + if (dtar->transChan >= MAX_DTAR_TRANSCHAN_TYPES) { + /* not valid channel */ + return 0.0f; + } + else if (dtar->transChan >= DTAR_TRANSCHAN_SCALEX) { + /* Extract scale, and choose the right axis, + * inline 'mat4_to_size'. */ + return len_v3(mat[dtar->transChan - DTAR_TRANSCHAN_SCALEX]); + } + else if (dtar->transChan >= DTAR_TRANSCHAN_ROTX) { + /* extract rotation as eulers (if needed) + * - definitely if rotation order isn't eulers already + * - if eulers, then we have 2 options: + * a) decompose transform matrix as required, then try to make eulers from + * there compatible with original values + * b) [NOT USED] directly use the original values (no decomposition) + * - only an option for "transform space", if quality is really bad with a) + */ + float eul[3]; + + mat4_to_eulO(eul, rot_order, mat); + + if (use_eulers) { + compatible_eul(eul, oldEul); + } + + return eul[dtar->transChan - DTAR_TRANSCHAN_ROTX]; + } + else { + /* extract location and choose right axis */ + return mat[3][dtar->transChan]; + } } /* ......... */ /* Table of Driver Varaiable Type Info Data */ static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = { - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) - dvar_eval_singleProp, /* eval callback */ - 1, /* number of targets used */ - {"Property"}, /* UI names for targets */ - {0} /* flags */ - END_DVAR_TYPEDEF, - - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) - dvar_eval_rotDiff, /* eval callback */ - 2, /* number of targets used */ - {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ - {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ - END_DVAR_TYPEDEF, - - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) - dvar_eval_locDiff, /* eval callback */ - 2, /* number of targets used */ - {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ - {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ - END_DVAR_TYPEDEF, - - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) - dvar_eval_transChan, /* eval callback */ - 1, /* number of targets used */ - {"Object/Bone"}, /* UI names for targets */ - {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ - END_DVAR_TYPEDEF, + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* eval callback */ + 1, /* number of targets used */ + {"Property"}, /* UI names for targets */ + {0} /* flags */ + END_DVAR_TYPEDEF, + + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */ + 2, /* number of targets used */ + {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ + {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, + DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + END_DVAR_TYPEDEF, + + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */ + 2, /* number of targets used */ + {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ + {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, + DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + END_DVAR_TYPEDEF, + + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* eval callback */ + 1, /* number of targets used */ + {"Object/Bone"}, /* UI names for targets */ + {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + END_DVAR_TYPEDEF, }; /* Get driver variable typeinfo */ static const DriverVarTypeInfo *get_dvar_typeinfo(int type) { - /* check if valid type */ - if ((type >= 0) && (type < MAX_DVAR_TYPES)) - return &dvar_types[type]; - else - return NULL; + /* check if valid type */ + if ((type >= 0) && (type < MAX_DVAR_TYPES)) + return &dvar_types[type]; + else + return NULL; } /* Driver API --------------------------------- */ @@ -1659,345 +1714,355 @@ static const DriverVarTypeInfo *get_dvar_typeinfo(int type) /* Perform actual freeing driver variable and remove it from the given list */ void driver_free_variable(ListBase *variables, DriverVar *dvar) { - /* sanity checks */ - if (dvar == NULL) - return; - - /* free target vars - * - need to go over all of them, not just up to the ones that are used - * currently, since there may be some lingering RNA paths from - * previous users needing freeing - */ - DRIVER_TARGETS_LOOPER_BEGIN(dvar) - { - /* free RNA path if applicable */ - if (dtar->rna_path) - MEM_freeN(dtar->rna_path); - } - DRIVER_TARGETS_LOOPER_END; - - /* remove the variable from the driver */ - BLI_freelinkN(variables, dvar); + /* sanity checks */ + if (dvar == NULL) + return; + + /* free target vars + * - need to go over all of them, not just up to the ones that are used + * currently, since there may be some lingering RNA paths from + * previous users needing freeing + */ + DRIVER_TARGETS_LOOPER_BEGIN (dvar) { + /* free RNA path if applicable */ + if (dtar->rna_path) + MEM_freeN(dtar->rna_path); + } + DRIVER_TARGETS_LOOPER_END; + + /* remove the variable from the driver */ + BLI_freelinkN(variables, dvar); } /* Free the driver variable and do extra updates */ void driver_free_variable_ex(ChannelDriver *driver, DriverVar *dvar) { - /* remove and free the driver variable */ - driver_free_variable(&driver->variables, dvar); + /* remove and free the driver variable */ + driver_free_variable(&driver->variables, dvar); - /* since driver variables are cached, the expression needs re-compiling too */ - BKE_driver_invalidate_expression(driver, false, true); + /* since driver variables are cached, the expression needs re-compiling too */ + BKE_driver_invalidate_expression(driver, false, true); } /* Copy driver variables from src_vars list to dst_vars list */ void driver_variables_copy(ListBase *dst_vars, const ListBase *src_vars) { - BLI_assert(BLI_listbase_is_empty(dst_vars)); - BLI_duplicatelist(dst_vars, src_vars); - - for (DriverVar *dvar = dst_vars->first; dvar; dvar = dvar->next) { - /* need to go over all targets so that we don't leave any dangling paths */ - DRIVER_TARGETS_LOOPER_BEGIN(dvar) - { - /* make a copy of target's rna path if available */ - if (dtar->rna_path) - dtar->rna_path = MEM_dupallocN(dtar->rna_path); - } - DRIVER_TARGETS_LOOPER_END; - } + BLI_assert(BLI_listbase_is_empty(dst_vars)); + BLI_duplicatelist(dst_vars, src_vars); + + for (DriverVar *dvar = dst_vars->first; dvar; dvar = dvar->next) { + /* need to go over all targets so that we don't leave any dangling paths */ + DRIVER_TARGETS_LOOPER_BEGIN (dvar) { + /* make a copy of target's rna path if available */ + if (dtar->rna_path) + dtar->rna_path = MEM_dupallocN(dtar->rna_path); + } + DRIVER_TARGETS_LOOPER_END; + } } /* Change the type of driver variable */ void driver_change_variable_type(DriverVar *dvar, int type) { - const DriverVarTypeInfo *dvti = get_dvar_typeinfo(type); - - /* sanity check */ - if (ELEM(NULL, dvar, dvti)) - return; - - /* set the new settings */ - dvar->type = type; - dvar->num_targets = dvti->num_targets; - - /* make changes to the targets based on the defines for these types - * NOTE: only need to make sure the ones we're using here are valid... - */ - DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar) - { - short flags = dvti->target_flags[tarIndex]; - - /* store the flags */ - dtar->flag = flags; - - /* object ID types only, or idtype not yet initialized */ - if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) - dtar->idtype = ID_OB; - } - DRIVER_TARGETS_LOOPER_END; + const DriverVarTypeInfo *dvti = get_dvar_typeinfo(type); + + /* sanity check */ + if (ELEM(NULL, dvar, dvti)) + return; + + /* set the new settings */ + dvar->type = type; + dvar->num_targets = dvti->num_targets; + + /* make changes to the targets based on the defines for these types + * NOTE: only need to make sure the ones we're using here are valid... + */ + DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { + short flags = dvti->target_flags[tarIndex]; + + /* store the flags */ + dtar->flag = flags; + + /* object ID types only, or idtype not yet initialized */ + if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) + dtar->idtype = ID_OB; + } + DRIVER_TARGETS_LOOPER_END; } /* Validate driver name (after being renamed) */ void driver_variable_name_validate(DriverVar *dvar) { - /* Special character blacklist */ - const char special_char_blacklist[] = { - '~', '`', '!', '@', '#', '$', '%', '^', '&', '*', '+', '=', '-', - '/', '\\', '?', ':', ';', '<', '>', '{', '}', '[', ']', '|', - ' ', '.', '\t', '\n', '\r', - }; - - /* sanity checks */ - if (dvar == NULL) - return; - - /* clear all invalid-name flags */ - dvar->flag &= ~DVAR_ALL_INVALID_FLAGS; - - /* 0) Zero-length identifiers are not allowed */ - if (dvar->name[0] == '\0') { - dvar->flag |= DVAR_FLAG_INVALID_EMPTY; - } - - /* 1) Must start with a letter */ - /* XXX: We assume that valid unicode letters in other languages are ok too, hence the blacklisting */ - if (IN_RANGE_INCL(dvar->name[0], '0', '9')) { - dvar->flag |= DVAR_FLAG_INVALID_START_NUM; - } - else if (dvar->name[0] == '_') { - /* NOTE: We don't allow names to start with underscores (i.e. it helps when ruling out security risks) */ - dvar->flag |= DVAR_FLAG_INVALID_START_CHAR; - } - - /* 2) Must not contain invalid stuff in the middle of the string */ - if (strchr(dvar->name, ' ')) { - dvar->flag |= DVAR_FLAG_INVALID_HAS_SPACE; - } - if (strchr(dvar->name, '.')) { - dvar->flag |= DVAR_FLAG_INVALID_HAS_DOT; - } - - /* 3) Check for special characters - Either at start, or in the middle */ - for (int i = 0; i < sizeof(special_char_blacklist); i++) { - char *match = strchr(dvar->name, special_char_blacklist[i]); - - if (match == dvar->name) - dvar->flag |= DVAR_FLAG_INVALID_START_CHAR; - else if (match != NULL) - dvar->flag |= DVAR_FLAG_INVALID_HAS_SPECIAL; - } - - /* 4) Check if the name is a reserved keyword - * NOTE: These won't confuse Python, but it will be impossible to use the variable - * in an expression without Python misinterpreting what these are for - */ + /* Special character blacklist */ + const char special_char_blacklist[] = { + '~', '`', '!', '@', '#', '$', '%', '^', '&', '*', '+', '=', '-', '/', '\\', + '?', ':', ';', '<', '>', '{', '}', '[', ']', '|', ' ', '.', '\t', '\n', '\r', + }; + + /* sanity checks */ + if (dvar == NULL) + return; + + /* clear all invalid-name flags */ + dvar->flag &= ~DVAR_ALL_INVALID_FLAGS; + + /* 0) Zero-length identifiers are not allowed */ + if (dvar->name[0] == '\0') { + dvar->flag |= DVAR_FLAG_INVALID_EMPTY; + } + + /* 1) Must start with a letter */ + /* XXX: We assume that valid unicode letters in other languages are ok too, hence the blacklisting */ + if (IN_RANGE_INCL(dvar->name[0], '0', '9')) { + dvar->flag |= DVAR_FLAG_INVALID_START_NUM; + } + else if (dvar->name[0] == '_') { + /* NOTE: We don't allow names to start with underscores (i.e. it helps when ruling out security risks) */ + dvar->flag |= DVAR_FLAG_INVALID_START_CHAR; + } + + /* 2) Must not contain invalid stuff in the middle of the string */ + if (strchr(dvar->name, ' ')) { + dvar->flag |= DVAR_FLAG_INVALID_HAS_SPACE; + } + if (strchr(dvar->name, '.')) { + dvar->flag |= DVAR_FLAG_INVALID_HAS_DOT; + } + + /* 3) Check for special characters - Either at start, or in the middle */ + for (int i = 0; i < sizeof(special_char_blacklist); i++) { + char *match = strchr(dvar->name, special_char_blacklist[i]); + + if (match == dvar->name) + dvar->flag |= DVAR_FLAG_INVALID_START_CHAR; + else if (match != NULL) + dvar->flag |= DVAR_FLAG_INVALID_HAS_SPECIAL; + } + + /* 4) Check if the name is a reserved keyword + * NOTE: These won't confuse Python, but it will be impossible to use the variable + * in an expression without Python misinterpreting what these are for + */ #ifdef WITH_PYTHON - if (BPY_string_is_keyword(dvar->name)) { - dvar->flag |= DVAR_FLAG_INVALID_PY_KEYWORD; - } + if (BPY_string_is_keyword(dvar->name)) { + dvar->flag |= DVAR_FLAG_INVALID_PY_KEYWORD; + } #endif - /* If any these conditions match, the name is invalid */ - if (dvar->flag & DVAR_ALL_INVALID_FLAGS) - dvar->flag |= DVAR_FLAG_INVALID_NAME; + /* If any these conditions match, the name is invalid */ + if (dvar->flag & DVAR_ALL_INVALID_FLAGS) + dvar->flag |= DVAR_FLAG_INVALID_NAME; } /* Add a new driver variable */ DriverVar *driver_add_new_variable(ChannelDriver *driver) { - DriverVar *dvar; + DriverVar *dvar; - /* sanity checks */ - if (driver == NULL) - return NULL; + /* sanity checks */ + if (driver == NULL) + return NULL; - /* make a new variable */ - dvar = MEM_callocN(sizeof(DriverVar), "DriverVar"); - BLI_addtail(&driver->variables, dvar); + /* make a new variable */ + dvar = MEM_callocN(sizeof(DriverVar), "DriverVar"); + BLI_addtail(&driver->variables, dvar); - /* give the variable a 'unique' name */ - strcpy(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var")); - BLI_uniquename(&driver->variables, dvar, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"), '_', - offsetof(DriverVar, name), sizeof(dvar->name)); + /* give the variable a 'unique' name */ + strcpy(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var")); + BLI_uniquename(&driver->variables, + dvar, + CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"), + '_', + offsetof(DriverVar, name), + sizeof(dvar->name)); - /* set the default type to 'single prop' */ - driver_change_variable_type(dvar, DVAR_TYPE_SINGLE_PROP); + /* set the default type to 'single prop' */ + driver_change_variable_type(dvar, DVAR_TYPE_SINGLE_PROP); - /* since driver variables are cached, the expression needs re-compiling too */ - BKE_driver_invalidate_expression(driver, false, true); + /* since driver variables are cached, the expression needs re-compiling too */ + BKE_driver_invalidate_expression(driver, false, true); - /* return the target */ - return dvar; + /* return the target */ + return dvar; } /* This frees the driver itself */ void fcurve_free_driver(FCurve *fcu) { - ChannelDriver *driver; - DriverVar *dvar, *dvarn; + ChannelDriver *driver; + DriverVar *dvar, *dvarn; - /* sanity checks */ - if (ELEM(NULL, fcu, fcu->driver)) - return; - driver = fcu->driver; + /* sanity checks */ + if (ELEM(NULL, fcu, fcu->driver)) + return; + driver = fcu->driver; - /* free driver targets */ - for (dvar = driver->variables.first; dvar; dvar = dvarn) { - dvarn = dvar->next; - driver_free_variable_ex(driver, dvar); - } + /* free driver targets */ + for (dvar = driver->variables.first; dvar; dvar = dvarn) { + dvarn = dvar->next; + driver_free_variable_ex(driver, dvar); + } #ifdef WITH_PYTHON - /* free compiled driver expression */ - if (driver->expr_comp) - BPY_DECREF(driver->expr_comp); + /* free compiled driver expression */ + if (driver->expr_comp) + BPY_DECREF(driver->expr_comp); #endif - BLI_expr_pylike_free(driver->expr_simple); + BLI_expr_pylike_free(driver->expr_simple); - /* free driver itself, then set F-Curve's point to this to NULL (as the curve may still be used) */ - MEM_freeN(driver); - fcu->driver = NULL; + /* free driver itself, then set F-Curve's point to this to NULL (as the curve may still be used) */ + MEM_freeN(driver); + fcu->driver = NULL; } /* This makes a copy of the given driver */ ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver) { - ChannelDriver *ndriver; + ChannelDriver *ndriver; - /* sanity checks */ - if (driver == NULL) - return NULL; + /* sanity checks */ + if (driver == NULL) + return NULL; - /* copy all data */ - ndriver = MEM_dupallocN(driver); - ndriver->expr_comp = NULL; - ndriver->expr_simple = NULL; + /* copy all data */ + ndriver = MEM_dupallocN(driver); + ndriver->expr_comp = NULL; + ndriver->expr_simple = NULL; - /* copy variables */ - BLI_listbase_clear(&ndriver->variables); /* to get rid of refs to non-copied data (that's still used on original) */ - driver_variables_copy(&ndriver->variables, &driver->variables); + /* copy variables */ + BLI_listbase_clear( + &ndriver + ->variables); /* to get rid of refs to non-copied data (that's still used on original) */ + driver_variables_copy(&ndriver->variables, &driver->variables); - /* return the new driver */ - return ndriver; + /* return the new driver */ + return ndriver; } /* Driver Expression Evaluation --------------- */ static ExprPyLike_Parsed *driver_compile_simple_expr_impl(ChannelDriver *driver) { - /* Prepare parameter names. */ - int names_len = BLI_listbase_count(&driver->variables); - const char **names = BLI_array_alloca(names, names_len + 1); - int i = 0; + /* Prepare parameter names. */ + int names_len = BLI_listbase_count(&driver->variables); + const char **names = BLI_array_alloca(names, names_len + 1); + int i = 0; - names[i++] = "frame"; + names[i++] = "frame"; - for (DriverVar *dvar = driver->variables.first; dvar; dvar = dvar->next) { - names[i++] = dvar->name; - } + for (DriverVar *dvar = driver->variables.first; dvar; dvar = dvar->next) { + names[i++] = dvar->name; + } - return BLI_expr_pylike_parse(driver->expression, names, names_len + 1); + return BLI_expr_pylike_parse(driver->expression, names, names_len + 1); } -static bool driver_evaluate_simple_expr(ChannelDriver *driver, ExprPyLike_Parsed *expr, float *result, float time) +static bool driver_evaluate_simple_expr(ChannelDriver *driver, + ExprPyLike_Parsed *expr, + float *result, + float time) { - /* Prepare parameter values. */ - int vars_len = BLI_listbase_count(&driver->variables); - double *vars = BLI_array_alloca(vars, vars_len + 1); - int i = 0; - - vars[i++] = time; - - for (DriverVar *dvar = driver->variables.first; dvar; dvar = dvar->next) { - vars[i++] = driver_get_variable_value(driver, dvar); - } - - /* Evaluate expression. */ - double result_val; - eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, vars, vars_len + 1, &result_val); - const char *message; - - switch (status) { - case EXPR_PYLIKE_SUCCESS: - if (isfinite(result_val)) { - *result = (float)result_val; - } - return true; - - case EXPR_PYLIKE_DIV_BY_ZERO: - case EXPR_PYLIKE_MATH_ERROR: - message = (status == EXPR_PYLIKE_DIV_BY_ZERO) ? "Division by Zero" : "Math Domain Error"; - CLOG_ERROR(&LOG, "%s in Driver: '%s'", message, driver->expression); - - driver->flag |= DRIVER_FLAG_INVALID; - return true; - - default: - /* arriving here means a bug, not user error */ - CLOG_ERROR(&LOG, "simple driver expression evaluation failed: '%s'", driver->expression); - return false; - } + /* Prepare parameter values. */ + int vars_len = BLI_listbase_count(&driver->variables); + double *vars = BLI_array_alloca(vars, vars_len + 1); + int i = 0; + + vars[i++] = time; + + for (DriverVar *dvar = driver->variables.first; dvar; dvar = dvar->next) { + vars[i++] = driver_get_variable_value(driver, dvar); + } + + /* Evaluate expression. */ + double result_val; + eExprPyLike_EvalStatus status = BLI_expr_pylike_eval(expr, vars, vars_len + 1, &result_val); + const char *message; + + switch (status) { + case EXPR_PYLIKE_SUCCESS: + if (isfinite(result_val)) { + *result = (float)result_val; + } + return true; + + case EXPR_PYLIKE_DIV_BY_ZERO: + case EXPR_PYLIKE_MATH_ERROR: + message = (status == EXPR_PYLIKE_DIV_BY_ZERO) ? "Division by Zero" : "Math Domain Error"; + CLOG_ERROR(&LOG, "%s in Driver: '%s'", message, driver->expression); + + driver->flag |= DRIVER_FLAG_INVALID; + return true; + + default: + /* arriving here means a bug, not user error */ + CLOG_ERROR(&LOG, "simple driver expression evaluation failed: '%s'", driver->expression); + return false; + } } /* Compile and cache the driver expression if necessary, with thread safety. */ static bool driver_compile_simple_expr(ChannelDriver *driver) { - if (driver->expr_simple != NULL) { - return true; - } + if (driver->expr_simple != NULL) { + return true; + } - if (driver->type != DRIVER_TYPE_PYTHON) { - return false; - } + if (driver->type != DRIVER_TYPE_PYTHON) { + return false; + } - /* It's safe to parse in multiple threads; at worst it'll - * waste some effort, but in return avoids mutex contention. */ - ExprPyLike_Parsed *expr = driver_compile_simple_expr_impl(driver); + /* It's safe to parse in multiple threads; at worst it'll + * waste some effort, but in return avoids mutex contention. */ + ExprPyLike_Parsed *expr = driver_compile_simple_expr_impl(driver); - /* Store the result if the field is still NULL, or discard - * it if another thread got here first. */ - if (atomic_cas_ptr((void **)&driver->expr_simple, NULL, expr) != NULL) { - BLI_expr_pylike_free(expr); - } + /* Store the result if the field is still NULL, or discard + * it if another thread got here first. */ + if (atomic_cas_ptr((void **)&driver->expr_simple, NULL, expr) != NULL) { + BLI_expr_pylike_free(expr); + } - return true; + return true; } /* Try using the simple expression evaluator to compute the result of the driver. * On success, stores the result and returns true; on failure result is set to 0. */ -static bool driver_try_evaluate_simple_expr(ChannelDriver *driver, ChannelDriver *driver_orig, float *result, float time) +static bool driver_try_evaluate_simple_expr(ChannelDriver *driver, + ChannelDriver *driver_orig, + float *result, + float time) { - *result = 0.0f; + *result = 0.0f; - return driver_compile_simple_expr(driver_orig) && - BLI_expr_pylike_is_valid(driver_orig->expr_simple) && - driver_evaluate_simple_expr(driver, driver_orig->expr_simple, result, time); + return driver_compile_simple_expr(driver_orig) && + BLI_expr_pylike_is_valid(driver_orig->expr_simple) && + driver_evaluate_simple_expr(driver, driver_orig->expr_simple, result, time); } /* Check if the expression in the driver conforms to the simple subset. */ bool BKE_driver_has_simple_expression(ChannelDriver *driver) { - return driver_compile_simple_expr(driver) && BLI_expr_pylike_is_valid(driver->expr_simple); + return driver_compile_simple_expr(driver) && BLI_expr_pylike_is_valid(driver->expr_simple); } /* Reset cached compiled expression data */ -void BKE_driver_invalidate_expression(ChannelDriver *driver, bool expr_changed, bool varname_changed) +void BKE_driver_invalidate_expression(ChannelDriver *driver, + bool expr_changed, + bool varname_changed) { - if (expr_changed || varname_changed) { - BLI_expr_pylike_free(driver->expr_simple); - driver->expr_simple = NULL; - } + if (expr_changed || varname_changed) { + BLI_expr_pylike_free(driver->expr_simple); + driver->expr_simple = NULL; + } #ifdef WITH_PYTHON - if (expr_changed) { - driver->flag |= DRIVER_FLAG_RECOMPILE; - } + if (expr_changed) { + driver->flag |= DRIVER_FLAG_RECOMPILE; + } - if (varname_changed) { - driver->flag |= DRIVER_FLAG_RENAMEVAR; - } + if (varname_changed) { + driver->flag |= DRIVER_FLAG_RENAMEVAR; + } #endif } @@ -2006,24 +2071,24 @@ void BKE_driver_invalidate_expression(ChannelDriver *driver, bool expr_changed, /* Evaluate a Driver Variable to get a value that contributes to the final */ float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar) { - const DriverVarTypeInfo *dvti; + const DriverVarTypeInfo *dvti; - /* sanity check */ - if (ELEM(NULL, driver, dvar)) - return 0.0f; + /* sanity check */ + if (ELEM(NULL, driver, dvar)) + return 0.0f; - /* call the relevant callbacks to get the variable value - * using the variable type info, storing the obtained value - * in dvar->curval so that drivers can be debugged - */ - dvti = get_dvar_typeinfo(dvar->type); + /* call the relevant callbacks to get the variable value + * using the variable type info, storing the obtained value + * in dvar->curval so that drivers can be debugged + */ + dvti = get_dvar_typeinfo(dvar->type); - if (dvti && dvti->get_value) - dvar->curval = dvti->get_value(driver, dvar); - else - dvar->curval = 0.0f; + if (dvti && dvti->get_value) + dvar->curval = dvti->get_value(driver, dvar); + else + dvar->curval = 0.0f; - return dvar->curval; + return dvar->curval; } /* Evaluate an Channel-Driver to get a 'time' value to use instead of "evaltime" @@ -2031,113 +2096,113 @@ float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar) * - has to return a float value * - driver_orig is where we cache Python expressions, in case of COW */ -float evaluate_driver(PathResolvedRNA *anim_rna, ChannelDriver *driver, ChannelDriver *driver_orig, const float evaltime) +float evaluate_driver(PathResolvedRNA *anim_rna, + ChannelDriver *driver, + ChannelDriver *driver_orig, + const float evaltime) { - DriverVar *dvar; - - /* check if driver can be evaluated */ - if (driver_orig->flag & DRIVER_FLAG_INVALID) - return 0.0f; - - switch (driver->type) { - case DRIVER_TYPE_AVERAGE: /* average values of driver targets */ - case DRIVER_TYPE_SUM: /* sum values of driver targets */ - { - /* check how many variables there are first (i.e. just one?) */ - if (BLI_listbase_is_single(&driver->variables)) { - /* just one target, so just use that */ - dvar = driver->variables.first; - driver->curval = driver_get_variable_value(driver, dvar); - } - else { - /* more than one target, so average the values of the targets */ - float value = 0.0f; - int tot = 0; - - /* loop through targets, adding (hopefully we don't get any overflow!) */ - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - value += driver_get_variable_value(driver, dvar); - tot++; - } - - /* perform operations on the total if appropriate */ - if (driver->type == DRIVER_TYPE_AVERAGE) - driver->curval = tot ? (value / (float)tot) : 0.0f; - else - driver->curval = value; - } - break; - } - case DRIVER_TYPE_MIN: /* smallest value */ - case DRIVER_TYPE_MAX: /* largest value */ - { - float value = 0.0f; - - /* loop through the variables, getting the values and comparing them to existing ones */ - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - /* get value */ - float tmp_val = driver_get_variable_value(driver, dvar); - - /* store this value if appropriate */ - if (dvar->prev) { - /* check if greater/smaller than the baseline */ - if (driver->type == DRIVER_TYPE_MAX) { - /* max? */ - if (tmp_val > value) - value = tmp_val; - } - else { - /* min? */ - if (tmp_val < value) - value = tmp_val; - } - } - else { - /* first item - make this the baseline for comparisons */ - value = tmp_val; - } - } - - /* store value in driver */ - driver->curval = value; - break; - } - case DRIVER_TYPE_PYTHON: /* expression */ - { - /* check for empty or invalid expression */ - if ( (driver_orig->expression[0] == '\0') || - (driver_orig->flag & DRIVER_FLAG_INVALID) ) - { - driver->curval = 0.0f; - } - else if (!driver_try_evaluate_simple_expr(driver, driver_orig, &driver->curval, evaltime)) { + DriverVar *dvar; + + /* check if driver can be evaluated */ + if (driver_orig->flag & DRIVER_FLAG_INVALID) + return 0.0f; + + switch (driver->type) { + case DRIVER_TYPE_AVERAGE: /* average values of driver targets */ + case DRIVER_TYPE_SUM: /* sum values of driver targets */ + { + /* check how many variables there are first (i.e. just one?) */ + if (BLI_listbase_is_single(&driver->variables)) { + /* just one target, so just use that */ + dvar = driver->variables.first; + driver->curval = driver_get_variable_value(driver, dvar); + } + else { + /* more than one target, so average the values of the targets */ + float value = 0.0f; + int tot = 0; + + /* loop through targets, adding (hopefully we don't get any overflow!) */ + for (dvar = driver->variables.first; dvar; dvar = dvar->next) { + value += driver_get_variable_value(driver, dvar); + tot++; + } + + /* perform operations on the total if appropriate */ + if (driver->type == DRIVER_TYPE_AVERAGE) + driver->curval = tot ? (value / (float)tot) : 0.0f; + else + driver->curval = value; + } + break; + } + case DRIVER_TYPE_MIN: /* smallest value */ + case DRIVER_TYPE_MAX: /* largest value */ + { + float value = 0.0f; + + /* loop through the variables, getting the values and comparing them to existing ones */ + for (dvar = driver->variables.first; dvar; dvar = dvar->next) { + /* get value */ + float tmp_val = driver_get_variable_value(driver, dvar); + + /* store this value if appropriate */ + if (dvar->prev) { + /* check if greater/smaller than the baseline */ + if (driver->type == DRIVER_TYPE_MAX) { + /* max? */ + if (tmp_val > value) + value = tmp_val; + } + else { + /* min? */ + if (tmp_val < value) + value = tmp_val; + } + } + else { + /* first item - make this the baseline for comparisons */ + value = tmp_val; + } + } + + /* store value in driver */ + driver->curval = value; + break; + } + case DRIVER_TYPE_PYTHON: /* expression */ + { + /* check for empty or invalid expression */ + if ((driver_orig->expression[0] == '\0') || (driver_orig->flag & DRIVER_FLAG_INVALID)) { + driver->curval = 0.0f; + } + else if (!driver_try_evaluate_simple_expr(driver, driver_orig, &driver->curval, evaltime)) { #ifdef WITH_PYTHON - /* this evaluates the expression using Python, and returns its result: - * - on errors it reports, then returns 0.0f - */ - BLI_mutex_lock(&python_driver_lock); + /* this evaluates the expression using Python, and returns its result: + * - on errors it reports, then returns 0.0f + */ + BLI_mutex_lock(&python_driver_lock); - driver->curval = BPY_driver_exec(anim_rna, driver, driver_orig, evaltime); + driver->curval = BPY_driver_exec(anim_rna, driver, driver_orig, evaltime); - BLI_mutex_unlock(&python_driver_lock); -#else /* WITH_PYTHON*/ - UNUSED_VARS(anim_rna, evaltime); + BLI_mutex_unlock(&python_driver_lock); +#else /* WITH_PYTHON*/ + UNUSED_VARS(anim_rna, evaltime); #endif /* WITH_PYTHON*/ - } - break; - } - default: - { - /* special 'hack' - just use stored value - * This is currently used as the mechanism which allows animated settings to be able - * to be changed via the UI. - */ - break; - } - } - - /* return value for driver */ - return driver->curval; + } + break; + } + default: { + /* special 'hack' - just use stored value + * This is currently used as the mechanism which allows animated settings to be able + * to be changed via the UI. + */ + break; + } + } + + /* return value for driver */ + return driver->curval; } /* ***************************** Curve Calculations ********************************* */ @@ -2148,606 +2213,635 @@ float evaluate_driver(PathResolvedRNA *anim_rna, ChannelDriver *driver, ChannelD */ void correct_bezpart(float v1[2], float v2[2], float v3[2], float v4[2]) { - float h1[2], h2[2], len1, len2, len, fac; - - /* calculate handle deltas */ - h1[0] = v1[0] - v2[0]; - h1[1] = v1[1] - v2[1]; - - h2[0] = v4[0] - v3[0]; - h2[1] = v4[1] - v3[1]; - - /* calculate distances: - * - len = span of time between keyframes - * - len1 = length of handle of start key - * - len2 = length of handle of end key - */ - len = v4[0] - v1[0]; - len1 = fabsf(h1[0]); - len2 = fabsf(h2[0]); - - /* if the handles have no length, no need to do any corrections */ - if ((len1 + len2) == 0.0f) - return; - - /* the two handles cross over each other, so force them - * apart using the proportion they overlap - */ - if ((len1 + len2) > len) { - fac = len / (len1 + len2); - - v2[0] = (v1[0] - fac * h1[0]); - v2[1] = (v1[1] - fac * h1[1]); - - v3[0] = (v4[0] - fac * h2[0]); - v3[1] = (v4[1] - fac * h2[1]); - } + float h1[2], h2[2], len1, len2, len, fac; + + /* calculate handle deltas */ + h1[0] = v1[0] - v2[0]; + h1[1] = v1[1] - v2[1]; + + h2[0] = v4[0] - v3[0]; + h2[1] = v4[1] - v3[1]; + + /* calculate distances: + * - len = span of time between keyframes + * - len1 = length of handle of start key + * - len2 = length of handle of end key + */ + len = v4[0] - v1[0]; + len1 = fabsf(h1[0]); + len2 = fabsf(h2[0]); + + /* if the handles have no length, no need to do any corrections */ + if ((len1 + len2) == 0.0f) + return; + + /* the two handles cross over each other, so force them + * apart using the proportion they overlap + */ + if ((len1 + len2) > len) { + fac = len / (len1 + len2); + + v2[0] = (v1[0] - fac * h1[0]); + v2[1] = (v1[1] - fac * h1[1]); + + v3[0] = (v4[0] - fac * h2[0]); + v3[1] = (v4[1] - fac * h2[1]); + } } /* find root ('zero') */ static int findzero(float x, float q0, float q1, float q2, float q3, float *o) { - double c0, c1, c2, c3, a, b, c, p, q, d, t, phi; - int nr = 0; - - c0 = q0 - x; - c1 = 3.0f * (q1 - q0); - c2 = 3.0f * (q0 - 2.0f * q1 + q2); - c3 = q3 - q0 + 3.0f * (q1 - q2); - - if (c3 != 0.0) { - a = c2 / c3; - b = c1 / c3; - c = c0 / c3; - a = a / 3; - - p = b / 3 - a * a; - q = (2 * a * a * a - a * b + c) / 2; - d = q * q + p * p * p; - - if (d > 0.0) { - t = sqrt(d); - o[0] = (float)(sqrt3d(-q + t) + sqrt3d(-q - t) - a); - - if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) return 1; - else return 0; - } - else if (d == 0.0) { - t = sqrt3d(-q); - o[0] = (float)(2 * t - a); - - if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) nr++; - o[nr] = (float)(-t - a); - - if ((o[nr] >= (float)SMALL) && (o[nr] <= 1.000001f)) return nr + 1; - else return nr; - } - else { - phi = acos(-q / sqrt(-(p * p * p))); - t = sqrt(-p); - p = cos(phi / 3); - q = sqrt(3 - 3 * p * p); - o[0] = (float)(2 * t * p - a); - - if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) nr++; - o[nr] = (float)(-t * (p + q) - a); - - if ((o[nr] >= (float)SMALL) && (o[nr] <= 1.000001f)) nr++; - o[nr] = (float)(-t * (p - q) - a); - - if ((o[nr] >= (float)SMALL) && (o[nr] <= 1.000001f)) return nr + 1; - else return nr; - } - } - else { - a = c2; - b = c1; - c = c0; - - if (a != 0.0) { - /* discriminant */ - p = b * b - 4 * a * c; - - if (p > 0) { - p = sqrt(p); - o[0] = (float)((-b - p) / (2 * a)); - - if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) nr++; - o[nr] = (float)((-b + p) / (2 * a)); - - if ((o[nr] >= (float)SMALL) && (o[nr] <= 1.000001f)) return nr + 1; - else return nr; - } - else if (p == 0) { - o[0] = (float)(-b / (2 * a)); - if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) return 1; - else return 0; - } - } - else if (b != 0.0) { - o[0] = (float)(-c / b); - - if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) return 1; - else return 0; - } - else if (c == 0.0) { - o[0] = 0.0; - return 1; - } - - return 0; - } + double c0, c1, c2, c3, a, b, c, p, q, d, t, phi; + int nr = 0; + + c0 = q0 - x; + c1 = 3.0f * (q1 - q0); + c2 = 3.0f * (q0 - 2.0f * q1 + q2); + c3 = q3 - q0 + 3.0f * (q1 - q2); + + if (c3 != 0.0) { + a = c2 / c3; + b = c1 / c3; + c = c0 / c3; + a = a / 3; + + p = b / 3 - a * a; + q = (2 * a * a * a - a * b + c) / 2; + d = q * q + p * p * p; + + if (d > 0.0) { + t = sqrt(d); + o[0] = (float)(sqrt3d(-q + t) + sqrt3d(-q - t) - a); + + if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) + return 1; + else + return 0; + } + else if (d == 0.0) { + t = sqrt3d(-q); + o[0] = (float)(2 * t - a); + + if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) + nr++; + o[nr] = (float)(-t - a); + + if ((o[nr] >= (float)SMALL) && (o[nr] <= 1.000001f)) + return nr + 1; + else + return nr; + } + else { + phi = acos(-q / sqrt(-(p * p * p))); + t = sqrt(-p); + p = cos(phi / 3); + q = sqrt(3 - 3 * p * p); + o[0] = (float)(2 * t * p - a); + + if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) + nr++; + o[nr] = (float)(-t * (p + q) - a); + + if ((o[nr] >= (float)SMALL) && (o[nr] <= 1.000001f)) + nr++; + o[nr] = (float)(-t * (p - q) - a); + + if ((o[nr] >= (float)SMALL) && (o[nr] <= 1.000001f)) + return nr + 1; + else + return nr; + } + } + else { + a = c2; + b = c1; + c = c0; + + if (a != 0.0) { + /* discriminant */ + p = b * b - 4 * a * c; + + if (p > 0) { + p = sqrt(p); + o[0] = (float)((-b - p) / (2 * a)); + + if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) + nr++; + o[nr] = (float)((-b + p) / (2 * a)); + + if ((o[nr] >= (float)SMALL) && (o[nr] <= 1.000001f)) + return nr + 1; + else + return nr; + } + else if (p == 0) { + o[0] = (float)(-b / (2 * a)); + if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) + return 1; + else + return 0; + } + } + else if (b != 0.0) { + o[0] = (float)(-c / b); + + if ((o[0] >= (float)SMALL) && (o[0] <= 1.000001f)) + return 1; + else + return 0; + } + else if (c == 0.0) { + o[0] = 0.0; + return 1; + } + + return 0; + } } static void berekeny(float f1, float f2, float f3, float f4, float *o, int b) { - float t, c0, c1, c2, c3; - int a; - - c0 = f1; - c1 = 3.0f * (f2 - f1); - c2 = 3.0f * (f1 - 2.0f * f2 + f3); - c3 = f4 - f1 + 3.0f * (f2 - f3); - - for (a = 0; a < b; a++) { - t = o[a]; - o[a] = c0 + t * c1 + t * t * c2 + t * t * t * c3; - } + float t, c0, c1, c2, c3; + int a; + + c0 = f1; + c1 = 3.0f * (f2 - f1); + c2 = 3.0f * (f1 - 2.0f * f2 + f3); + c3 = f4 - f1 + 3.0f * (f2 - f3); + + for (a = 0; a < b; a++) { + t = o[a]; + o[a] = c0 + t * c1 + t * t * c2 + t * t * t * c3; + } } - /* -------------------------- */ /* Calculate F-Curve value for 'evaltime' using BezTriple keyframes */ static float fcurve_eval_keyframes(FCurve *fcu, BezTriple *bezts, float evaltime) { - const float eps = 1.e-8f; - BezTriple *bezt, *prevbezt, *lastbezt; - float v1[2], v2[2], v3[2], v4[2], opl[32], dx, fac; - unsigned int a; - int b; - float cvalue = 0.0f; - - /* get pointers */ - a = fcu->totvert - 1; - prevbezt = bezts; - bezt = prevbezt + 1; - lastbezt = prevbezt + a; - - /* evaluation time at or past endpoints? */ - if (prevbezt->vec[1][0] >= evaltime) { - /* before or on first keyframe */ - if ( (fcu->extend == FCURVE_EXTRAPOLATE_LINEAR) && (prevbezt->ipo != BEZT_IPO_CONST) && - !(fcu->flag & FCURVE_DISCRETE_VALUES) ) - { - /* linear or bezier interpolation */ - if (prevbezt->ipo == BEZT_IPO_LIN) { - /* Use the next center point instead of our own handle for - * linear interpolated extrapolate - */ - if (fcu->totvert == 1) { - cvalue = prevbezt->vec[1][1]; - } - else { - bezt = prevbezt + 1; - dx = prevbezt->vec[1][0] - evaltime; - fac = bezt->vec[1][0] - prevbezt->vec[1][0]; - - /* prevent division by zero */ - if (fac) { - fac = (bezt->vec[1][1] - prevbezt->vec[1][1]) / fac; - cvalue = prevbezt->vec[1][1] - (fac * dx); - } - else { - cvalue = prevbezt->vec[1][1]; - } - } - } - else { - /* Use the first handle (earlier) of first BezTriple to calculate the - * gradient and thus the value of the curve at evaltime - */ - dx = prevbezt->vec[1][0] - evaltime; - fac = prevbezt->vec[1][0] - prevbezt->vec[0][0]; - - /* prevent division by zero */ - if (fac) { - fac = (prevbezt->vec[1][1] - prevbezt->vec[0][1]) / fac; - cvalue = prevbezt->vec[1][1] - (fac * dx); - } - else { - cvalue = prevbezt->vec[1][1]; - } - } - } - else { - /* constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation, - * so just extend first keyframe's value - */ - cvalue = prevbezt->vec[1][1]; - } - } - else if (lastbezt->vec[1][0] <= evaltime) { - /* after or on last keyframe */ - if ( (fcu->extend == FCURVE_EXTRAPOLATE_LINEAR) && (lastbezt->ipo != BEZT_IPO_CONST) && - !(fcu->flag & FCURVE_DISCRETE_VALUES) ) - { - /* linear or bezier interpolation */ - if (lastbezt->ipo == BEZT_IPO_LIN) { - /* Use the next center point instead of our own handle for - * linear interpolated extrapolate - */ - if (fcu->totvert == 1) { - cvalue = lastbezt->vec[1][1]; - } - else { - prevbezt = lastbezt - 1; - dx = evaltime - lastbezt->vec[1][0]; - fac = lastbezt->vec[1][0] - prevbezt->vec[1][0]; - - /* prevent division by zero */ - if (fac) { - fac = (lastbezt->vec[1][1] - prevbezt->vec[1][1]) / fac; - cvalue = lastbezt->vec[1][1] + (fac * dx); - } - else { - cvalue = lastbezt->vec[1][1]; - } - } - } - else { - /* Use the gradient of the second handle (later) of last BezTriple to calculate the - * gradient and thus the value of the curve at evaltime - */ - dx = evaltime - lastbezt->vec[1][0]; - fac = lastbezt->vec[2][0] - lastbezt->vec[1][0]; - - /* prevent division by zero */ - if (fac) { - fac = (lastbezt->vec[2][1] - lastbezt->vec[1][1]) / fac; - cvalue = lastbezt->vec[1][1] + (fac * dx); - } - else { - cvalue = lastbezt->vec[1][1]; - } - } - } - else { - /* constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation, - * so just extend last keyframe's value - */ - cvalue = lastbezt->vec[1][1]; - } - } - else { - /* evaltime occurs somewhere in the middle of the curve */ - bool exact = false; - - /* Use binary search to find appropriate keyframes... - * - * The threshold here has the following constraints: - * - 0.001 is too coarse -> We get artifacts with 2cm driver movements at 1BU = 1m (see T40332) - * - 0.00001 is too fine -> Weird errors, like selecting the wrong keyframe range (see T39207), occur. - * This lower bound was established in b888a32eee8147b028464336ad2404d8155c64dd - */ - a = binarysearch_bezt_index_ex(bezts, evaltime, fcu->totvert, 0.0001, &exact); - if (G.debug & G_DEBUG) printf("eval fcurve '%s' - %f => %u/%u, %d\n", fcu->rna_path, evaltime, a, fcu->totvert, exact); - - if (exact) { - /* index returned must be interpreted differently when it sits on top of an existing keyframe - * - that keyframe is the start of the segment we need (see action_bug_2.blend in T39207) - */ - prevbezt = bezts + a; - bezt = (a < fcu->totvert - 1) ? (prevbezt + 1) : prevbezt; - } - else { - /* index returned refers to the keyframe that the eval-time occurs *before* - * - hence, that keyframe marks the start of the segment we're dealing with - */ - bezt = bezts + a; - prevbezt = (a > 0) ? (bezt - 1) : bezt; - } - - /* use if the key is directly on the frame, rare cases this is needed else we get 0.0 instead. */ - /* XXX: consult T39207 for examples of files where failure of these checks can cause issues */ - if (exact) { - cvalue = prevbezt->vec[1][1]; - } - else if (fabsf(bezt->vec[1][0] - evaltime) < eps) { - cvalue = bezt->vec[1][1]; - } - /* evaltime occurs within the interval defined by these two keyframes */ - else if ((prevbezt->vec[1][0] <= evaltime) && (bezt->vec[1][0] >= evaltime)) { - const float begin = prevbezt->vec[1][1]; - const float change = bezt->vec[1][1] - prevbezt->vec[1][1]; - const float duration = bezt->vec[1][0] - prevbezt->vec[1][0]; - const float time = evaltime - prevbezt->vec[1][0]; - const float amplitude = prevbezt->amplitude; - const float period = prevbezt->period; - - /* value depends on interpolation mode */ - if ((prevbezt->ipo == BEZT_IPO_CONST) || (fcu->flag & FCURVE_DISCRETE_VALUES) || (duration == 0)) { - /* constant (evaltime not relevant, so no interpolation needed) */ - cvalue = prevbezt->vec[1][1]; - } - else { - switch (prevbezt->ipo) { - /* interpolation ...................................... */ - case BEZT_IPO_BEZ: - /* bezier interpolation */ - /* (v1, v2) are the first keyframe and its 2nd handle */ - v1[0] = prevbezt->vec[1][0]; - v1[1] = prevbezt->vec[1][1]; - v2[0] = prevbezt->vec[2][0]; - v2[1] = prevbezt->vec[2][1]; - /* (v3, v4) are the last keyframe's 1st handle + the last keyframe */ - v3[0] = bezt->vec[0][0]; - v3[1] = bezt->vec[0][1]; - v4[0] = bezt->vec[1][0]; - v4[1] = bezt->vec[1][1]; - - if (fabsf(v1[1] - v4[1]) < FLT_EPSILON && - fabsf(v2[1] - v3[1]) < FLT_EPSILON && - fabsf(v3[1] - v4[1]) < FLT_EPSILON) - { - /* Optimisation: If all the handles are flat/at the same values, - * the value is simply the shared value (see T40372 -> F91346) - */ - cvalue = v1[1]; - } - else { - /* adjust handles so that they don't overlap (forming a loop) */ - correct_bezpart(v1, v2, v3, v4); - - /* try to get a value for this position - if failure, try another set of points */ - b = findzero(evaltime, v1[0], v2[0], v3[0], v4[0], opl); - if (b) { - berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1); - cvalue = opl[0]; - /* break; */ - } - else { - if (G.debug & G_DEBUG) printf(" ERROR: findzero() failed at %f with %f %f %f %f\n", evaltime, v1[0], v2[0], v3[0], v4[0]); - } - } - break; - - case BEZT_IPO_LIN: - /* linear - simply linearly interpolate between values of the two keyframes */ - cvalue = BLI_easing_linear_ease(time, begin, change, duration); - break; - - /* easing ............................................ */ - case BEZT_IPO_BACK: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_back_ease_in(time, begin, change, duration, prevbezt->back); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_back_ease_in_out(time, begin, change, duration, prevbezt->back); - break; - - default: /* default/auto: same as ease out */ - cvalue = BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); - break; - } - break; - - case BEZT_IPO_BOUNCE: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_bounce_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_bounce_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_bounce_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease out */ - cvalue = BLI_easing_bounce_ease_out(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_CIRC: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_circ_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_circ_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_circ_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_circ_ease_in(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_CUBIC: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_cubic_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_cubic_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_cubic_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_cubic_ease_in(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_ELASTIC: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_elastic_ease_in_out(time, begin, change, duration, amplitude, period); - break; - - default: /* default/auto: same as ease out */ - cvalue = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); - break; - } - break; - - case BEZT_IPO_EXPO: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_expo_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_expo_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_expo_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_expo_ease_in(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_QUAD: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_quad_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_quad_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_quad_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_quad_ease_in(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_QUART: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_quart_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_quart_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_quart_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_quart_ease_in(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_QUINT: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_quint_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_quint_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_quint_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_quint_ease_in(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_SINE: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_sine_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_sine_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_sine_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_sine_ease_in(time, begin, change, duration); - break; - } - break; - - - default: - cvalue = prevbezt->vec[1][1]; - break; - } - } - } - else { - if (G.debug & G_DEBUG) printf(" ERROR: failed eval - p=%f b=%f, t=%f (%f)\n", prevbezt->vec[1][0], bezt->vec[1][0], evaltime, fabsf(bezt->vec[1][0] - evaltime)); - } - } - - /* return value */ - return cvalue; + const float eps = 1.e-8f; + BezTriple *bezt, *prevbezt, *lastbezt; + float v1[2], v2[2], v3[2], v4[2], opl[32], dx, fac; + unsigned int a; + int b; + float cvalue = 0.0f; + + /* get pointers */ + a = fcu->totvert - 1; + prevbezt = bezts; + bezt = prevbezt + 1; + lastbezt = prevbezt + a; + + /* evaluation time at or past endpoints? */ + if (prevbezt->vec[1][0] >= evaltime) { + /* before or on first keyframe */ + if ((fcu->extend == FCURVE_EXTRAPOLATE_LINEAR) && (prevbezt->ipo != BEZT_IPO_CONST) && + !(fcu->flag & FCURVE_DISCRETE_VALUES)) { + /* linear or bezier interpolation */ + if (prevbezt->ipo == BEZT_IPO_LIN) { + /* Use the next center point instead of our own handle for + * linear interpolated extrapolate + */ + if (fcu->totvert == 1) { + cvalue = prevbezt->vec[1][1]; + } + else { + bezt = prevbezt + 1; + dx = prevbezt->vec[1][0] - evaltime; + fac = bezt->vec[1][0] - prevbezt->vec[1][0]; + + /* prevent division by zero */ + if (fac) { + fac = (bezt->vec[1][1] - prevbezt->vec[1][1]) / fac; + cvalue = prevbezt->vec[1][1] - (fac * dx); + } + else { + cvalue = prevbezt->vec[1][1]; + } + } + } + else { + /* Use the first handle (earlier) of first BezTriple to calculate the + * gradient and thus the value of the curve at evaltime + */ + dx = prevbezt->vec[1][0] - evaltime; + fac = prevbezt->vec[1][0] - prevbezt->vec[0][0]; + + /* prevent division by zero */ + if (fac) { + fac = (prevbezt->vec[1][1] - prevbezt->vec[0][1]) / fac; + cvalue = prevbezt->vec[1][1] - (fac * dx); + } + else { + cvalue = prevbezt->vec[1][1]; + } + } + } + else { + /* constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation, + * so just extend first keyframe's value + */ + cvalue = prevbezt->vec[1][1]; + } + } + else if (lastbezt->vec[1][0] <= evaltime) { + /* after or on last keyframe */ + if ((fcu->extend == FCURVE_EXTRAPOLATE_LINEAR) && (lastbezt->ipo != BEZT_IPO_CONST) && + !(fcu->flag & FCURVE_DISCRETE_VALUES)) { + /* linear or bezier interpolation */ + if (lastbezt->ipo == BEZT_IPO_LIN) { + /* Use the next center point instead of our own handle for + * linear interpolated extrapolate + */ + if (fcu->totvert == 1) { + cvalue = lastbezt->vec[1][1]; + } + else { + prevbezt = lastbezt - 1; + dx = evaltime - lastbezt->vec[1][0]; + fac = lastbezt->vec[1][0] - prevbezt->vec[1][0]; + + /* prevent division by zero */ + if (fac) { + fac = (lastbezt->vec[1][1] - prevbezt->vec[1][1]) / fac; + cvalue = lastbezt->vec[1][1] + (fac * dx); + } + else { + cvalue = lastbezt->vec[1][1]; + } + } + } + else { + /* Use the gradient of the second handle (later) of last BezTriple to calculate the + * gradient and thus the value of the curve at evaltime + */ + dx = evaltime - lastbezt->vec[1][0]; + fac = lastbezt->vec[2][0] - lastbezt->vec[1][0]; + + /* prevent division by zero */ + if (fac) { + fac = (lastbezt->vec[2][1] - lastbezt->vec[1][1]) / fac; + cvalue = lastbezt->vec[1][1] + (fac * dx); + } + else { + cvalue = lastbezt->vec[1][1]; + } + } + } + else { + /* constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation, + * so just extend last keyframe's value + */ + cvalue = lastbezt->vec[1][1]; + } + } + else { + /* evaltime occurs somewhere in the middle of the curve */ + bool exact = false; + + /* Use binary search to find appropriate keyframes... + * + * The threshold here has the following constraints: + * - 0.001 is too coarse -> We get artifacts with 2cm driver movements at 1BU = 1m (see T40332) + * - 0.00001 is too fine -> Weird errors, like selecting the wrong keyframe range (see T39207), occur. + * This lower bound was established in b888a32eee8147b028464336ad2404d8155c64dd + */ + a = binarysearch_bezt_index_ex(bezts, evaltime, fcu->totvert, 0.0001, &exact); + if (G.debug & G_DEBUG) + printf( + "eval fcurve '%s' - %f => %u/%u, %d\n", fcu->rna_path, evaltime, a, fcu->totvert, exact); + + if (exact) { + /* index returned must be interpreted differently when it sits on top of an existing keyframe + * - that keyframe is the start of the segment we need (see action_bug_2.blend in T39207) + */ + prevbezt = bezts + a; + bezt = (a < fcu->totvert - 1) ? (prevbezt + 1) : prevbezt; + } + else { + /* index returned refers to the keyframe that the eval-time occurs *before* + * - hence, that keyframe marks the start of the segment we're dealing with + */ + bezt = bezts + a; + prevbezt = (a > 0) ? (bezt - 1) : bezt; + } + + /* use if the key is directly on the frame, rare cases this is needed else we get 0.0 instead. */ + /* XXX: consult T39207 for examples of files where failure of these checks can cause issues */ + if (exact) { + cvalue = prevbezt->vec[1][1]; + } + else if (fabsf(bezt->vec[1][0] - evaltime) < eps) { + cvalue = bezt->vec[1][1]; + } + /* evaltime occurs within the interval defined by these two keyframes */ + else if ((prevbezt->vec[1][0] <= evaltime) && (bezt->vec[1][0] >= evaltime)) { + const float begin = prevbezt->vec[1][1]; + const float change = bezt->vec[1][1] - prevbezt->vec[1][1]; + const float duration = bezt->vec[1][0] - prevbezt->vec[1][0]; + const float time = evaltime - prevbezt->vec[1][0]; + const float amplitude = prevbezt->amplitude; + const float period = prevbezt->period; + + /* value depends on interpolation mode */ + if ((prevbezt->ipo == BEZT_IPO_CONST) || (fcu->flag & FCURVE_DISCRETE_VALUES) || + (duration == 0)) { + /* constant (evaltime not relevant, so no interpolation needed) */ + cvalue = prevbezt->vec[1][1]; + } + else { + switch (prevbezt->ipo) { + /* interpolation ...................................... */ + case BEZT_IPO_BEZ: + /* bezier interpolation */ + /* (v1, v2) are the first keyframe and its 2nd handle */ + v1[0] = prevbezt->vec[1][0]; + v1[1] = prevbezt->vec[1][1]; + v2[0] = prevbezt->vec[2][0]; + v2[1] = prevbezt->vec[2][1]; + /* (v3, v4) are the last keyframe's 1st handle + the last keyframe */ + v3[0] = bezt->vec[0][0]; + v3[1] = bezt->vec[0][1]; + v4[0] = bezt->vec[1][0]; + v4[1] = bezt->vec[1][1]; + + if (fabsf(v1[1] - v4[1]) < FLT_EPSILON && fabsf(v2[1] - v3[1]) < FLT_EPSILON && + fabsf(v3[1] - v4[1]) < FLT_EPSILON) { + /* Optimisation: If all the handles are flat/at the same values, + * the value is simply the shared value (see T40372 -> F91346) + */ + cvalue = v1[1]; + } + else { + /* adjust handles so that they don't overlap (forming a loop) */ + correct_bezpart(v1, v2, v3, v4); + + /* try to get a value for this position - if failure, try another set of points */ + b = findzero(evaltime, v1[0], v2[0], v3[0], v4[0], opl); + if (b) { + berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1); + cvalue = opl[0]; + /* break; */ + } + else { + if (G.debug & G_DEBUG) + printf(" ERROR: findzero() failed at %f with %f %f %f %f\n", + evaltime, + v1[0], + v2[0], + v3[0], + v4[0]); + } + } + break; + + case BEZT_IPO_LIN: + /* linear - simply linearly interpolate between values of the two keyframes */ + cvalue = BLI_easing_linear_ease(time, begin, change, duration); + break; + + /* easing ............................................ */ + case BEZT_IPO_BACK: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = BLI_easing_back_ease_in(time, begin, change, duration, prevbezt->back); + break; + case BEZT_IPO_EASE_OUT: + cvalue = BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = BLI_easing_back_ease_in_out( + time, begin, change, duration, prevbezt->back); + break; + + default: /* default/auto: same as ease out */ + cvalue = BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); + break; + } + break; + + case BEZT_IPO_BOUNCE: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = BLI_easing_bounce_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = BLI_easing_bounce_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = BLI_easing_bounce_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease out */ + cvalue = BLI_easing_bounce_ease_out(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_CIRC: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = BLI_easing_circ_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = BLI_easing_circ_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = BLI_easing_circ_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + cvalue = BLI_easing_circ_ease_in(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_CUBIC: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = BLI_easing_cubic_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = BLI_easing_cubic_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = BLI_easing_cubic_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + cvalue = BLI_easing_cubic_ease_in(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_ELASTIC: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = BLI_easing_elastic_ease_in( + time, begin, change, duration, amplitude, period); + break; + case BEZT_IPO_EASE_OUT: + cvalue = BLI_easing_elastic_ease_out( + time, begin, change, duration, amplitude, period); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = BLI_easing_elastic_ease_in_out( + time, begin, change, duration, amplitude, period); + break; + + default: /* default/auto: same as ease out */ + cvalue = BLI_easing_elastic_ease_out( + time, begin, change, duration, amplitude, period); + break; + } + break; + + case BEZT_IPO_EXPO: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = BLI_easing_expo_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = BLI_easing_expo_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = BLI_easing_expo_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + cvalue = BLI_easing_expo_ease_in(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_QUAD: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = BLI_easing_quad_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = BLI_easing_quad_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = BLI_easing_quad_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + cvalue = BLI_easing_quad_ease_in(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_QUART: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = BLI_easing_quart_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = BLI_easing_quart_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = BLI_easing_quart_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + cvalue = BLI_easing_quart_ease_in(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_QUINT: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = BLI_easing_quint_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = BLI_easing_quint_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = BLI_easing_quint_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + cvalue = BLI_easing_quint_ease_in(time, begin, change, duration); + break; + } + break; + + case BEZT_IPO_SINE: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + cvalue = BLI_easing_sine_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + cvalue = BLI_easing_sine_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + cvalue = BLI_easing_sine_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + cvalue = BLI_easing_sine_ease_in(time, begin, change, duration); + break; + } + break; + + default: + cvalue = prevbezt->vec[1][1]; + break; + } + } + } + else { + if (G.debug & G_DEBUG) + printf(" ERROR: failed eval - p=%f b=%f, t=%f (%f)\n", + prevbezt->vec[1][0], + bezt->vec[1][0], + evaltime, + fabsf(bezt->vec[1][0] - evaltime)); + } + } + + /* return value */ + return cvalue; } /* Calculate F-Curve value for 'evaltime' using FPoint samples */ static float fcurve_eval_samples(FCurve *fcu, FPoint *fpts, float evaltime) { - FPoint *prevfpt, *lastfpt, *fpt; - float cvalue = 0.0f; - - /* get pointers */ - prevfpt = fpts; - lastfpt = prevfpt + fcu->totvert - 1; - - /* evaluation time at or past endpoints? */ - if (prevfpt->vec[0] >= evaltime) { - /* before or on first sample, so just extend value */ - cvalue = prevfpt->vec[1]; - } - else if (lastfpt->vec[0] <= evaltime) { - /* after or on last sample, so just extend value */ - cvalue = lastfpt->vec[1]; - } - else { - float t = fabsf(evaltime - floorf(evaltime)); - - /* find the one on the right frame (assume that these are spaced on 1-frame intervals) */ - fpt = prevfpt + ((int)evaltime - (int)prevfpt->vec[0]); - - /* if not exactly on the frame, perform linear interpolation with the next one */ - if ((t != 0.0f) && (t < 1.0f)) - cvalue = interpf(fpt->vec[1], (fpt + 1)->vec[1], 1.0f - t); - else - cvalue = fpt->vec[1]; - } - - /* return value */ - return cvalue; + FPoint *prevfpt, *lastfpt, *fpt; + float cvalue = 0.0f; + + /* get pointers */ + prevfpt = fpts; + lastfpt = prevfpt + fcu->totvert - 1; + + /* evaluation time at or past endpoints? */ + if (prevfpt->vec[0] >= evaltime) { + /* before or on first sample, so just extend value */ + cvalue = prevfpt->vec[1]; + } + else if (lastfpt->vec[0] <= evaltime) { + /* after or on last sample, so just extend value */ + cvalue = lastfpt->vec[1]; + } + else { + float t = fabsf(evaltime - floorf(evaltime)); + + /* find the one on the right frame (assume that these are spaced on 1-frame intervals) */ + fpt = prevfpt + ((int)evaltime - (int)prevfpt->vec[0]); + + /* if not exactly on the frame, perform linear interpolation with the next one */ + if ((t != 0.0f) && (t < 1.0f)) + cvalue = interpf(fpt->vec[1], (fpt + 1)->vec[1], 1.0f - t); + else + cvalue = fpt->vec[1]; + } + + /* return value */ + return cvalue; } /* ***************************** F-Curve - Evaluation ********************************* */ @@ -2757,118 +2851,119 @@ static float fcurve_eval_samples(FCurve *fcu, FPoint *fpts, float evaltime) */ static float evaluate_fcurve_ex(FCurve *fcu, float evaltime, float cvalue) { - FModifierStackStorage *storage; - float devaltime; - - /* evaluate modifiers which modify time to evaluate the base curve at */ - storage = evaluate_fmodifiers_storage_new(&fcu->modifiers); - devaltime = evaluate_time_fmodifiers(storage, &fcu->modifiers, fcu, cvalue, evaltime); - - /* evaluate curve-data - * - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying - * F-Curve modifier on the stack requested the curve to be evaluated at - */ - if (fcu->bezt) - cvalue = fcurve_eval_keyframes(fcu, fcu->bezt, devaltime); - else if (fcu->fpt) - cvalue = fcurve_eval_samples(fcu, fcu->fpt, devaltime); - - /* evaluate modifiers */ - evaluate_value_fmodifiers(storage, &fcu->modifiers, fcu, &cvalue, devaltime); - - evaluate_fmodifiers_storage_free(storage); - - /* if curve can only have integral values, perform truncation (i.e. drop the decimal part) - * here so that the curve can be sampled correctly - */ - if (fcu->flag & FCURVE_INT_VALUES) - cvalue = floorf(cvalue + 0.5f); - - /* return evaluated value */ - return cvalue; + FModifierStackStorage *storage; + float devaltime; + + /* evaluate modifiers which modify time to evaluate the base curve at */ + storage = evaluate_fmodifiers_storage_new(&fcu->modifiers); + devaltime = evaluate_time_fmodifiers(storage, &fcu->modifiers, fcu, cvalue, evaltime); + + /* evaluate curve-data + * - 'devaltime' instead of 'evaltime', as this is the time that the last time-modifying + * F-Curve modifier on the stack requested the curve to be evaluated at + */ + if (fcu->bezt) + cvalue = fcurve_eval_keyframes(fcu, fcu->bezt, devaltime); + else if (fcu->fpt) + cvalue = fcurve_eval_samples(fcu, fcu->fpt, devaltime); + + /* evaluate modifiers */ + evaluate_value_fmodifiers(storage, &fcu->modifiers, fcu, &cvalue, devaltime); + + evaluate_fmodifiers_storage_free(storage); + + /* if curve can only have integral values, perform truncation (i.e. drop the decimal part) + * here so that the curve can be sampled correctly + */ + if (fcu->flag & FCURVE_INT_VALUES) + cvalue = floorf(cvalue + 0.5f); + + /* return evaluated value */ + return cvalue; } float evaluate_fcurve(FCurve *fcu, float evaltime) { - BLI_assert(fcu->driver == NULL); + BLI_assert(fcu->driver == NULL); - return evaluate_fcurve_ex(fcu, evaltime, 0.0); + return evaluate_fcurve_ex(fcu, evaltime, 0.0); } float evaluate_fcurve_only_curve(FCurve *fcu, float evaltime) { - /* Can be used to evaluate the (keyframed) fcurve only. - * Also works for driver-fcurves when the driver itself is not relevant. - * E.g. when inserting a keyframe in a driver fcurve. */ - return evaluate_fcurve_ex(fcu, evaltime, 0.0); + /* Can be used to evaluate the (keyframed) fcurve only. + * Also works for driver-fcurves when the driver itself is not relevant. + * E.g. when inserting a keyframe in a driver fcurve. */ + return evaluate_fcurve_ex(fcu, evaltime, 0.0); } -float evaluate_fcurve_driver(PathResolvedRNA *anim_rna, FCurve *fcu, ChannelDriver *driver_orig, float evaltime) +float evaluate_fcurve_driver(PathResolvedRNA *anim_rna, + FCurve *fcu, + ChannelDriver *driver_orig, + float evaltime) { - BLI_assert(fcu->driver != NULL); - float cvalue = 0.0f; - - /* if there is a driver (only if this F-Curve is acting as 'driver'), evaluate it to find value to use as "evaltime" - * since drivers essentially act as alternative input (i.e. in place of 'time') for F-Curves - */ - if (fcu->driver) { - /* evaltime now serves as input for the curve */ - evaltime = evaluate_driver(anim_rna, fcu->driver, driver_orig, evaltime); - - /* only do a default 1-1 mapping if it's unlikely that anything else will set a value... */ - if (fcu->totvert == 0) { - FModifier *fcm; - bool do_linear = true; - - /* out-of-range F-Modifiers will block, as will those which just plain overwrite the values - * XXX: additive is a bit more dicey; it really depends then if things are in range or not... - */ - for (fcm = fcu->modifiers.first; fcm; fcm = fcm->next) { - /* if there are range-restrictions, we must definitely block [#36950] */ - if ((fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) == 0 || - ((fcm->sfra <= evaltime) && (fcm->efra >= evaltime)) ) - { - /* within range: here it probably doesn't matter, though we'd want to check on additive... */ - } - else { - /* outside range: modifier shouldn't contribute to the curve here, though it does in other areas, - * so neither should the driver! - */ - do_linear = false; - } - } - - /* only copy over results if none of the modifiers disagreed with this */ - if (do_linear) { - cvalue = evaltime; - } - } - } - - return evaluate_fcurve_ex(fcu, evaltime, cvalue); + BLI_assert(fcu->driver != NULL); + float cvalue = 0.0f; + + /* if there is a driver (only if this F-Curve is acting as 'driver'), evaluate it to find value to use as "evaltime" + * since drivers essentially act as alternative input (i.e. in place of 'time') for F-Curves + */ + if (fcu->driver) { + /* evaltime now serves as input for the curve */ + evaltime = evaluate_driver(anim_rna, fcu->driver, driver_orig, evaltime); + + /* only do a default 1-1 mapping if it's unlikely that anything else will set a value... */ + if (fcu->totvert == 0) { + FModifier *fcm; + bool do_linear = true; + + /* out-of-range F-Modifiers will block, as will those which just plain overwrite the values + * XXX: additive is a bit more dicey; it really depends then if things are in range or not... + */ + for (fcm = fcu->modifiers.first; fcm; fcm = fcm->next) { + /* if there are range-restrictions, we must definitely block [#36950] */ + if ((fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) == 0 || + ((fcm->sfra <= evaltime) && (fcm->efra >= evaltime))) { + /* within range: here it probably doesn't matter, though we'd want to check on additive... */ + } + else { + /* outside range: modifier shouldn't contribute to the curve here, though it does in other areas, + * so neither should the driver! + */ + do_linear = false; + } + } + + /* only copy over results if none of the modifiers disagreed with this */ + if (do_linear) { + cvalue = evaltime; + } + } + } + + return evaluate_fcurve_ex(fcu, evaltime, cvalue); } /* Calculate the value of the given F-Curve at the given frame, and set its curval */ float calculate_fcurve(PathResolvedRNA *anim_rna, FCurve *fcu, float evaltime) { - /* only calculate + set curval (overriding the existing value) if curve has - * any data which warrants this... - */ - if ((fcu->totvert) || (fcu->driver && !(fcu->driver->flag & DRIVER_FLAG_INVALID)) || - list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE)) - { - /* calculate and set curval (evaluates driver too if necessary) */ - float curval; - if (fcu->driver) { - curval = evaluate_fcurve_driver(anim_rna, fcu, fcu->driver, evaltime); - } - else { - curval = evaluate_fcurve(fcu, evaltime); - } - fcu->curval = curval; /* debug display only, not thread safe! */ - return curval; - } - else { - return 0.0f; - } + /* only calculate + set curval (overriding the existing value) if curve has + * any data which warrants this... + */ + if ((fcu->totvert) || (fcu->driver && !(fcu->driver->flag & DRIVER_FLAG_INVALID)) || + list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE)) { + /* calculate and set curval (evaluates driver too if necessary) */ + float curval; + if (fcu->driver) { + curval = evaluate_fcurve_driver(anim_rna, fcu, fcu->driver, evaltime); + } + else { + curval = evaluate_fcurve(fcu, evaltime); + } + fcu->curval = curval; /* debug display only, not thread safe! */ + return curval; + } + else { + return 0.0f; + } } |