static short selmap_build_bezier_more(KeyframeEditData *ked, BezTriple *bezt) { FCurve *fcu = ked->fcu; char *map = ked->data; int i = ked->curIndex; /* if current is selected, just make sure it stays this way */ if (BEZSELECTED(bezt)) { map[i] = 1; return 0; } /* if previous is selected, that means that selection should extend across */ if (i > 0) { BezTriple *prev = bezt - 1; if (BEZSELECTED(prev)) { map[i] = 1; return 0; } } /* if next is selected, that means that selection should extend across */ if (i < (fcu->totvert - 1)) { BezTriple *next = bezt + 1; if (BEZSELECTED(next)) { map[i] = 1; return 0; } } return 0; }
/* Create a ActKeyColumn for a pair of BezTriples */ static ActKeyBlock *bezts_to_new_actkeyblock(BezTriple *prev, BezTriple *beztn) { ActKeyBlock *ab = MEM_callocN(sizeof(ActKeyBlock), "ActKeyBlock"); ab->start = prev->vec[1][0]; ab->end = beztn->vec[1][0]; ab->val = beztn->vec[1][1]; ab->sel = (BEZSELECTED(prev) || BEZSELECTED(beztn)) ? SELECT : 0; ab->modified = 1; return ab; }
// TODO: should we return if we hit something? static void nearest_fcurve_vert_store (ListBase *matches, View2D *v2d, FCurve *fcu, BezTriple *bezt, FPoint *fpt, short hpoint, int mval[2]) { /* Keyframes or Samples? */ if (bezt) { int screen_co[2], dist; /* convert from data-space to screen coordinates * NOTE: hpoint+1 gives us 0,1,2 respectively for each handle, * needed to access the relevant vertex coordinates in the 3x3 * 'vec' matrix */ UI_view2d_view_to_region(v2d, bezt->vec[hpoint+1][0], bezt->vec[hpoint+1][1], &screen_co[0], &screen_co[1]); /* check if distance from mouse cursor to vert in screen space is within tolerance */ // XXX: inlined distance calculation, since we cannot do this on ints using the math lib... //dist = len_v2v2(mval, screen_co); dist = sqrt((mval[0] - screen_co[0])*(mval[0] - screen_co[0]) + (mval[1] - screen_co[1])*(mval[1] - screen_co[1])); if (dist <= GVERTSEL_TOL) { tNearestVertInfo *nvi = (tNearestVertInfo *)matches->last; short replace = 0; /* if there is already a point for the F-Curve, check if this point is closer than that was */ if ((nvi) && (nvi->fcu == fcu)) { /* replace if we are closer, or if equal and that one wasn't selected but we are... */ if ( (nvi->dist > dist) || ((nvi->sel == 0) && BEZSELECTED(bezt)) ) replace= 1; } /* add new if not replacing... */ if (replace == 0) nvi = MEM_callocN(sizeof(tNearestVertInfo), "Nearest Graph Vert Info - Bezt"); /* store values */ nvi->fcu = fcu; nvi->bezt = bezt; nvi->hpoint = hpoint; nvi->dist = dist; nvi->sel= BEZSELECTED(bezt); // XXX... should this use the individual verts instead? /* add to list of matches if appropriate... */ if (replace == 0) BLI_addtail(matches, nvi); } } else if (fpt) { // TODO... } }
/* get 'active' keyframe for panel editing */ static short get_active_fcurve_keyframe_edit(FCurve *fcu, BezTriple **bezt, BezTriple **prevbezt) { BezTriple *b; int i; /* zero the pointers */ *bezt = *prevbezt = NULL; /* sanity checks */ if ((fcu->bezt == NULL) || (fcu->totvert == 0)) return 0; /* find first selected keyframe for now, and call it the active one * - this is a reasonable assumption, given that whenever anyone * wants to edit numerically, there is likely to only be 1 vert selected */ for (i = 0, b = fcu->bezt; i < fcu->totvert; i++, b++) { if (BEZSELECTED(b)) { /* found * - 'previous' is either the one before, of the keyframe itself (which is still fine) * XXX: we can just make this null instead if needed */ *prevbezt = (i > 0) ? b - 1 : b; *bezt = b; return 1; } } /* not found */ return 0; }
/* 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); }
/* Add the given BezTriple to the given 'list' of Keyframes */ static void add_bezt_to_keycolumns_list(DLRBT_Tree *keys, BezTriple *bezt) { ActKeyColumn *new_ak=NULL; if ELEM(NULL, keys, bezt) return; /* if there are no keys already, just add as root */ if (keys->root == NULL) { /* just add this as the root, then call the tree-balancing functions to validate */ new_ak= bezt_to_new_actkeycolumn(bezt); keys->root= (DLRBT_Node *)new_ak; } else { ActKeyColumn *ak, *akp=NULL, *akn=NULL; /* traverse tree to find an existing entry to update the status of, * or a suitable point to add at */ for (ak= keys->root; ak; akp= ak, ak= akn) { /* check if this is a match, or whether we go left or right */ if (ak->cfra == bezt->vec[1][0]) { /* set selection status and 'touched' status */ if (BEZSELECTED(bezt)) ak->sel = SELECT; ak->modified += 1; /* for keyframe type, 'proper' keyframes have priority over breakdowns (and other types for now) */ if (BEZKEYTYPE(bezt) == BEZT_KEYTYPE_KEYFRAME) ak->key_type= BEZT_KEYTYPE_KEYFRAME; /* done... no need to insert */ return; } else { ActKeyColumn **aknp= NULL; /* check if go left or right, but if not available, add new node */ if (ak->cfra < bezt->vec[1][0]) aknp= &ak->right; else aknp= &ak->left; /* if this does not exist, add a new node, otherwise continue... */ if (*aknp == NULL) { /* add a new node representing this, and attach it to the relevant place */ new_ak= bezt_to_new_actkeycolumn(bezt); new_ak->parent= ak; *aknp= new_ak; break; } else akn= *aknp; } } } /* now, balance the tree taking into account this newly added node */ BLI_dlrbTree_insert(keys, (DLRBT_Node *)new_ak); }
static short ok_bezier_selected(KeyframeEditData *UNUSED(ked), BezTriple *bezt) { /* this macro checks all beztriple handles for selection... * only one of the verts has to be selected for this to be ok... */ if (BEZSELECTED(bezt)) return KEYFRAME_OK_ALL; else return 0; }
static short selmap_build_bezier_less(KeyframeEditData *ked, BezTriple *bezt) { FCurve *fcu = ked->fcu; char *map = ked->data; int i = ked->curIndex; /* if current is selected, check the left/right keyframes * since it might need to be deselected (but otherwise no) */ if (BEZSELECTED(bezt)) { /* if previous is not selected, we're on the tip of an iceberg */ if (i > 0) { BezTriple *prev = bezt - 1; if (BEZSELECTED(prev) == 0) return 0; } else if (i == 0) { /* current keyframe is selected at an endpoint, so should get deselected */ return 0; } /* if next is not selected, we're on the tip of an iceberg */ if (i < (fcu->totvert - 1)) { BezTriple *next = bezt + 1; if (BEZSELECTED(next) == 0) return 0; } else if (i == (fcu->totvert - 1)) { /* current keyframe is selected at an endpoint, so should get deselected */ return 0; } /* if we're still here, that means that keyframe should remain untouched */ map[i] = 1; } return 0; }
/* Node updater callback used for building ActKeyColumns from BezTriples */ static void nupdate_ak_bezt(void *node, void *data) { ActKeyColumn *ak = (ActKeyColumn *)node; BezTriple *bezt = (BezTriple *)data; /* set selection status and 'touched' status */ if (BEZSELECTED(bezt)) ak->sel = SELECT; ak->modified += 1; /* for keyframe type, 'proper' keyframes have priority over breakdowns (and other types for now) */ if (BEZKEYTYPE(bezt) == BEZT_KEYTYPE_KEYFRAME) ak->key_type = BEZT_KEYTYPE_KEYFRAME; }
/* Create a ActKeyColumn from a BezTriple */ static ActKeyColumn *bezt_to_new_actkeycolumn(BezTriple *bezt) { ActKeyColumn *ak= MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); /* store settings based on state of BezTriple */ ak->cfra= bezt->vec[1][0]; ak->sel= BEZSELECTED(bezt) ? SELECT : 0; ak->key_type= BEZKEYTYPE(bezt); /* set 'modified', since this is used to identify long keyframes */ ak->modified = 1; return ak; }
/* New node callback used for building ActKeyColumns from BezTriples */ static DLRBT_Node *nalloc_ak_bezt(void *data) { ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); BezTriple *bezt = (BezTriple *)data; /* store settings based on state of BezTriple */ ak->cfra = bezt->vec[1][0]; ak->sel = BEZSELECTED(bezt) ? SELECT : 0; ak->key_type = BEZKEYTYPE(bezt); /* set 'modified', since this is used to identify long keyframes */ ak->modified = 1; return (DLRBT_Node *)ak; }
/* helper func - draw handle vertices only for an F-Curve (if it is not protected) */ static void draw_fcurve_vertices_handles(FCurve *fcu, SpaceIpo *sipo, View2D *v2d, short sel, short sel_handle_only, float units_scale) { BezTriple *bezt = fcu->bezt; BezTriple *prevbezt = NULL; float hsize, xscale, yscale; int i; /* get view settings */ hsize = UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE) * U.pixelsize; UI_view2d_getscale(v2d, &xscale, &yscale); /* Compensate OGL scale sued for unit mapping, so circle will be circle, not ellipse */ yscale *= units_scale; /* set handle color */ if (sel) UI_ThemeColor(TH_HANDLE_VERTEX_SELECT); else UI_ThemeColor(TH_HANDLE_VERTEX); /* anti-aliased lines for more consistent appearance */ if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); for (i = 0; i < fcu->totvert; i++, prevbezt = bezt, bezt++) { /* Draw the editmode handles for a bezier curve (others don't have handles) * if their selection status matches the selection status we're drawing for * - first handle only if previous beztriple was bezier-mode * - second handle only if current beztriple is bezier-mode * * Also, need to take into account whether the keyframe was selected * if a Graph Editor option to only show handles of selected keys is on. */ if (!sel_handle_only || BEZSELECTED(bezt)) { if ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) { if ((bezt->f1 & SELECT) == sel) /* && v2d->cur.xmin < bezt->vec[0][0] < v2d->cur.xmax)*/ draw_fcurve_handle_control(bezt->vec[0][0], bezt->vec[0][1], xscale, yscale, hsize); } if (bezt->ipo == BEZT_IPO_BEZ) { if ((bezt->f3 & SELECT) == sel) /* && v2d->cur.xmin < bezt->vec[2][0] < v2d->cur.xmax)*/ draw_fcurve_handle_control(bezt->vec[2][0], bezt->vec[2][1], xscale, yscale, hsize); } } } if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); }
/* This function adds data to the keyframes copy/paste buffer, freeing existing data first */ short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data) { bAnimListElem *ale; Scene *scene = ac->scene; /* clear buffer first */ free_anim_copybuf(); /* assume that each of these is an F-Curve */ for (ale = anim_data->first; ale; ale = ale->next) { FCurve *fcu = (FCurve *)ale->key_data; tAnimCopybufItem *aci; BezTriple *bezt, *nbezt, *newbuf; int i; /* firstly, check if F-Curve has any selected keyframes * - skip if no selected keyframes found (so no need to create unnecessary copy-buffer data) * - this check should also eliminate any problems associated with using sample-data */ if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, ANIM_editkeyframes_ok(BEZT_OK_SELECTED), NULL) == 0) continue; /* init copybuf item info */ aci = MEM_callocN(sizeof(tAnimCopybufItem), "AnimCopybufItem"); aci->id = ale->id; aci->id_type = GS(ale->id->name); aci->grp = fcu->grp; aci->rna_path = MEM_dupallocN(fcu->rna_path); aci->array_index = fcu->array_index; BLI_addtail(&animcopybuf, aci); /* add selected keyframes to buffer */ /* TODO: currently, we resize array every time we add a new vert - * this works ok as long as it is assumed only a few keys are copied */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { if (BEZSELECTED(bezt)) { /* add to buffer */ newbuf = MEM_callocN(sizeof(BezTriple) * (aci->totvert + 1), "copybuf beztriple"); /* assume that since we are just re-sizing the array, just copy all existing data across */ if (aci->bezt) memcpy(newbuf, aci->bezt, sizeof(BezTriple) * (aci->totvert)); /* copy current beztriple across too */ nbezt = &newbuf[aci->totvert]; *nbezt = *bezt; /* ensure copy buffer is selected so pasted keys are selected */ BEZ_SEL(nbezt); /* free old array and set the new */ if (aci->bezt) MEM_freeN(aci->bezt); aci->bezt = newbuf; aci->totvert++; /* check if this is the earliest frame encountered so far */ if (bezt->vec[1][0] < animcopy_firstframe) animcopy_firstframe = bezt->vec[1][0]; if (bezt->vec[1][0] > animcopy_lastframe) animcopy_lastframe = bezt->vec[1][0]; } } } /* check if anything ended up in the buffer */ if (ELEM(NULL, animcopybuf.first, animcopybuf.last)) return -1; /* in case 'relative' paste method is used */ animcopy_cfra = CFRA; /* everything went fine */ return 0; }
// 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); }
/* draw lines for F-Curve handles only (this is only done in EditMode) * note: draw_fcurve_handles_check must be checked before running this. */ static void draw_fcurve_handles(SpaceIpo *sipo, FCurve *fcu) { int sel, b; /* a single call to GL_LINES here around these calls should be sufficient to still * get separate line segments, but which aren't wrapped with GL_LINE_STRIP every time we * want a single line */ glBegin(GL_LINES); /* slightly hacky, but we want to draw unselected points before selected ones * so that selected points are clearly visible */ for (sel = 0; sel < 2; sel++) { BezTriple *bezt = fcu->bezt, *prevbezt = NULL; int basecol = (sel) ? TH_HANDLE_SEL_FREE : TH_HANDLE_FREE; float *fp; unsigned char col[4]; for (b = 0; b < fcu->totvert; b++, prevbezt = bezt, bezt++) { /* if only selected keyframes can get their handles shown, * check that keyframe is selected */ if (sipo->flag & SIPO_SELVHANDLESONLY) { if (BEZSELECTED(bezt) == 0) continue; } /* draw handle with appropriate set of colors if selection is ok */ if ((bezt->f2 & SELECT) == sel) { fp = bezt->vec[0]; /* only draw first handle if previous segment had handles */ if ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) { UI_GetThemeColor3ubv(basecol + bezt->h1, col); col[3] = fcurve_display_alpha(fcu) * 255; glColor4ubv((GLubyte *)col); glVertex2fv(fp); glVertex2fv(fp + 3); } /* only draw second handle if this segment is bezier */ if (bezt->ipo == BEZT_IPO_BEZ) { UI_GetThemeColor3ubv(basecol + bezt->h2, col); col[3] = fcurve_display_alpha(fcu) * 255; glColor4ubv((GLubyte *)col); glVertex2fv(fp + 3); glVertex2fv(fp + 6); } } else { /* only draw first handle if previous segment was had handles, and selection is ok */ if (((bezt->f1 & SELECT) == sel) && ((!prevbezt && (bezt->ipo == BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ)))) { fp = bezt->vec[0]; UI_GetThemeColor3ubv(basecol + bezt->h1, col); col[3] = fcurve_display_alpha(fcu) * 255; glColor4ubv((GLubyte *)col); glVertex2fv(fp); glVertex2fv(fp + 3); } /* only draw second handle if this segment is bezier, and selection is ok */ if (((bezt->f3 & SELECT) == sel) && (bezt->ipo == BEZT_IPO_BEZ)) { fp = bezt->vec[1]; UI_GetThemeColor3ubv(basecol + bezt->h2, col); col[3] = fcurve_display_alpha(fcu) * 255; glColor4ubv((GLubyte *)col); glVertex2fv(fp); glVertex2fv(fp + 3); } } } } glEnd(); /* GL_LINES */ }
// XXX also need to check for int-values only? static bool fcurve_handle_sel_check(SpaceIpo *sipo, BezTriple *bezt) { if (sipo->flag & SIPO_NOHANDLES) return 0; if ((sipo->flag & SIPO_SELVHANDLESONLY) && BEZSELECTED(bezt) == 0) return 0; return 1; }
static void add_bezt_to_keyblocks_list(DLRBT_Tree *blocks, BezTriple *first_bezt, BezTriple *beztn) { ActKeyBlock *new_ab = NULL; BezTriple *prev = NULL; /* get the BezTriple immediately before the given one which has the same value */ if (beztn != first_bezt) { /* XXX: Unless I'm overlooking some details from the past, this should be sufficient? * The old code did some elaborate stuff trying to find keyframe columns for * the given BezTriple, then step backwards to the column before that, and find * an appropriate BezTriple with matching values there. Maybe that was warranted * in the past, but now, that list is only ever filled with keyframes from the * current FCurve. * * -- Aligorith (20140415) */ prev = beztn - 1; } /* check if block needed - same value(s)? * -> firstly, handles must have same central value as each other * -> secondly, handles which control that section of the curve must be constant */ if (prev == NULL) return; if (IS_EQF(beztn->vec[1][1], prev->vec[1][1]) == 0) return; if (IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) == 0) return; if (IS_EQF(prev->vec[1][1], prev->vec[2][1]) == 0) return; /* if there are no blocks already, just add as root */ if (blocks->root == NULL) { /* just add this as the root, then call the tree-balancing functions to validate */ new_ab = bezts_to_new_actkeyblock(prev, beztn); blocks->root = (DLRBT_Node *)new_ab; } else { ActKeyBlock *ab, *abn = NULL; /* try to find a keyblock that starts on the previous beztriple, and add a new one if none start there * Note: we perform a tree traversal here NOT a standard linked-list traversal... * Note: we can't search from end to try to optimize this as it causes errors there's * an A ___ B |---| B situation */ // FIXME: here there is a bug where we are trying to get the summary for the following channels // A|--------------|A ______________ B|--------------|B // A|------------------------------------------------|A // A|----|A|---|A|-----------------------------------|A for (ab = blocks->root; ab; ab = abn) { /* check if this is a match, or whether we go left or right * NOTE: we now use a float threshold to prevent precision errors causing problems with summaries */ if (IS_EQT(ab->start, prev->vec[1][0], BEZT_BINARYSEARCH_THRESH)) { /* set selection status and 'touched' status */ if (BEZSELECTED(beztn)) ab->sel = SELECT; ab->modified++; /* done... no need to insert */ return; } else { ActKeyBlock **abnp = NULL; /* branch to go down - used to hook new blocks to parents */ /* check if go left or right, but if not available, add new node */ if (ab->start < prev->vec[1][0]) abnp = &ab->right; else abnp = &ab->left; /* if this does not exist, add a new node, otherwise continue... */ if (*abnp == NULL) { /* add a new node representing this, and attach it to the relevant place */ new_ab = bezts_to_new_actkeyblock(prev, beztn); new_ab->parent = ab; *abnp = new_ab; break; } else abn = *abnp; } } } /* now, balance the tree taking into account this newly added node */ BLI_dlrbTree_insert(blocks, (DLRBT_Node *)new_ab); }
/* 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); }
static void add_bezt_to_keyblocks_list(DLRBT_Tree *blocks, FCurve *fcu, int index) { ActKeyBlock *new_ab= NULL; BezTriple *beztn=NULL, *prev=NULL; BezTriple *bezt; int v; /* get beztriples */ beztn= (fcu->bezt + index); /* we need to go through all beztriples, as they may not be in order (i.e. during transform) */ // TODO: this seems to be a bit of a bottleneck for (v=0, bezt=fcu->bezt; v < fcu->totvert; v++, bezt++) { /* skip if beztriple is current */ if (v != index) { /* check if beztriple is immediately before */ if (beztn->vec[1][0] > bezt->vec[1][0]) { /* check if closer than previous was */ if (prev) { if (prev->vec[1][0] < bezt->vec[1][0]) prev= bezt; } else { prev= bezt; } } } } /* check if block needed - same value(s)? * -> firstly, handles must have same central value as each other * -> secondly, handles which control that section of the curve must be constant */ if ((!prev) || (!beztn)) return; if (IS_EQ(beztn->vec[1][1], prev->vec[1][1])==0) return; if (IS_EQ(beztn->vec[1][1], beztn->vec[0][1])==0) return; if (IS_EQ(prev->vec[1][1], prev->vec[2][1])==0) return; /* if there are no blocks already, just add as root */ if (blocks->root == NULL) { /* just add this as the root, then call the tree-balancing functions to validate */ new_ab= bezts_to_new_actkeyblock(prev, beztn); blocks->root= (DLRBT_Node *)new_ab; } else { ActKeyBlock *ab, *abp=NULL, *abn=NULL; /* try to find a keyblock that starts on the previous beztriple, and add a new one if none start there * Note: we can't search from end to try to optimise this as it causes errors there's * an A ___ B |---| B situation */ // FIXME: here there is a bug where we are trying to get the summary for the following channels // A|--------------|A ______________ B|--------------|B // A|------------------------------------------------|A // A|----|A|---|A|-----------------------------------|A for (ab= blocks->root; ab; abp= ab, ab= abn) { /* check if this is a match, or whether we go left or right */ if (ab->start == prev->vec[1][0]) { /* set selection status and 'touched' status */ if (BEZSELECTED(beztn)) ab->sel = SELECT; ab->modified += 1; /* done... no need to insert */ return; } else { ActKeyBlock **abnp= NULL; /* check if go left or right, but if not available, add new node */ if (ab->start < prev->vec[1][0]) abnp= &ab->right; else abnp= &ab->left; /* if this does not exist, add a new node, otherwise continue... */ if (*abnp == NULL) { /* add a new node representing this, and attach it to the relevant place */ new_ab= bezts_to_new_actkeyblock(prev, beztn); new_ab->parent= ab; *abnp= new_ab; break; } else abn= *abnp; } } } /* now, balance the tree taking into account this newly added node */ BLI_dlrbTree_insert(blocks, (DLRBT_Node *)new_ab); }
/* option 1) select keyframe directly under mouse */ static void mouse_graph_keys(bAnimContext *ac, const int mval[2], short select_mode, short curves_only) { SpaceIpo *sipo = (SpaceIpo *)ac->sl; tNearestVertInfo *nvi; BezTriple *bezt = NULL; /* find the beztriple that we're selecting, and the handle that was clicked on */ nvi = find_nearest_fcurve_vert(ac, mval); /* check if anything to select */ if (nvi == NULL) return; /* deselect all other curves? */ if (select_mode == SELECT_REPLACE) { /* reset selection mode */ select_mode = SELECT_ADD; /* deselect all other keyframes (+ F-Curves too) */ deselect_graph_keys(ac, 0, SELECT_SUBTRACT, true); /* deselect other channels too, but only only do this if * selection of channel when the visibility of keyframes * doesn't depend on this */ if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); } /* if points can be selected on this F-Curve */ // TODO: what about those with no keyframes? if ((curves_only == 0) && ((nvi->fcu->flag & FCURVE_PROTECTED) == 0)) { /* only if there's keyframe */ if (nvi->bezt) { bezt = nvi->bezt; /* used to check bezt seletion is set */ /* depends on selection mode */ if (select_mode == SELECT_INVERT) { /* keyframe - invert select of all */ if (nvi->hpoint == NEAREST_HANDLE_KEY) { if (BEZSELECTED(bezt)) { BEZ_DESEL(bezt); } else { BEZ_SEL(bezt); } } /* handles - toggle selection of relevant handle */ else if (nvi->hpoint == NEAREST_HANDLE_LEFT) { /* toggle selection */ bezt->f1 ^= SELECT; } else { /* toggle selection */ bezt->f3 ^= SELECT; } } else { /* if the keyframe was clicked on, select all verts of given beztriple */ if (nvi->hpoint == NEAREST_HANDLE_KEY) { BEZ_SEL(bezt); } /* otherwise, select the handle that applied */ else if (nvi->hpoint == NEAREST_HANDLE_LEFT) bezt->f1 |= SELECT; else bezt->f3 |= SELECT; } } else if (nvi->fpt) { // TODO: need to handle sample points } } else { KeyframeEditFunc select_cb; KeyframeEditData ked; /* initialize keyframe editing data */ memset(&ked, 0, sizeof(KeyframeEditData)); /* set up BezTriple edit callbacks */ select_cb = ANIM_editkeyframes_select(select_mode); /* select all keyframes */ ANIM_fcurve_keyframes_loop(&ked, nvi->fcu, NULL, select_cb, NULL); } /* only change selection of channel when the visibility of keyframes doesn't depend on this */ if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) { /* select or deselect curve? */ if (bezt) { /* take selection status from item that got hit, to prevent flip/flop on channel * selection status when shift-selecting (i.e. "SELECT_INVERT") points */ if (BEZSELECTED(bezt)) nvi->fcu->flag |= FCURVE_SELECTED; else nvi->fcu->flag &= ~FCURVE_SELECTED; } else { /* didn't hit any channel, so just apply that selection mode to the curve's selection status */ if (select_mode == SELECT_INVERT) nvi->fcu->flag ^= FCURVE_SELECTED; else if (select_mode == SELECT_ADD) nvi->fcu->flag |= FCURVE_SELECTED; } } /* set active F-Curve (NOTE: sync the filter flags with findnearest_fcurve_vert) */ /* needs to be called with (sipo->flag & SIPO_SELCUVERTSONLY) otherwise the active flag won't be set [#26452] */ if (nvi->fcu->flag & FCURVE_SELECTED) { int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nvi->fcu, ANIMTYPE_FCURVE); } /* free temp sample data for filtering */ MEM_freeN(nvi); }