/* helper func - draw one repeat of an F-Curve */ static void draw_fcurve_curve_bezts(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d) { BezTriple *prevbezt = fcu->bezt; BezTriple *bezt = prevbezt + 1; float v1[2], v2[2], v3[2], v4[2]; float *fp, data[120]; float fac = 0.0f; int b = fcu->totvert - 1; int resol; float unit_scale; short mapping_flag = ANIM_get_normalization_flags(ac); /* apply unit mapping */ glPushMatrix(); unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag); glScalef(1.0f, unit_scale, 1.0f); glBegin(GL_LINE_STRIP); /* extrapolate to left? */ if (prevbezt->vec[1][0] > v2d->cur.xmin) { /* left-side of view comes before first keyframe, so need to extend as not cyclic */ v1[0] = v2d->cur.xmin; /* y-value depends on the interpolation */ if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (prevbezt->ipo == BEZT_IPO_CONST) || (fcu->totvert == 1)) { /* just extend across the first keyframe's value */ v1[1] = prevbezt->vec[1][1]; } else if (prevbezt->ipo == BEZT_IPO_LIN) { /* extrapolate linear dosnt use the handle, use the next points center instead */ fac = (prevbezt->vec[1][0] - bezt->vec[1][0]) / (prevbezt->vec[1][0] - v1[0]); if (fac) fac = 1.0f / fac; v1[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[1][1] - bezt->vec[1][1]); } else { /* based on angle of handle 1 (relative to keyframe) */ fac = (prevbezt->vec[0][0] - prevbezt->vec[1][0]) / (prevbezt->vec[1][0] - v1[0]); if (fac) fac = 1.0f / fac; v1[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[0][1] - prevbezt->vec[1][1]); } glVertex2fv(v1); } /* if only one keyframe, add it now */ if (fcu->totvert == 1) { v1[0] = prevbezt->vec[1][0]; v1[1] = prevbezt->vec[1][1]; glVertex2fv(v1); } /* draw curve between first and last keyframe (if there are enough to do so) */ /* TODO: optimize this to not have to calc stuff out of view too? */ while (b--) { if (prevbezt->ipo == BEZT_IPO_CONST) { /* Constant-Interpolation: draw segment between previous keyframe and next, but holding same value */ v1[0] = prevbezt->vec[1][0]; v1[1] = prevbezt->vec[1][1]; glVertex2fv(v1); v1[0] = bezt->vec[1][0]; v1[1] = prevbezt->vec[1][1]; glVertex2fv(v1); } else if (prevbezt->ipo == BEZT_IPO_LIN) { /* Linear interpolation: just add one point (which should add a new line segment) */ v1[0] = prevbezt->vec[1][0]; v1[1] = prevbezt->vec[1][1]; glVertex2fv(v1); } else { /* Bezier-Interpolation: draw curve as series of segments between keyframes * - resol determines number of points to sample in between keyframes */ /* resol depends on distance between points (not just horizontal) OR is a fixed high res */ /* TODO: view scale should factor into this someday too... */ if (fcu->driver) resol = 32; else resol = (int)(5.0f * len_v2v2(bezt->vec[1], prevbezt->vec[1])); if (resol < 2) { /* only draw one */ v1[0] = prevbezt->vec[1][0]; v1[1] = prevbezt->vec[1][1]; glVertex2fv(v1); } else { /* clamp resolution to max of 32 */ /* NOTE: higher values will crash */ if (resol > 32) resol = 32; 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[0] = bezt->vec[0][0]; v3[1] = bezt->vec[0][1]; v4[0] = bezt->vec[1][0]; v4[1] = bezt->vec[1][1]; correct_bezpart(v1, v2, v3, v4); BKE_curve_forward_diff_bezier(v1[0], v2[0], v3[0], v4[0], data, resol, sizeof(float) * 3); BKE_curve_forward_diff_bezier(v1[1], v2[1], v3[1], v4[1], data + 1, resol, sizeof(float) * 3); for (fp = data; resol; resol--, fp += 3) glVertex2fv(fp); } } /* get next pointers */ prevbezt = bezt; bezt++; /* last point? */ if (b == 0) { v1[0] = prevbezt->vec[1][0]; v1[1] = prevbezt->vec[1][1]; glVertex2fv(v1); } } /* extrapolate to right? (see code for left-extrapolation above too) */ if (prevbezt->vec[1][0] < v2d->cur.xmax) { v1[0] = v2d->cur.xmax; /* y-value depends on the interpolation */ if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (fcu->flag & FCURVE_INT_VALUES) || (prevbezt->ipo == BEZT_IPO_CONST) || (fcu->totvert == 1)) { /* based on last keyframe's value */ v1[1] = prevbezt->vec[1][1]; } else if (prevbezt->ipo == BEZT_IPO_LIN) { /* extrapolate linear dosnt use the handle, use the previous points center instead */ bezt = prevbezt - 1; fac = (prevbezt->vec[1][0] - bezt->vec[1][0]) / (prevbezt->vec[1][0] - v1[0]); if (fac) fac = 1.0f / fac; v1[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[1][1] - bezt->vec[1][1]); } else { /* based on angle of handle 1 (relative to keyframe) */ fac = (prevbezt->vec[2][0] - prevbezt->vec[1][0]) / (prevbezt->vec[1][0] - v1[0]); if (fac) fac = 1.0f / fac; v1[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[2][1] - prevbezt->vec[1][1]); } glVertex2fv(v1); } glEnd(); glPopMatrix(); }
/* only creates a table for a single channel in CurveMapping */ static void curvemap_make_table(CurveMap *cuma, rctf *clipr) { CurveMapPoint *cmp = cuma->curve; BezTriple *bezt; float *fp, *allpoints, *lastpoint, curf, range; int a, totpoint; if (cuma->curve == NULL) return; /* default rect also is table range */ cuma->mintable = clipr->xmin; cuma->maxtable = clipr->xmax; /* hrmf... we now rely on blender ipo beziers, these are more advanced */ bezt = MEM_callocN(cuma->totpoint * sizeof(BezTriple), "beztarr"); for (a = 0; a < cuma->totpoint; a++) { cuma->mintable = MIN2(cuma->mintable, cmp[a].x); cuma->maxtable = MAX2(cuma->maxtable, cmp[a].x); bezt[a].vec[1][0] = cmp[a].x; bezt[a].vec[1][1] = cmp[a].y; if (cmp[a].flag & CUMA_VECTOR) bezt[a].h1 = bezt[a].h2 = HD_VECT; else bezt[a].h1 = bezt[a].h2 = HD_AUTO; } for (a = 0; a < cuma->totpoint; a++) { if (a == 0) calchandle_curvemap(bezt, NULL, bezt + 1, 0); else if (a == cuma->totpoint - 1) calchandle_curvemap(bezt + a, bezt + a - 1, NULL, 0); else calchandle_curvemap(bezt + a, bezt + a - 1, bezt + a + 1, 0); } /* first and last handle need correction, instead of pointing to center of next/prev, * we let it point to the closest handle */ if (cuma->totpoint > 2) { float hlen, nlen, vec[3]; if (bezt[0].h2 == HD_AUTO) { hlen = len_v3v3(bezt[0].vec[1], bezt[0].vec[2]); /* original handle length */ /* clip handle point */ copy_v3_v3(vec, bezt[1].vec[0]); if (vec[0] < bezt[0].vec[1][0]) vec[0] = bezt[0].vec[1][0]; sub_v3_v3(vec, bezt[0].vec[1]); nlen = len_v3(vec); if (nlen > FLT_EPSILON) { mul_v3_fl(vec, hlen / nlen); add_v3_v3v3(bezt[0].vec[2], vec, bezt[0].vec[1]); sub_v3_v3v3(bezt[0].vec[0], bezt[0].vec[1], vec); } } a = cuma->totpoint - 1; if (bezt[a].h2 == HD_AUTO) { hlen = len_v3v3(bezt[a].vec[1], bezt[a].vec[0]); /* original handle length */ /* clip handle point */ copy_v3_v3(vec, bezt[a - 1].vec[2]); if (vec[0] > bezt[a].vec[1][0]) vec[0] = bezt[a].vec[1][0]; sub_v3_v3(vec, bezt[a].vec[1]); nlen = len_v3(vec); if (nlen > FLT_EPSILON) { mul_v3_fl(vec, hlen / nlen); add_v3_v3v3(bezt[a].vec[0], vec, bezt[a].vec[1]); sub_v3_v3v3(bezt[a].vec[2], bezt[a].vec[1], vec); } } } /* make the bezier curve */ if (cuma->table) MEM_freeN(cuma->table); totpoint = (cuma->totpoint - 1) * CM_RESOL; fp = allpoints = MEM_callocN(totpoint * 2 * sizeof(float), "table"); for (a = 0; a < cuma->totpoint - 1; a++, fp += 2 * CM_RESOL) { correct_bezpart(bezt[a].vec[1], bezt[a].vec[2], bezt[a + 1].vec[0], bezt[a + 1].vec[1]); BKE_curve_forward_diff_bezier(bezt[a].vec[1][0], bezt[a].vec[2][0], bezt[a + 1].vec[0][0], bezt[a + 1].vec[1][0], fp, CM_RESOL - 1, 2 * sizeof(float)); BKE_curve_forward_diff_bezier(bezt[a].vec[1][1], bezt[a].vec[2][1], bezt[a + 1].vec[0][1], bezt[a + 1].vec[1][1], fp + 1, CM_RESOL - 1, 2 * sizeof(float)); } /* store first and last handle for extrapolation, unit length */ cuma->ext_in[0] = bezt[0].vec[0][0] - bezt[0].vec[1][0]; cuma->ext_in[1] = bezt[0].vec[0][1] - bezt[0].vec[1][1]; range = sqrt(cuma->ext_in[0] * cuma->ext_in[0] + cuma->ext_in[1] * cuma->ext_in[1]); cuma->ext_in[0] /= range; cuma->ext_in[1] /= range; a = cuma->totpoint - 1; cuma->ext_out[0] = bezt[a].vec[1][0] - bezt[a].vec[2][0]; cuma->ext_out[1] = bezt[a].vec[1][1] - bezt[a].vec[2][1]; range = sqrt(cuma->ext_out[0] * cuma->ext_out[0] + cuma->ext_out[1] * cuma->ext_out[1]); cuma->ext_out[0] /= range; cuma->ext_out[1] /= range; /* cleanup */ MEM_freeN(bezt); range = CM_TABLEDIV * (cuma->maxtable - cuma->mintable); cuma->range = 1.0f / range; /* now make a table with CM_TABLE equal x distances */ fp = allpoints; lastpoint = allpoints + 2 * (totpoint - 1); cmp = MEM_callocN((CM_TABLE + 1) * sizeof(CurveMapPoint), "dist table"); for (a = 0; a <= CM_TABLE; a++) { curf = cuma->mintable + range * (float)a; cmp[a].x = curf; /* get the first x coordinate larger than curf */ while (curf >= fp[0] && fp != lastpoint) { fp += 2; } if (fp == allpoints || (curf >= fp[0] && fp == lastpoint)) cmp[a].y = curvemap_calc_extend(cuma, curf, allpoints, lastpoint); else { float fac1 = fp[0] - fp[-2]; float fac2 = fp[0] - curf; if (fac1 > FLT_EPSILON) fac1 = fac2 / fac1; else fac1 = 0.0f; cmp[a].y = fac1 * fp[-1] + (1.0f - fac1) * fp[1]; } } MEM_freeN(allpoints); cuma->table = cmp; }
static float normalization_factor_get(Scene *scene, FCurve *fcu, short flag, float *r_offset) { float factor = 1.0f, offset = 0.0f; if (flag & ANIM_UNITCONV_RESTORE) { if (r_offset) *r_offset = fcu->prev_offset; return 1.0f / fcu->prev_norm_factor; } if (flag & ANIM_UNITCONV_NORMALIZE_FREEZE) { if (r_offset) *r_offset = fcu->prev_offset; if (fcu->prev_norm_factor == 0.0f) { /* Happens when Auto Normalize was disabled before * any curves were displayed. */ return 1.0f; } return fcu->prev_norm_factor; } if (G.moving & G_TRANSFORM_FCURVES) { if (r_offset) *r_offset = fcu->prev_offset; if (fcu->prev_norm_factor == 0.0f) { /* Same as above. */ return 1.0f; } return fcu->prev_norm_factor; } fcu->prev_norm_factor = 1.0f; if (fcu->bezt) { const bool use_preview_only = PRVRANGEON; const BezTriple *bezt; int i; float max_coord = -FLT_MAX; float min_coord = FLT_MAX; float range; if (fcu->totvert < 1) { return 1.0f; } for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { if (use_preview_only && !IN_RANGE_INCL(bezt->vec[1][0], scene->r.psfra, scene->r.pefra)) { continue; } if (i == 0) { /* We ignore extrapolation flags and handle here, and use the * control point position only. so we normalize "interesting" * part of the curve. * * Here we handle left extrapolation. */ max_coord = max_ff(max_coord, bezt->vec[1][1]); min_coord = min_ff(min_coord, bezt->vec[1][1]); } else { const BezTriple *prev_bezt = bezt - 1; if (prev_bezt->ipo == BEZT_IPO_CONST) { /* Constant interpolation: previous CV value is used up * to the current keyframe. */ max_coord = max_ff(max_coord, bezt->vec[1][1]); min_coord = min_ff(min_coord, bezt->vec[1][1]); } else if (prev_bezt->ipo == BEZT_IPO_LIN) { /* Linear interpolation: min/max using both previous and * and current CV. */ max_coord = max_ff(max_coord, bezt->vec[1][1]); min_coord = min_ff(min_coord, bezt->vec[1][1]); max_coord = max_ff(max_coord, prev_bezt->vec[1][1]); min_coord = min_ff(min_coord, prev_bezt->vec[1][1]); } else if (prev_bezt->ipo == BEZT_IPO_BEZ) { const int resol = fcu->driver ? 32 : min_ii((int)(5.0f * len_v2v2(bezt->vec[1], prev_bezt->vec[1])), 32); if (resol < 2) { max_coord = max_ff(max_coord, prev_bezt->vec[1][1]); min_coord = min_ff(min_coord, prev_bezt->vec[1][1]); } else { float data[120]; float v1[2], v2[2], v3[2], v4[2]; v1[0] = prev_bezt->vec[1][0]; v1[1] = prev_bezt->vec[1][1]; v2[0] = prev_bezt->vec[2][0]; v2[1] = prev_bezt->vec[2][1]; v3[0] = bezt->vec[0][0]; v3[1] = bezt->vec[0][1]; v4[0] = bezt->vec[1][0]; v4[1] = bezt->vec[1][1]; correct_bezpart(v1, v2, v3, v4); BKE_curve_forward_diff_bezier(v1[0], v2[0], v3[0], v4[0], data, resol, sizeof(float) * 3); BKE_curve_forward_diff_bezier(v1[1], v2[1], v3[1], v4[1], data + 1, resol, sizeof(float) * 3); for (int j = 0; j <= resol; ++j) { const float *fp = &data[j * 3]; max_coord = max_ff(max_coord, fp[1]); min_coord = min_ff(min_coord, fp[1]); } } } } } if (max_coord > min_coord) { range = max_coord - min_coord; if (range > FLT_EPSILON) { factor = 2.0f / range; } offset = -min_coord - range / 2.0f; } else if (max_coord == min_coord) { factor = 1.0f; offset = -min_coord; } } BLI_assert(factor != 0.0f); if (r_offset) { *r_offset = offset; } fcu->prev_norm_factor = factor; fcu->prev_offset = offset; return factor; }