/* Only delete the nominated keyframe from provided F-Curve. * Not recommended to be used many times successively. For that * there is delete_fcurve_keys(). */ void delete_fcurve_key(FCurve *fcu, int index, short do_recalc) { /* sanity check */ if (fcu == NULL) return; /* verify the index: * 1) cannot be greater than the number of available keyframes * 2) negative indices are for specifying a value from the end of the array */ if (abs(index) >= fcu->totvert) return; else if (index < 0) index += fcu->totvert; /* Delete this keyframe */ memmove(&fcu->bezt[index], &fcu->bezt[index + 1], sizeof(BezTriple) * (fcu->totvert - index - 1)); fcu->totvert--; if (fcu->totvert == 0) { if (fcu->bezt) MEM_freeN(fcu->bezt); fcu->bezt = NULL; } /* recalc handles - only if it won't cause problems */ if (do_recalc) calchandles_fcurve(fcu); }
/* Evaluates the curves between each selected keyframe on each frame, and keys the value */ void sample_fcurve (FCurve *fcu) { BezTriple *bezt, *start=NULL, *end=NULL; tempFrameValCache *value_cache, *fp; int sfra, range; int i, n, nIndex; /* find selected keyframes... once pair has been found, add keyframes */ for (i=0, bezt=fcu->bezt; i < fcu->totvert; i++, bezt++) { /* check if selected, and which end this is */ if (BEZSELECTED(bezt)) { if (start) { /* set end */ end= bezt; /* cache values then add keyframes using these values, as adding * keyframes while sampling will affect the outcome... * - only start sampling+adding from index=1, so that we don't overwrite original keyframe */ range= (int)( ceil(end->vec[1][0] - start->vec[1][0]) ); sfra= (int)( floor(start->vec[1][0]) ); if (range) { value_cache= MEM_callocN(sizeof(tempFrameValCache)*range, "IcuFrameValCache"); /* sample values */ for (n=1, fp=value_cache; n<range && fp; n++, fp++) { fp->frame= (float)(sfra + n); fp->val= evaluate_fcurve(fcu, fp->frame); } /* add keyframes with these, tagging as 'breakdowns' */ for (n=1, fp=value_cache; n<range && fp; n++, fp++) { nIndex= insert_vert_fcurve(fcu, fp->frame, fp->val, 1); BEZKEYTYPE(fcu->bezt + nIndex)= BEZT_KEYTYPE_BREAKDOWN; } /* free temp cache */ MEM_freeN(value_cache); /* as we added keyframes, we need to compensate so that bezt is at the right place */ bezt = fcu->bezt + i + range - 1; i += (range - 1); } /* bezt was selected, so it now marks the start of a whole new chain to search */ start= bezt; end= NULL; } else { /* just set start keyframe */ start= bezt; end= NULL; } } } /* recalculate channel's handles? */ calchandles_fcurve(fcu); }
static void rna_FCurve_convert_to_keyframes(FCurve *fcu, ReportList *reports, int start, int end) { if (start >= end) { BKE_reportf(reports, RPT_ERROR, "Invalid frame range (%d - %d)", start, end); } else if (fcu->bezt) { BKE_report(reports, RPT_WARNING, "FCurve has already keyframes"); } else if (!fcu->fpt) { BKE_report(reports, RPT_WARNING, "FCurve has no sample points"); } else { BezTriple *bezt; FPoint *fpt = fcu->fpt; int tot_kf = end - start; int tot_sp = fcu->totvert; bezt = fcu->bezt = MEM_callocN(sizeof(*fcu->bezt) * (size_t)tot_kf, __func__); fcu->totvert = tot_kf; /* Get first sample point to 'copy' as keyframe. */ for (; tot_sp && (fpt->vec[0] < (float)start); fpt++, tot_sp--); /* Add heading dummy flat points if needed. */ for (; tot_kf && (fpt->vec[0] > (float)start); start++, bezt++, tot_kf--) { /* Linear interpolation, of course. */ bezt->f1 = bezt->f2 = bezt->f3 = SELECT; bezt->ipo = BEZT_IPO_LIN; bezt->h1 = bezt->h2 = HD_AUTO_ANIM; bezt->vec[1][0] = (float)start; bezt->vec[1][1] = fpt->vec[1]; } /* Copy actual sample points. */ for (; tot_kf && tot_sp; start++, bezt++, tot_kf--, fpt++, tot_sp--) { /* Linear interpolation, of course. */ bezt->f1 = bezt->f2 = bezt->f3 = SELECT; bezt->ipo = BEZT_IPO_LIN; bezt->h1 = bezt->h2 = HD_AUTO_ANIM; copy_v2_v2(bezt->vec[1], fpt->vec); } /* Add leading dummy flat points if needed. */ for (fpt--; tot_kf; start++, bezt++, tot_kf--) { /* Linear interpolation, of course. */ bezt->f1 = bezt->f2 = bezt->f3 = SELECT; bezt->ipo = BEZT_IPO_LIN; bezt->h1 = bezt->h2 = HD_AUTO_ANIM; bezt->vec[1][0] = (float)start; bezt->vec[1][1] = fpt->vec[1]; } MEM_SAFE_FREE(fcu->fpt); /* Not strictly needed since we use linear interpolation, but better be consistent here. */ calchandles_fcurve(fcu); WM_main_add_notifier(NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); } }
/* update callback for active keyframe properties - base updates stuff */ static void graphedit_activekey_update_cb(bContext *UNUSED(C), void *fcu_ptr, void *UNUSED(bezt_ptr)) { FCurve *fcu = (FCurve *)fcu_ptr; /* make sure F-Curve and its handles are still valid after this editing */ sort_time_fcurve(fcu); calchandles_fcurve(fcu); }
/* Add a new F-Curve Modifier to the given F-Curve of a certain type */ FModifier *add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu) { const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(type); FModifier *fcm; /* sanity checks */ if (ELEM(NULL, modifiers, fmi)) { return NULL; } /* special checks for whether modifier can be added */ if ((modifiers->first) && (type == FMODIFIER_TYPE_CYCLES)) { /* cycles modifier must be first in stack, so for now, don't add if it can't be */ /* TODO: perhaps there is some better way, but for now, */ CLOG_STR_ERROR(&LOG, "Cannot add 'Cycles' modifier to F-Curve, as 'Cycles' modifier can only be " "first in stack."); return NULL; } /* add modifier itself */ fcm = MEM_callocN(sizeof(FModifier), "F-Curve Modifier"); fcm->type = type; fcm->flag = FMODIFIER_FLAG_EXPANDED; fcm->curve = owner_fcu; fcm->influence = 1.0f; BLI_addtail(modifiers, fcm); /* tag modifier as "active" if no other modifiers exist in the stack yet */ if (BLI_listbase_is_single(modifiers)) { fcm->flag |= FMODIFIER_FLAG_ACTIVE; } /* add modifier's data */ fcm->data = MEM_callocN(fmi->size, fmi->structName); /* init custom settings if necessary */ if (fmi->new_data) { fmi->new_data(fcm->data); } /* update the fcurve if the Cycles modifier is added */ if ((owner_fcu) && (type == FMODIFIER_TYPE_CYCLES)) { calchandles_fcurve(owner_fcu); } /* return modifier for further editing */ return fcm; }
void KX_BlenderSceneConverter::TestHandlesPhysicsObjectToAnimationIpo() { KX_SceneList *scenes = m_ketsjiEngine->CurrentScenes(); int numScenes = scenes->size(); int i; for (i = 0; i < numScenes; i++) { KX_Scene *scene = scenes->at(i); //PHY_IPhysicsEnvironment* physEnv = scene->GetPhysicsEnvironment(); CListValue *parentList = scene->GetRootParentList(); int numObjects = parentList->GetCount(); int g; for (g = 0; g < numObjects; g++) { KX_GameObject *gameObj = (KX_GameObject *)parentList->GetValue(g); if (gameObj->IsRecordAnimation()) { Object *blenderObject = gameObj->GetBlenderObject(); if (blenderObject && blenderObject->adt) { bAction *act = verify_adt_action(&blenderObject->id, false); FCurve *fcu; if (!act) { continue; } /* for now, not much choice but to run this on all curves... */ for (fcu = (FCurve *)act->curves.first; fcu; fcu = fcu->next) { /* Note: calling `sort_time_fcurve()` here is not needed, since * all keys have been added in 'right' order. */ calchandles_fcurve(fcu); } #if 0 // XXX animato Ipo* ipo = blenderObject->ipo; //create the curves, if not existing //testhandles_ipocurve checks for NULL testhandles_ipocurve(findIpoCurve((IpoCurve *)ipo->curve.first,"LocX")); testhandles_ipocurve(findIpoCurve((IpoCurve *)ipo->curve.first,"LocY")); testhandles_ipocurve(findIpoCurve((IpoCurve *)ipo->curve.first,"LocZ")); testhandles_ipocurve(findIpoCurve((IpoCurve *)ipo->curve.first,"RotX")); testhandles_ipocurve(findIpoCurve((IpoCurve *)ipo->curve.first,"RotY")); testhandles_ipocurve(findIpoCurve((IpoCurve *)ipo->curve.first,"RotZ")); #endif } } } } }
/* Only delete the nominated keyframe from provided ipo-curve. * Not recommended to be used many times successively. For that * there is delete_ipo_keys(). */ void delete_fcurve_key(FCurve *fcu, int index, short do_recalc) { /* firstly check that index is valid */ if (index < 0) index *= -1; if (fcu == NULL) return; if (index >= fcu->totvert) return; /* Delete this key */ memmove(&fcu->bezt[index], &fcu->bezt[index+1], sizeof(BezTriple)*(fcu->totvert-index-1)); fcu->totvert--; /* recalc handles - only if it won't cause problems */ if (do_recalc) calchandles_fcurve(fcu); }
/* Remove and free the given F-Modifier from the given stack */ bool remove_fmodifier(ListBase *modifiers, FModifier *fcm) { const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); /* sanity check */ if (fcm == NULL) { return false; } /* removing the cycles modifier requires a handle update */ FCurve *update_fcu = (fcm->type == FMODIFIER_TYPE_CYCLES) ? fcm->curve : NULL; /* free modifier's special data (stored inside fcm->data) */ if (fcm->data) { if (fmi && fmi->free_data) { fmi->free_data(fcm); } /* free modifier's data (fcm->data) */ MEM_freeN(fcm->data); } /* remove modifier from stack */ if (modifiers) { BLI_freelinkN(modifiers, fcm); /* update the fcurve if the Cycles modifier is removed */ if (update_fcu) { calchandles_fcurve(update_fcu); } return true; } else { /* XXX this case can probably be removed some day, as it shouldn't happen... */ CLOG_STR_ERROR(&LOG, "no modifier stack given"); MEM_freeN(fcm); return false; } }
// used to be recalc_*_ipos() where * was object or action void ANIM_editkeyframes_refresh(bAnimContext *ac) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; /* filter animation data */ filter = ANIMFILTER_DATA_VISIBLE; ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* loop over F-Curves that are likely to have been edited, and check them */ for (ale = anim_data.first; ale; ale = ale->next) { FCurve *fcu = ale->key_data; /* make sure keyframes in F-Curve are all in order, and handles are in valid positions */ sort_time_fcurve(fcu); calchandles_fcurve(fcu); } /* free temp data */ ANIM_animdata_freelist(&anim_data); }
void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data) { bAnimListElem *ale; if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { #ifdef DEBUG /* quiet assert */ for (ale = anim_data->first; ale; ale = ale->next) { ale->update = 0; } #endif return; } for (ale = anim_data->first; ale; ale = ale->next) { FCurve *fcu = ale->key_data; if (ale->update & ANIM_UPDATE_ORDER) { ale->update &= ~ANIM_UPDATE_ORDER; if (fcu) sort_time_fcurve(fcu); } if (ale->update & ANIM_UPDATE_HANDLES) { ale->update &= ~ANIM_UPDATE_HANDLES; if (fcu) calchandles_fcurve(fcu); } if (ale->update & ANIM_UPDATE_DEPS) { ale->update &= ~ANIM_UPDATE_DEPS; ANIM_list_elem_update(ac->scene, ale); } BLI_assert(ale->update == 0); } }
/* helper for paste_animedit_keys() - performs the actual pasting */ static void paste_animedit_keys_fcurve(FCurve *fcu, tAnimCopybufItem *aci, float offset, const eKeyMergeMode merge_mode) { BezTriple *bezt; int i; /* First de-select existing FCurve's keyframes */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { bezt->f2 &= ~SELECT; } /* mix mode with existing data */ switch (merge_mode) { case KEYFRAME_PASTE_MERGE_MIX: /* do-nothing */ break; case KEYFRAME_PASTE_MERGE_OVER: /* remove all keys */ clear_fcurve_keys(fcu); break; case KEYFRAME_PASTE_MERGE_OVER_RANGE: case KEYFRAME_PASTE_MERGE_OVER_RANGE_ALL: { float f_min; float f_max; if (merge_mode == KEYFRAME_PASTE_MERGE_OVER_RANGE) { f_min = aci->bezt[0].vec[1][0] + offset; f_max = aci->bezt[aci->totvert - 1].vec[1][0] + offset; } else { /* Entire Range */ f_min = animcopy_firstframe + offset; f_max = animcopy_lastframe + offset; } /* remove keys in range */ if (f_min < f_max) { /* select verts in range for removal */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { if ((f_min < bezt[0].vec[1][0]) && (bezt[0].vec[1][0] < f_max)) { bezt->f2 |= SELECT; } } /* remove frames in the range */ delete_fcurve_keys(fcu); } break; } } /* just start pasting, with the the first keyframe on the current frame, and so on */ for (i = 0, bezt = aci->bezt; i < aci->totvert; i++, bezt++) { /* temporarily apply offset to src beztriple while copying */ bezt->vec[0][0] += offset; bezt->vec[1][0] += offset; bezt->vec[2][0] += offset; /* insert the keyframe * NOTE: no special flags here for now */ insert_bezt_fcurve(fcu, bezt, 0); /* un-apply offset from src beztriple after copying */ bezt->vec[0][0] -= offset; bezt->vec[1][0] -= offset; bezt->vec[2][0] -= offset; } /* recalculate F-Curve's handles? */ calchandles_fcurve(fcu); }
// TODO: introduce scaling factor for weighting falloff void smooth_fcurve(FCurve *fcu) { BezTriple *bezt; int i, x, totSel = 0; if (fcu->bezt == NULL) { return; } /* first loop through - count how many verts are selected */ bezt = fcu->bezt; for (i = 0; i < fcu->totvert; i++, bezt++) { if (BEZSELECTED(bezt)) totSel++; } /* if any points were selected, allocate tSmooth_Bezt points to work on */ if (totSel >= 3) { tSmooth_Bezt *tarray, *tsb; /* allocate memory in one go */ tsb = tarray = MEM_callocN(totSel * sizeof(tSmooth_Bezt), "tSmooth_Bezt Array"); /* populate tarray with data of selected points */ bezt = fcu->bezt; for (i = 0, x = 0; (i < fcu->totvert) && (x < totSel); i++, bezt++) { if (BEZSELECTED(bezt)) { /* tsb simply needs pointer to vec, and index */ tsb->h1 = &bezt->vec[0][1]; tsb->h2 = &bezt->vec[1][1]; tsb->h3 = &bezt->vec[2][1]; /* advance to the next tsb to populate */ if (x < totSel - 1) tsb++; else break; } } /* calculate the new smoothed F-Curve's with weighted averages: * - this is done with two passes to avoid progressive corruption errors * - uses 5 points for each operation (which stores in the relevant handles) * - previous: w/a ratio = 3:5:2:1:1 * - next: w/a ratio = 1:1:2:5:3 */ /* round 1: calculate smoothing deltas and new values */ tsb = tarray; for (i = 0; i < totSel; i++, tsb++) { /* don't touch end points (otherwise, curves slowly explode, as we don't have enough data there) */ if (ELEM(i, 0, (totSel - 1)) == 0) { const tSmooth_Bezt *tP1 = tsb - 1; const tSmooth_Bezt *tP2 = (i - 2 > 0) ? (tsb - 2) : (NULL); const tSmooth_Bezt *tN1 = tsb + 1; const tSmooth_Bezt *tN2 = (i + 2 < totSel) ? (tsb + 2) : (NULL); const float p1 = *tP1->h2; const float p2 = (tP2) ? (*tP2->h2) : (*tP1->h2); const float c1 = *tsb->h2; const float n1 = *tN1->h2; const float n2 = (tN2) ? (*tN2->h2) : (*tN1->h2); /* calculate previous and next, then new position by averaging these */ tsb->y1 = (3 * p2 + 5 * p1 + 2 * c1 + n1 + n2) / 12; tsb->y3 = (p2 + p1 + 2 * c1 + 5 * n1 + 3 * n2) / 12; tsb->y2 = (tsb->y1 + tsb->y3) / 2; } } /* round 2: apply new values */ tsb = tarray; for (i = 0; i < totSel; i++, tsb++) { /* don't touch end points, as their values were't touched above */ if (ELEM(i, 0, (totSel - 1)) == 0) { /* y2 takes the average of the 2 points */ *tsb->h2 = tsb->y2; /* handles are weighted between their original values and the averaged values */ *tsb->h1 = ((*tsb->h1) * 0.7f) + (tsb->y1 * 0.3f); *tsb->h3 = ((*tsb->h3) * 0.7f) + (tsb->y3 * 0.3f); } } /* free memory required for tarray */ MEM_freeN(tarray); } /* recalculate handles */ calchandles_fcurve(fcu); }
static void gp_stroke_path_animation(bContext *C, ReportList *reports, Curve *cu, tGpTimingData *gtd) { Scene *scene = CTX_data_scene(C); bAction *act; FCurve *fcu; PointerRNA ptr; PropertyRNA *prop = NULL; int nbr_gaps = 0, i; if (gtd->mode == GP_STROKECONVERT_TIMING_NONE) return; /* gap_duration and gap_randomness are in frames, but we need seconds!!! */ gtd->gap_duration = FRA2TIME(gtd->gap_duration); gtd->gap_randomness = FRA2TIME(gtd->gap_randomness); /* Enable path! */ cu->flag |= CU_PATH; cu->pathlen = gtd->frame_range; /* Get RNA pointer to read/write path time values */ RNA_id_pointer_create((ID *)cu, &ptr); prop = RNA_struct_find_property(&ptr, "eval_time"); /* Ensure we have an F-Curve to add keyframes to */ act = verify_adt_action((ID *)cu, true); fcu = verify_fcurve(act, NULL, &ptr, "eval_time", 0, true); if (G.debug & G_DEBUG) { printf("%s: tot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); for (i = 0; i < gtd->num_points; i++) { printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); } } if (gtd->mode == GP_STROKECONVERT_TIMING_LINEAR) { float cfra; /* Linear extrapolation! */ fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; cu->ctime = 0.0f; cfra = (float)gtd->start_frame; insert_keyframe_direct(reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST); cu->ctime = cu->pathlen; if (gtd->realtime) { cfra += (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */ } else { cfra = (float)gtd->end_frame; } insert_keyframe_direct(reports, ptr, prop, fcu, cfra, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST); } else { /* Use actual recorded timing! */ RNG *rng = BLI_rng_new(0); float time_range; /* CustomGaps specific */ float tot_gaps_time = 0.0f; /* Pre-process gaps, in case we don't want to keep their original timing */ if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { gp_stroke_path_animation_preprocess_gaps(gtd, rng, &nbr_gaps, &tot_gaps_time); } if (gtd->realtime) { time_range = (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */ } else { time_range = (float)(gtd->end_frame - gtd->start_frame); } if (G.debug & G_DEBUG) { printf("GP Stroke Path Conversion: Starting keying!\n"); } gp_stroke_path_animation_add_keyframes(reports, ptr, prop, fcu, cu, gtd, rng, time_range, nbr_gaps, tot_gaps_time); BLI_rng_free(rng); } /* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */ calchandles_fcurve(fcu); if (G.debug & G_DEBUG) { printf("%s: \ntot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); for (i = 0; i < gtd->num_points; i++) { printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); } printf("\n\n"); } WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); /* send updates */ DAG_id_tag_update(&cu->id, 0); }
/* Get (or add relevant data to be able to do so) F-Curve from the driver stack, * for the given Animation Data block. This assumes that all the destinations are valid. * * - add: 0 - don't add anything if not found, * 1 - add new Driver FCurve (with keyframes for visual tweaking), * 2 - add new Driver FCurve (with generator, for script backwards compatibility) * -1 - add new Driver FCurve without driver stuff (for pasting) */ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_index, short add) { AnimData *adt; FCurve *fcu; /* sanity checks */ if (ELEM(NULL, id, rna_path)) return NULL; /* init animdata if none available yet */ adt = BKE_animdata_from_id(id); if ((adt == NULL) && (add)) adt = BKE_id_add_animdata(id); if (adt == NULL) { /* if still none (as not allowed to add, or ID doesn't have animdata for some reason) */ return NULL; } /* try to find f-curve matching for this setting * - add if not found and allowed to add one * TODO: add auto-grouping support? how this works will need to be resolved */ fcu = list_find_fcurve(&adt->drivers, rna_path, array_index); if ((fcu == NULL) && (add)) { /* use default settings to make a F-Curve */ fcu = MEM_callocN(sizeof(FCurve), "FCurve"); fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); /* store path - make copy, and store that */ fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path)); fcu->array_index = array_index; /* if add is negative, don't init this data yet, since it will be filled in by the pasted driver */ if (add > 0) { BezTriple *bezt; size_t i; /* add some new driver data */ fcu->driver = MEM_callocN(sizeof(ChannelDriver), "ChannelDriver"); fcu->driver->flag |= DRIVER_FLAG_SHOWDEBUG; /* F-Modifier or Keyframes? */ // FIXME: replace these magic numbers with defines if (add == 2) { /* Python API Backwards compatibility hack: * Create FModifier so that old scripts won't break * for now before 2.7 series -- (September 4, 2013) */ add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR); } else { /* add 2 keyframes so that user has something to work with * - These are configured to 0,0 and 1,1 to give a 1-1 mapping * which can be easily tweaked from there. */ insert_vert_fcurve(fcu, 0.0f, 0.0f, INSERTKEY_FAST); insert_vert_fcurve(fcu, 1.0f, 1.0f, INSERTKEY_FAST); /* configure this curve to extrapolate */ for (i = 0, bezt = fcu->bezt; (i < fcu->totvert) && bezt; i++, bezt++) { bezt->h1 = bezt->h2 = HD_VECT; } fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; calchandles_fcurve(fcu); } } /* just add F-Curve to end of driver list */ BLI_addtail(&adt->drivers, fcu); } /* return the F-Curve */ return fcu; }
/* Use a weighted moving-means method to reduce intensity of fluctuations */ void smooth_fcurve (FCurve *fcu) { BezTriple *bezt; int i, x, totSel = 0; /* first loop through - count how many verts are selected, and fix up handles * this is done for both modes */ bezt= fcu->bezt; for (i=0; i < fcu->totvert; i++, bezt++) { if (BEZSELECTED(bezt)) { /* line point's handles up with point's vertical position */ bezt->vec[0][1]= bezt->vec[2][1]= bezt->vec[1][1]; if ((bezt->h1==HD_AUTO) || (bezt->h1==HD_VECT)) bezt->h1= HD_ALIGN; if ((bezt->h2==HD_AUTO) || (bezt->h2==HD_VECT)) bezt->h2= HD_ALIGN; /* add value to total */ totSel++; } } /* if any points were selected, allocate tSmooth_Bezt points to work on */ if (totSel >= 3) { tSmooth_Bezt *tarray, *tsb; /* allocate memory in one go */ tsb= tarray= MEM_callocN(totSel*sizeof(tSmooth_Bezt), "tSmooth_Bezt Array"); /* populate tarray with data of selected points */ bezt= fcu->bezt; for (i=0, x=0; (i < fcu->totvert) && (x < totSel); i++, bezt++) { if (BEZSELECTED(bezt)) { /* tsb simply needs pointer to vec, and index */ tsb->h1 = &bezt->vec[0][1]; tsb->h2 = &bezt->vec[1][1]; tsb->h3 = &bezt->vec[2][1]; /* advance to the next tsb to populate */ if (x < totSel- 1) tsb++; else break; } } /* calculate the new smoothed F-Curve's with weighted averages: * - this is done with two passes * - uses 5 points for each operation (which stores in the relevant handles) * - previous: w/a ratio = 3:5:2:1:1 * - next: w/a ratio = 1:1:2:5:3 */ /* round 1: calculate previous and next */ tsb= tarray; for (i=0; i < totSel; i++, tsb++) { /* don't touch end points (otherwise, curves slowly explode) */ if (ELEM(i, 0, (totSel-1)) == 0) { const tSmooth_Bezt *tP1 = tsb - 1; const tSmooth_Bezt *tP2 = (i-2 > 0) ? (tsb - 2) : (NULL); const tSmooth_Bezt *tN1 = tsb + 1; const tSmooth_Bezt *tN2 = (i+2 < totSel) ? (tsb + 2) : (NULL); const float p1 = *tP1->h2; const float p2 = (tP2) ? (*tP2->h2) : (*tP1->h2); const float c1 = *tsb->h2; const float n1 = *tN1->h2; const float n2 = (tN2) ? (*tN2->h2) : (*tN1->h2); /* calculate previous and next */ *tsb->h1= (3*p2 + 5*p1 + 2*c1 + n1 + n2) / 12; *tsb->h3= (p2 + p1 + 2*c1 + 5*n1 + 3*n2) / 12; } } /* round 2: calculate new values and reset handles */ tsb= tarray; for (i=0; i < totSel; i++, tsb++) { /* calculate new position by averaging handles */ *tsb->h2 = (*tsb->h1 + *tsb->h3) / 2; /* reset handles now */ *tsb->h1 = *tsb->h2; *tsb->h3 = *tsb->h2; } /* free memory required for tarray */ MEM_freeN(tarray); } /* recalculate handles */ calchandles_fcurve(fcu); }
/* This function pastes data from the keyframes copy/paste buffer */ short paste_animedit_keys (bAnimContext *ac, ListBase *anim_data) { bAnimListElem *ale; const Scene *scene= (ac->scene); const float offset = (float)(CFRA - animcopy_firstframe); short no_name= 0; /* check if buffer is empty */ if (ELEM(NULL, animcopybuf.first, animcopybuf.last)) { //error("No data in buffer to paste"); return -1; } /* check if single channel in buffer (disregard names if so) */ if (animcopybuf.first == animcopybuf.last) no_name= 1; /* from selected channels */ for (ale= anim_data->first; ale; ale= ale->next) { FCurve *fcu = (FCurve *)ale->data; /* destination F-Curve */ tAnimCopybufItem *aci= NULL; BezTriple *bezt; int i; /* find buffer item to paste from * - if names don't matter (i.e. only 1 channel in buffer), don't check id/group * - if names do matter, only check if id-type is ok for now (group check is not that important) * - most importantly, rna-paths should match (array indices are unimportant for now) */ // TODO: the matching algorithm here is pathetic! for (aci= animcopybuf.first; aci; aci= aci->next) { /* check that paths exist */ if (aci->rna_path && fcu->rna_path) { // FIXME: this breaks for bone names! if (strcmp(aci->rna_path, fcu->rna_path) == 0) { /* should be a match unless there's more than one of these */ if ((no_name) || (aci->array_index == fcu->array_index)) break; } } } /* copy the relevant data from the matching buffer curve */ if (aci) { /* just start pasting, with the the first keyframe on the current frame, and so on */ for (i=0, bezt=aci->bezt; i < aci->totvert; i++, bezt++) { /* temporarily apply offset to src beztriple while copying */ bezt->vec[0][0] += offset; bezt->vec[1][0] += offset; bezt->vec[2][0] += offset; /* insert the keyframe * NOTE: no special flags here for now */ insert_bezt_fcurve(fcu, bezt, 0); /* un-apply offset from src beztriple after copying */ bezt->vec[0][0] -= offset; bezt->vec[1][0] -= offset; bezt->vec[2][0] -= offset; } /* recalculate F-Curve's handles? */ calchandles_fcurve(fcu); } } return 0; }