Blender V4.3
keyframes_general.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cfloat>
10#include <cmath>
11#include <cstdlib>
12#include <cstring>
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_blenlib.h"
17#include "BLI_math_vector.h"
19#include "BLI_string_utils.hh"
20#include "BLI_utildefines.h"
21
22#include "DNA_anim_types.h"
23#include "DNA_object_types.h"
24#include "DNA_scene_types.h"
25#include "DNA_space_types.h"
26
27#include "BKE_action.hh"
28#include "BKE_curve.hh"
29#include "BKE_fcurve.hh"
30#include "BKE_main.hh"
31#include "BKE_scene.hh"
32
33#include "RNA_access.hh"
34#include "RNA_enum_types.hh"
35#include "RNA_path.hh"
36
37#include "ED_keyframes_edit.hh"
38
39#include "ANIM_animdata.hh"
40#include "ANIM_fcurve.hh"
41
42/* This file contains code for various keyframe-editing tools which are 'destructive'
43 * (i.e. they will modify the order of the keyframes, and change the size of the array).
44 * While some of these tools may eventually be moved out into blenkernel, for now, it is
45 * fine to have these calls here.
46 *
47 * There are also a few tools here which cannot be easily coded for in the other system (yet).
48 * These may also be moved around at some point, but for now, they are best added here.
49 *
50 * - Joshua Leung, Dec 2008
51 */
52
53/* **************************************************** */
54
56{
57 bool changed = false;
58
59 /* this can only work when there is an F-Curve, and also when there are some BezTriples */
60 if (ELEM(nullptr, fcu, fcu->bezt)) {
61 return changed;
62 }
63
64 for (int i = 0; i < fcu->totvert; i++) {
65 /* If a key is selected */
66 if (fcu->bezt[i].f2 & SELECT) {
67 /* Expand the list */
68 BezTriple *newbezt = static_cast<BezTriple *>(
69 MEM_callocN(sizeof(BezTriple) * (fcu->totvert + 1), "beztriple"));
70
71 memcpy(newbezt, fcu->bezt, sizeof(BezTriple) * (i + 1));
72 memcpy(newbezt + i + 1, fcu->bezt + i, sizeof(BezTriple));
73 memcpy(newbezt + i + 2, fcu->bezt + i + 1, sizeof(BezTriple) * (fcu->totvert - (i + 1)));
74 fcu->totvert++;
75 changed = true;
76 /* reassign pointers... (free old, and add new) */
77 MEM_freeN(fcu->bezt);
78 fcu->bezt = newbezt;
79
80 /* Unselect the current key */
81 BEZT_DESEL_ALL(&fcu->bezt[i]);
82 i++;
83
84 /* Select the copied key */
85 BEZT_SEL_ALL(&fcu->bezt[i]);
86 }
87 }
88 return changed;
89}
90
91/* -------------------------------------------------------------------- */
96 bAnimListElem *ale,
97 float thresh,
98 bool cleardefault,
99 const bool only_selected_keys)
100{
101 FCurve *fcu = (FCurve *)ale->key_data;
102 BezTriple *old_bezts, *bezt, *beztn;
103 BezTriple *lastb;
104 int totCount, i;
105
106 /* Check if any points. */
107 if ((fcu == nullptr) || (fcu->bezt == nullptr) || (fcu->totvert == 0) ||
108 (!cleardefault && fcu->totvert == 1))
109 {
110 return;
111 }
112
113 /* make a copy of the old BezTriples, and clear F-Curve */
114 old_bezts = fcu->bezt;
115 totCount = fcu->totvert;
116 fcu->bezt = nullptr;
117 fcu->totvert = 0;
118
119 /* now insert first keyframe, as it should be ok */
120 bezt = old_bezts;
122 if (!(bezt->f2 & SELECT)) {
123 lastb = fcu->bezt;
124 lastb->f1 = lastb->f2 = lastb->f3 = 0;
125 }
126
127 /* Loop through BezTriples, comparing them. Skip any that do
128 * not fit the criteria for "ok" points.
129 */
130 for (i = 1; i < totCount; i++) {
131 float prev[2], cur[2], next[2];
132
133 /* get BezTriples and their values */
134 if (i < (totCount - 1)) {
135 beztn = (old_bezts + (i + 1));
136 next[0] = beztn->vec[1][0];
137 next[1] = beztn->vec[1][1];
138 }
139 else {
140 beztn = nullptr;
141 next[0] = next[1] = 0.0f;
142 }
143 lastb = (fcu->bezt + (fcu->totvert - 1));
144 bezt = (old_bezts + i);
145
146 /* get references for quicker access */
147 prev[0] = lastb->vec[1][0];
148 prev[1] = lastb->vec[1][1];
149 cur[0] = bezt->vec[1][0];
150 cur[1] = bezt->vec[1][1];
151
152 if (only_selected_keys && !(bezt->f2 & SELECT)) {
154 lastb = (fcu->bezt + (fcu->totvert - 1));
155 lastb->f1 = lastb->f2 = lastb->f3 = 0;
156 continue;
157 }
158
159 /* check if current bezt occurs at same time as last ok */
160 if (IS_EQT(cur[0], prev[0], thresh)) {
161 /* If there is a next beztriple, and if occurs at the same time, only insert
162 * if there is a considerable distance between the points, and also if the
163 * current is further away than the next one is to the previous.
164 */
165 if (beztn && IS_EQT(cur[0], next[0], thresh) && (IS_EQT(next[1], prev[1], thresh) == 0)) {
166 /* only add if current is further away from previous */
167 if (cur[1] > next[1]) {
168 if (IS_EQT(cur[1], prev[1], thresh) == 0) {
169 /* add new keyframe */
171 }
172 }
173 }
174 else {
175 /* only add if values are a considerable distance apart */
176 if (IS_EQT(cur[1], prev[1], thresh) == 0) {
177 /* add new keyframe */
179 }
180 }
181 }
182 else {
183 /* checks required are dependent on whether this is last keyframe or not */
184 if (beztn) {
185 /* does current have same value as previous and next? */
186 if (IS_EQT(cur[1], prev[1], thresh) == 0) {
187 /* add new keyframe */
189 }
190 else if (IS_EQT(cur[1], next[1], thresh) == 0) {
191 /* add new keyframe */
193 }
194 }
195 else {
196 /* add if value doesn't equal that of previous */
197 if (IS_EQT(cur[1], prev[1], thresh) == 0) {
198 /* add new keyframe */
200 }
201 }
202 }
203 }
204
205 /* now free the memory used by the old BezTriples */
206 if (old_bezts) {
207 MEM_freeN(old_bezts);
208 }
209
210 /* final step, if there is just one key in fcurve, check if it's
211 * the default value and if is, remove fcurve completely. */
212 if (cleardefault && fcu->totvert == 1) {
213 float default_value = 0.0f;
215 PropertyRNA *prop;
216 PointerRNA id_ptr = RNA_id_pointer_create(ale->id);
217
218 /* get property to read from, and get value as appropriate */
219 if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
220 if (RNA_property_type(prop) == PROP_FLOAT) {
221 default_value = RNA_property_float_get_default_index(&ptr, prop, fcu->array_index);
222 }
223 }
224
225 if (fcu->bezt->vec[1][1] == default_value) {
227
228 /* check if curve is really unused and if it is, return signal for deletion */
229 if (BKE_fcurve_is_empty(fcu)) {
230 AnimData *adt = ale->adt;
232 ale->key_data = nullptr;
233 }
234 }
235 }
236}
237
246 const int start_index,
247 int *r_segment_start_idx,
248 int *r_segment_len)
249{
250 *r_segment_start_idx = 0;
251 *r_segment_len = 0;
252
253 bool in_segment = false;
254
255 for (int i = start_index; i < fcu->totvert; i++) {
256 const bool point_is_selected = fcu->bezt[i].f2 & SELECT;
257 const bool point_is_ignored = fcu->bezt[i].f2 & BEZT_FLAG_IGNORE_TAG;
258
259 if (point_is_selected && !point_is_ignored) {
260 if (!in_segment) {
261 *r_segment_start_idx = i;
262 in_segment = true;
263 }
264 (*r_segment_len)++;
265 }
266 else if (in_segment) {
267 /* If the curve point is not selected then we have reached the end of the selected curve
268 * segment. */
269 return true; /* Segment found. */
270 }
271 }
272
273 /* If the last curve point was in the segment, `r_segment_len` and `r_segment_start_idx`
274 * are already updated and true is returned. */
275 return in_segment;
276}
277
279{
280 ListBase segments = {nullptr, nullptr};
281
282 /* Ignore baked curves. */
283 if (!fcu->bezt) {
284 return segments;
285 }
286
287 int segment_start_idx = 0;
288 int segment_len = 0;
289 int current_index = 0;
290
291 while (find_fcurve_segment(fcu, current_index, &segment_start_idx, &segment_len)) {
292 FCurveSegment *segment;
293 segment = static_cast<FCurveSegment *>(MEM_callocN(sizeof(*segment), "FCurveSegment"));
294 segment->start_index = segment_start_idx;
295 segment->length = segment_len;
296 BLI_addtail(&segments, segment);
297 current_index = segment_start_idx + segment_len;
298 }
299 return segments;
300}
301
302static const BezTriple *fcurve_segment_start_get(FCurve *fcu, int index)
303{
304 const BezTriple *start_bezt = index - 1 >= 0 ? &fcu->bezt[index - 1] : &fcu->bezt[index];
305 return start_bezt;
306}
307
308static const BezTriple *fcurve_segment_end_get(FCurve *fcu, int index)
309{
310 const BezTriple *end_bezt = index < fcu->totvert ? &fcu->bezt[index] : &fcu->bezt[index - 1];
311 return end_bezt;
312}
313
314/* ---------------- */
315
316void blend_to_neighbor_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
317{
318 const BezTriple *target_bezt;
319 /* Find which key to blend towards. */
320 if (factor < 0) {
321 target_bezt = fcurve_segment_start_get(fcu, segment->start_index);
322 }
323 else {
324 target_bezt = fcurve_segment_end_get(fcu, segment->start_index + segment->length);
325 }
326 const float lerp_factor = fabs(factor);
327 /* Blend each key individually. */
328 for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
329 const float key_y_value = interpf(target_bezt->vec[1][1], fcu->bezt[i].vec[1][1], lerp_factor);
331 }
332}
333
334/* ---------------- */
335
337{
338 const int len = RNA_property_array_length(ptr, prop);
339
340 float default_value = 0;
341 /* Find the default value of that property. */
342 switch (RNA_property_type(prop)) {
343 case PROP_BOOLEAN:
344 if (len) {
345 default_value = float(RNA_property_boolean_get_default_index(ptr, prop, fcu->array_index));
346 }
347 else {
348 default_value = float(RNA_property_boolean_get_default(ptr, prop));
349 }
350 break;
351 case PROP_INT:
352 if (len) {
353 default_value = RNA_property_int_get_default_index(ptr, prop, fcu->array_index);
354 }
355 else {
356 default_value = RNA_property_int_get_default(ptr, prop);
357 }
358 break;
359 case PROP_FLOAT:
360 if (len) {
361 default_value = RNA_property_float_get_default_index(ptr, prop, fcu->array_index);
362 }
363 else {
364 default_value = RNA_property_float_get_default(ptr, prop);
365 }
366 break;
367
368 default:
369 break;
370 }
371 return default_value;
372}
373
374void blend_to_default_fcurve(PointerRNA *id_ptr, FCurve *fcu, const float factor)
375{
377 PropertyRNA *prop;
378
379 /* Check if path is valid. */
380 if (!RNA_path_resolve_property(id_ptr, fcu->rna_path, &ptr, &prop)) {
381 return;
382 }
383
384 const float default_value = get_default_rna_value(fcu, prop, &ptr);
385
386 /* Blend selected keys to default. */
387 for (int i = 0; i < fcu->totvert; i++) {
388 if (!(fcu->bezt[i].f2 & SELECT)) {
389 continue;
390 }
391 const float key_y_value = interpf(default_value, fcu->bezt[i].vec[1][1], factor);
393 }
394}
395
396/* ---------------- */
397
398void scale_average_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
399{
400 float y = 0;
401
402 /* Find first the average of the y values to then use it in the final calculation. */
403 for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
404 y += fcu->bezt[i].vec[1][1];
405 }
406
407 const float y_average = y / segment->length;
408
409 for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
410 const float key_y_value = interpf(y_average, fcu->bezt[i].vec[1][1], 1 - factor);
412 }
413}
414
415/* ---------------- */
416
418 double *A, *d1, *d2;
420};
421
423{
424 ButterworthCoefficients *bw_coeff = static_cast<ButterworthCoefficients *>(
425 MEM_callocN(sizeof(ButterworthCoefficients), "Butterworth Coefficients"));
426 bw_coeff->filter_order = filter_order;
427 bw_coeff->d1 = static_cast<double *>(
428 MEM_callocN(sizeof(double) * filter_order, "coeff filtered"));
429 bw_coeff->d2 = static_cast<double *>(
430 MEM_callocN(sizeof(double) * filter_order, "coeff samples"));
431 bw_coeff->A = static_cast<double *>(MEM_callocN(sizeof(double) * filter_order, "Butterworth A"));
432 return bw_coeff;
433}
434
436{
437 MEM_freeN(bw_coeff->d1);
438 MEM_freeN(bw_coeff->d2);
439 MEM_freeN(bw_coeff->A);
440 MEM_freeN(bw_coeff);
441}
442
443void ED_anim_calculate_butterworth_coefficients(const float cutoff_frequency,
444 const float sampling_frequency,
445 ButterworthCoefficients *bw_coeff)
446{
447 double s = double(sampling_frequency);
448 const double a = tan(M_PI * cutoff_frequency / s);
449 const double a2 = a * a;
450 double r;
451 for (int i = 0; i < bw_coeff->filter_order; ++i) {
452 r = sin(M_PI * (2.0 * i + 1.0) / (4.0 * bw_coeff->filter_order));
453 s = a2 + 2.0 * a * r + 1.0;
454 bw_coeff->A[i] = a2 / s;
455 bw_coeff->d1[i] = 2.0 * (1 - a2) / s;
456 bw_coeff->d2[i] = -(a2 - 2.0 * a * r + 1.0) / s;
457 }
458}
459
461 double x, double *w0, double *w1, double *w2, ButterworthCoefficients *bw_coeff)
462{
463 for (int i = 0; i < bw_coeff->filter_order; i++) {
464 w0[i] = bw_coeff->d1[i] * w1[i] + bw_coeff->d2[i] * w2[i] + x;
465 x = bw_coeff->A[i] * (w0[i] + 2.0 * w1[i] + w2[i]);
466 w2[i] = w1[i];
467 w1[i] = w0[i];
468 }
469 return x;
470}
471
472static float butterworth_calculate_blend_value(float *samples,
473 float *filtered_values,
474 const int start_index,
475 const int end_index,
476 const int sample_index,
477 const int blend_in_out)
478{
479 if (start_index == end_index || blend_in_out == 0) {
480 return samples[start_index];
481 }
482
483 const float blend_in_y_samples = samples[start_index];
484 const float blend_out_y_samples = samples[end_index];
485
486 const float blend_in_y_filtered = filtered_values[start_index + blend_in_out];
487 const float blend_out_y_filtered = filtered_values[end_index - blend_in_out];
488
489 const float slope_in_samples = samples[start_index] - samples[start_index - 1];
490 const float slope_out_samples = samples[end_index] - samples[end_index + 1];
491 const float slope_in_filtered = filtered_values[start_index + blend_in_out - 1] -
492 filtered_values[start_index + blend_in_out];
493 const float slope_out_filtered = filtered_values[end_index - blend_in_out] -
494 filtered_values[end_index - blend_in_out - 1];
495
496 if (sample_index - start_index <= blend_in_out) {
497 const int blend_index = sample_index - start_index;
498 const float blend_in_out_factor = clamp_f(float(blend_index) / blend_in_out, 0.0f, 1.0f);
499 const float blend_value = interpf(blend_in_y_filtered +
500 slope_in_filtered * (blend_in_out - blend_index),
501 blend_in_y_samples + slope_in_samples * blend_index,
502 blend_in_out_factor);
503 return blend_value;
504 }
505 if (end_index - sample_index <= blend_in_out) {
506 const int blend_index = end_index - sample_index;
507 const float blend_in_out_factor = clamp_f(float(blend_index) / blend_in_out, 0.0f, 1.0f);
508 const float blend_value = interpf(blend_out_y_filtered +
509 slope_out_filtered * (blend_in_out - blend_index),
510 blend_out_y_samples + slope_out_samples * blend_index,
511 blend_in_out_factor);
512 return blend_value;
513 }
514 return 0;
515}
516
518 FCurveSegment *segment,
519 float *samples,
520 const int sample_count,
521 const float factor,
522 const int blend_in_out,
523 const int sample_rate,
524 ButterworthCoefficients *bw_coeff)
525{
526 const int filter_order = bw_coeff->filter_order;
527
528 float *filtered_values = static_cast<float *>(
529 MEM_callocN(sizeof(float) * sample_count, "Butterworth Filtered FCurve Values"));
530
531 double *w0 = static_cast<double *>(MEM_callocN(sizeof(double) * filter_order, "w0"));
532 double *w1 = static_cast<double *>(MEM_callocN(sizeof(double) * filter_order, "w1"));
533 double *w2 = static_cast<double *>(MEM_callocN(sizeof(double) * filter_order, "w2"));
534
535 /* The values need to be offset so the first sample starts at 0. This avoids oscillations at the
536 * start and end of the curve. */
537 const float fwd_offset = samples[0];
538
539 for (int i = 0; i < sample_count; i++) {
540 const double x = double(samples[i] - fwd_offset);
541 const double filtered_value = butterworth_filter_value(x, w0, w1, w2, bw_coeff);
542 filtered_values[i] = float(filtered_value) + fwd_offset;
543 }
544
545 for (int i = 0; i < filter_order; i++) {
546 w0[i] = 0.0;
547 w1[i] = 0.0;
548 w2[i] = 0.0;
549 }
550
551 const float bwd_offset = filtered_values[sample_count - 1];
552
553 /* Run the filter backwards as well to remove phase offset. */
554 for (int i = sample_count - 1; i >= 0; i--) {
555 const double x = double(filtered_values[i] - bwd_offset);
556 const double filtered_value = butterworth_filter_value(x, w0, w1, w2, bw_coeff);
557 filtered_values[i] = float(filtered_value) + bwd_offset;
558 }
559
560 const int segment_end_index = segment->start_index + segment->length;
561 BezTriple left_bezt = fcu->bezt[segment->start_index];
562 BezTriple right_bezt = fcu->bezt[segment_end_index - 1];
563
564 const int samples_start_index = filter_order * sample_rate;
565 const int samples_end_index = int(right_bezt.vec[1][0] - left_bezt.vec[1][0] + filter_order) *
566 sample_rate;
567
568 const int blend_in_out_clamped = min_ii(blend_in_out,
569 (samples_end_index - samples_start_index) / 2);
570
571 for (int i = segment->start_index; i < segment_end_index; i++) {
572 float blend_in_out_factor;
573 if (blend_in_out_clamped == 0) {
574 blend_in_out_factor = 1;
575 }
576 else if (i < segment->start_index + segment->length / 2) {
577 blend_in_out_factor = min_ff(float(i - segment->start_index) / blend_in_out_clamped, 1.0f);
578 }
579 else {
580 blend_in_out_factor = min_ff(float(segment_end_index - i - 1) / blend_in_out_clamped, 1.0f);
581 }
582
583 const float x_delta = fcu->bezt[i].vec[1][0] - left_bezt.vec[1][0] + filter_order;
584 /* Using round() instead of casting to int. Casting would introduce a stepping issue when the
585 * x-value is just below a full frame. */
586 const int filter_index = round(x_delta * sample_rate);
587 const float blend_value = butterworth_calculate_blend_value(samples,
588 filtered_values,
589 samples_start_index,
590 samples_end_index,
591 filter_index,
592 blend_in_out_clamped);
593
594 const float blended_value = interpf(
595 filtered_values[filter_index], blend_value, blend_in_out_factor);
596 const float key_y_value = interpf(blended_value, samples[filter_index], factor);
597
599 }
600
601 MEM_freeN(filtered_values);
602 MEM_freeN(w0);
603 MEM_freeN(w1);
604 MEM_freeN(w2);
605}
606
607/* ---------------- */
608
609void ED_ANIM_get_1d_gauss_kernel(const float sigma, const int kernel_size, double *r_kernel)
610{
611 BLI_assert(sigma > 0.0f);
612 BLI_assert(kernel_size > 0);
613 const double sigma_sq = 2.0 * sigma * sigma;
614 double sum = 0.0;
615
616 for (int i = 0; i < kernel_size; i++) {
617 const double normalized_index = double(i) / (kernel_size - 1);
618 r_kernel[i] = exp(-normalized_index * normalized_index / sigma_sq);
619 if (i == 0) {
620 sum += r_kernel[i];
621 }
622 else {
623 /* We only calculate half the kernel,
624 * the normalization needs to take that into account. */
625 sum += r_kernel[i] * 2;
626 }
627 }
628
629 /* Normalize kernel values. */
630 for (int i = 0; i < kernel_size; i++) {
631 r_kernel[i] /= sum;
632 }
633}
634
636 FCurveSegment *segment,
637 float *samples,
638 const float factor,
639 const int kernel_size,
640 double *kernel)
641{
642 const int segment_end_index = segment->start_index + segment->length;
643 const float segment_start_x = fcu->bezt[segment->start_index].vec[1][0];
644 for (int i = segment->start_index; i < segment_end_index; i++) {
645 /* Using round() instead of (int). The latter would create stepping on x-values that are just
646 * below a full frame. */
647 const int sample_index = round(fcu->bezt[i].vec[1][0] - segment_start_x) + kernel_size;
648 /* Apply the kernel. */
649 double filter_result = samples[sample_index] * kernel[0];
650 for (int j = 1; j <= kernel_size; j++) {
651 const double kernel_value = kernel[j];
652 filter_result += samples[sample_index + j] * kernel_value;
653 filter_result += samples[sample_index - j] * kernel_value;
654 }
655 const float key_y_value = interpf(float(filter_result), samples[sample_index], factor);
657 }
658}
659/* ---------------- */
660
661static float ease_sigmoid_function(const float x, const float width, const float shift)
662{
663 const float x_shift = (x - shift) * width;
664 const float y = x_shift / sqrt(1 + pow2f(x_shift));
665 /* Normalize result to 0-1. */
666 return (y + 1) * 0.5f;
667}
668
670 FCurveSegment *segment,
671 const float factor,
672 const float width)
673{
674 const BezTriple *left_key = fcurve_segment_start_get(fcu, segment->start_index);
675 const BezTriple *right_key = fcurve_segment_end_get(fcu, segment->start_index + segment->length);
676
677 const float key_x_range = right_key->vec[1][0] - left_key->vec[1][0];
678 const float key_y_range = right_key->vec[1][1] - left_key->vec[1][1];
679
680 /* Happens if there is only 1 key on the FCurve. Needs to be skipped because it
681 * would be a divide by 0. */
682 if (IS_EQF(key_x_range, 0.0f)) {
683 return;
684 }
685
686 /* Using the factor on the X-shift we are basically moving the curve horizontally. */
687 const float shift = -factor;
688 const float y_min = ease_sigmoid_function(-1, width, shift);
689 const float y_max = ease_sigmoid_function(1, width, shift);
690
691 for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
692 /* Mapping the x-location of the key within the segment to a -1/1 range. */
693 const float x = ((fcu->bezt[i].vec[1][0] - left_key->vec[1][0]) / key_x_range) * 2 - 1;
694 const float y = ease_sigmoid_function(x, width, shift);
695 /* Normalizing the y value to the min and max to ensure that the keys at the end are not
696 * detached from the rest of the animation. */
697 const float blend = (y - y_min) * (1 / (y_max - y_min));
698
699 const float key_y_value = left_key->vec[1][1] + key_y_range * blend;
701 }
702}
703
704/* ---------------- */
705
706void blend_offset_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
707{
708 const BezTriple *left_key = fcurve_segment_start_get(fcu, segment->start_index);
709 const BezTriple *right_key = fcurve_segment_end_get(fcu, segment->start_index + segment->length);
710
711 float y_delta;
712
713 if (factor > 0) {
714 const BezTriple segment_last_key = fcu->bezt[segment->start_index + segment->length - 1];
715 y_delta = right_key->vec[1][1] - segment_last_key.vec[1][1];
716 }
717 else {
718 const BezTriple segment_first_key = fcu->bezt[segment->start_index];
719 y_delta = left_key->vec[1][1] - segment_first_key.vec[1][1];
720 }
721
722 const float offset_value = y_delta * fabs(factor);
723 for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
724 const float key_y_value = fcu->bezt[i].vec[1][1] + offset_value;
726 }
727}
728
729/* ---------------- */
730
731static float s_curve(float x, float slope, float width, float height, float xshift, float yshift)
732{
733 /* Formula for 'S' curve we use for the "ease" sliders.
734 * The shift values move the curve vertically or horizontally.
735 * The range of the curve used is from 0 to 1 on "x" and "y"
736 * so we can scale it (width and height) and move it (`xshift` and y `yshift`)
737 * to crop the part of the curve we need. Slope determines how curvy the shape is. */
738 float y = height * pow((x - xshift), slope) /
739 (pow((x - xshift), slope) + pow((width - (x - xshift)), slope)) +
740 yshift;
741
742 /* The curve doesn't do what we want beyond our margins so we clamp the values. */
743 if (x > xshift + width) {
744 y = height + yshift;
745 }
746 else if (x < xshift) {
747 y = yshift;
748 }
749 return y;
750}
751
752void blend_to_ease_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
753{
754 const BezTriple *left_key = fcurve_segment_start_get(fcu, segment->start_index);
755 const BezTriple *right_key = fcurve_segment_end_get(fcu, segment->start_index + segment->length);
756
757 const float key_x_range = right_key->vec[1][0] - left_key->vec[1][0];
758 const float key_y_range = right_key->vec[1][1] - left_key->vec[1][1];
759
760 /* Happens if there is only 1 key on the FCurve. Needs to be skipped because it
761 * would be a divide by 0. */
762 if (IS_EQF(key_x_range, 0.0f)) {
763 return;
764 }
765
766 const float slope = 3.0;
767 /* By doubling the size of the "S" curve we just one side of it, a "C" shape. */
768 const float width = 2.0;
769 const float height = 2.0;
770 float xy_shift;
771
772 /* Shifting the x and y values we can decide what side of the "S" shape to use. */
773 if (factor > 0) {
774 xy_shift = -1.0;
775 }
776 else {
777 xy_shift = 0.0;
778 }
779
780 for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
781
782 const float x = (fcu->bezt[i].vec[1][0] - left_key->vec[1][0]) / key_x_range;
783 const float ease = s_curve(x, slope, width, height, xy_shift, xy_shift);
784 const float base = left_key->vec[1][1] + key_y_range * ease;
785
786 float y_delta;
787 if (factor > 0) {
788 y_delta = base - fcu->bezt[i].vec[1][1];
789 }
790 else {
791 y_delta = fcu->bezt[i].vec[1][1] - base;
792 }
793
794 const float key_y_value = fcu->bezt[i].vec[1][1] + y_delta * factor;
796 }
797}
798
799/* ---------------- */
800
801bool match_slope_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
802{
803 const BezTriple *left_key = fcurve_segment_start_get(fcu, segment->start_index);
804 const BezTriple *right_key = fcurve_segment_end_get(fcu, segment->start_index + segment->length);
805
806 BezTriple beyond_key;
807 const BezTriple *reference_key;
808
809 if (factor >= 0) {
810 /* Stop the function if there is no key beyond the right neighboring one. */
811 if (segment->start_index + segment->length >= fcu->totvert - 1) {
812 return false;
813 }
814 reference_key = right_key;
815 beyond_key = fcu->bezt[segment->start_index + segment->length + 1];
816 }
817 else {
818 /* Stop the function if there is no key beyond the left neighboring one. */
819 if (segment->start_index <= 1) {
820 return false;
821 }
822 reference_key = left_key;
823 beyond_key = fcu->bezt[segment->start_index - 2];
824 }
825
826 /* This delta values are used to get the relationship between the bookend keys and the
827 * reference keys beyond those. */
828 const float y_delta = beyond_key.vec[1][1] - reference_key->vec[1][1];
829 const float x_delta = beyond_key.vec[1][0] - reference_key->vec[1][0];
830
831 /* Avoids dividing by 0. */
832 if (x_delta == 0) {
833 return false;
834 }
835
836 for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
837
838 /* These new deltas are used to determine the relationship between the current key and the
839 * bookend ones. */
840 const float new_x_delta = fcu->bezt[i].vec[1][0] - reference_key->vec[1][0];
841 const float new_y_delta = new_x_delta * y_delta / x_delta;
842
843 const float delta = reference_key->vec[1][1] + new_y_delta - fcu->bezt[i].vec[1][1];
844
845 const float key_y_value = fcu->bezt[i].vec[1][1] + delta * fabs(factor);
847 }
848 return true;
849}
850
851/* ---------------- */
852
854 FCurveSegment *segment,
855 const float factor,
856 tShearDirection direction)
857{
858 const BezTriple *left_key = fcurve_segment_start_get(fcu, segment->start_index);
859 const BezTriple *right_key = fcurve_segment_end_get(fcu, segment->start_index + segment->length);
860
861 const float key_x_range = right_key->vec[1][0] - left_key->vec[1][0];
862 const float key_y_range = right_key->vec[1][1] - left_key->vec[1][1];
863
864 /* Happens if there is only 1 key on the FCurve. Needs to be skipped because it
865 * would be a divide by 0. */
866 if (IS_EQF(key_x_range, 0.0f)) {
867 return;
868 }
869
870 for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
871 /* For easy calculation of the curve, the values are normalized. */
872 float normalized_x;
873 if (direction == SHEAR_FROM_LEFT) {
874 normalized_x = (fcu->bezt[i].vec[1][0] - left_key->vec[1][0]) / key_x_range;
875 }
876 else {
877 normalized_x = (right_key->vec[1][0] - fcu->bezt[i].vec[1][0]) / key_x_range;
878 }
879
880 const float y_delta = key_y_range * normalized_x;
881
882 const float key_y_value = fcu->bezt[i].vec[1][1] + y_delta * factor;
884 }
885}
886
887/* ---------------- */
888
889void push_pull_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
890{
891 const BezTriple *left_key = fcurve_segment_start_get(fcu, segment->start_index);
892 const BezTriple *right_key = fcurve_segment_end_get(fcu, segment->start_index + segment->length);
893
894 const float key_x_range = right_key->vec[1][0] - left_key->vec[1][0];
895 const float key_y_range = right_key->vec[1][1] - left_key->vec[1][1];
896
897 /* Happens if there is only 1 key on the FCurve. Needs to be skipped because it
898 * would be a divide by 0. */
899 if (IS_EQF(key_x_range, 0.0f)) {
900 return;
901 }
902
903 for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
904 /* For easy calculation of the curve, the values are normalized. */
905 const float normalized_x = (fcu->bezt[i].vec[1][0] - left_key->vec[1][0]) / key_x_range;
906
907 const float linear = left_key->vec[1][1] + key_y_range * normalized_x;
908
909 const float delta = fcu->bezt[i].vec[1][1] - linear;
910
911 const float key_y_value = linear + delta * factor;
913 }
914}
915
916/* ---------------- */
917
918void time_offset_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float frame_offset)
919{
920 /* Two bookend keys of the fcurve are needed to be able to cycle the values. */
921 const BezTriple *last_key = &fcu->bezt[fcu->totvert - 1];
922 const BezTriple *first_key = &fcu->bezt[0];
923
924 const float fcu_x_range = last_key->vec[1][0] - first_key->vec[1][0];
925 const float fcu_y_range = last_key->vec[1][1] - first_key->vec[1][1];
926
927 const float first_key_x = first_key->vec[1][0];
928
929 /* If we operate directly on the fcurve there will be a feedback loop
930 * so we need to capture the "y" values on an array to then apply them on a second loop. */
931 float *y_values = static_cast<float *>(
932 MEM_callocN(sizeof(float) * segment->length, "Time Offset Samples"));
933
934 for (int i = 0; i < segment->length; i++) {
935 /* This simulates the fcu curve moving in time. */
936 const float time = fcu->bezt[segment->start_index + i].vec[1][0] + frame_offset;
937 /* Need to normalize time to first_key to specify that as the wrapping point. */
938 const float wrapped_time = floored_fmod(time - first_key_x, fcu_x_range) + first_key_x;
939 const float delta_y = fcu_y_range * floorf((time - first_key_x) / fcu_x_range);
940
941 const float key_y_value = evaluate_fcurve(fcu, wrapped_time) + delta_y;
942 y_values[i] = key_y_value;
943 }
944
945 for (int i = 0; i < segment->length; i++) {
946 BKE_fcurve_keyframe_move_value_with_handles(&fcu->bezt[segment->start_index + i], y_values[i]);
947 }
948 MEM_freeN(y_values);
949}
950
951/* ---------------- */
952
954 FCurveSegment *segment,
955 const float factor,
956 const FCurveSegmentAnchor anchor)
957{
958 const BezTriple *reference_key;
959 switch (anchor) {
961 reference_key = fcurve_segment_start_get(fcu, segment->start_index);
962 break;
964 reference_key = fcurve_segment_end_get(fcu, segment->start_index + segment->length);
965 break;
966 }
967
968 for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
969 const float key_y_value = interpf(fcu->bezt[i].vec[1][1], reference_key->vec[1][1], factor);
971 }
972}
973
974/* ---------------- */
975
976void breakdown_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
977{
978 const BezTriple *left_bezt = fcurve_segment_start_get(fcu, segment->start_index);
979 const BezTriple *right_bezt = fcurve_segment_end_get(fcu,
980 segment->start_index + segment->length);
981
982 const float lerp_factor = (factor + 1) / 2;
983 for (int i = segment->start_index; i < segment->start_index + segment->length; i++) {
984 const float key_y_value = interpf(right_bezt->vec[1][1], left_bezt->vec[1][1], lerp_factor);
986 }
987}
988
991/* -------------------------------------------------------------------- */
995/* Check if the keyframe interpolation type is supported */
996static bool prepare_for_decimate(FCurve *fcu, int i)
997{
998 switch (fcu->bezt[i].ipo) {
999 case BEZT_IPO_BEZ:
1000 /* We do not need to do anything here as the keyframe already has the required setting.
1001 */
1002 return true;
1003 case BEZT_IPO_LIN:
1004 /* Convert to a linear bezt curve to be able to use the decimation algorithm. */
1005 fcu->bezt[i].ipo = BEZT_IPO_BEZ;
1006 fcu->bezt[i].h1 = HD_FREE;
1007 fcu->bezt[i].h2 = HD_FREE;
1008
1009 if (i != 0) {
1010 float h1[3];
1011 sub_v3_v3v3(h1, fcu->bezt[i - 1].vec[1], fcu->bezt[i].vec[1]);
1012 mul_v3_fl(h1, 1.0f / 3.0f);
1013 add_v3_v3(h1, fcu->bezt[i].vec[1]);
1014 copy_v3_v3(fcu->bezt[i].vec[0], h1);
1015 }
1016
1017 if (i + 1 != fcu->totvert) {
1018 float h2[3];
1019 sub_v3_v3v3(h2, fcu->bezt[i + 1].vec[1], fcu->bezt[i].vec[1]);
1020 mul_v3_fl(h2, 1.0f / 3.0f);
1021 add_v3_v3(h2, fcu->bezt[i].vec[1]);
1022 copy_v3_v3(fcu->bezt[i].vec[2], h2);
1023 }
1024 return true;
1025 default:
1026 /* These are unsupported. */
1027 return false;
1028 }
1029}
1030
1031/* Decimate the given curve segment. */
1033 int bezt_segment_start_idx,
1034 int bezt_segment_len,
1035 float remove_ratio,
1036 float error_sq_max)
1037{
1038 int selected_len = bezt_segment_len;
1039
1040 /* Make sure that we can remove the start/end point of the segment if they
1041 * are not the start/end point of the curve. BKE_curve_decimate_bezt_array
1042 * has a check that prevents removal of the first and last index in the
1043 * passed array. */
1044 if (bezt_segment_len + bezt_segment_start_idx != fcu->totvert &&
1045 prepare_for_decimate(fcu, bezt_segment_len + bezt_segment_start_idx))
1046 {
1047 bezt_segment_len++;
1048 }
1049 if (bezt_segment_start_idx != 0 && prepare_for_decimate(fcu, bezt_segment_start_idx - 1)) {
1050 bezt_segment_start_idx--;
1051 bezt_segment_len++;
1052 }
1053
1054 const int target_fcurve_verts = ceil(bezt_segment_len - selected_len * remove_ratio);
1055
1056 BKE_curve_decimate_bezt_array(&fcu->bezt[bezt_segment_start_idx],
1057 bezt_segment_len,
1058 12, /* The actual resolution displayed in the viewport is dynamic
1059 * so we just pick a value that preserves the curve shape. */
1060 false,
1061 SELECT,
1063 error_sq_max,
1064 target_fcurve_verts);
1065}
1066
1067bool decimate_fcurve(bAnimListElem *ale, float remove_ratio, float error_sq_max)
1068{
1069 FCurve *fcu = (FCurve *)ale->key_data;
1070 /* Check if the curve actually has any points. */
1071 if (fcu == nullptr || fcu->bezt == nullptr || fcu->totvert == 0) {
1072 return true;
1073 }
1074
1075 BezTriple *old_bezts = fcu->bezt;
1076
1077 bool can_decimate_all_selected = true;
1078
1079 for (int i = 0; i < fcu->totvert; i++) {
1080 /* Ignore keyframes that are not supported. */
1081 if (!prepare_for_decimate(fcu, i)) {
1082 can_decimate_all_selected = false;
1083 fcu->bezt[i].f2 |= BEZT_FLAG_IGNORE_TAG;
1084 }
1085 /* Make sure that the temp flag is unset as we use it to determine what to remove. */
1086 fcu->bezt[i].f2 &= ~BEZT_FLAG_TEMP_TAG;
1087 }
1088
1089 ListBase segments = find_fcurve_segments(fcu);
1090 LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
1092 fcu, segment->start_index, segment->length, remove_ratio, error_sq_max);
1093 }
1094 BLI_freelistN(&segments);
1095
1096 uint old_totvert = fcu->totvert;
1097 fcu->bezt = nullptr;
1098 fcu->totvert = 0;
1099
1100 for (int i = 0; i < old_totvert; i++) {
1101 BezTriple *bezt = (old_bezts + i);
1102 bezt->f2 &= ~BEZT_FLAG_IGNORE_TAG;
1103 if ((bezt->f2 & BEZT_FLAG_TEMP_TAG) == 0) {
1105 }
1106 }
1107 /* now free the memory used by the old BezTriples */
1108 if (old_bezts) {
1109 MEM_freeN(old_bezts);
1110 }
1111
1112 return can_decimate_all_selected;
1113}
1114
1117/* -------------------------------------------------------------------- */
1121/* temp struct used for smooth_fcurve */
1123 float *h1, *h2, *h3; /* bezt->vec[0,1,2][1] */
1124 float y1, y2, y3; /* averaged before/new/after y-values */
1125};
1126
1128{
1129 int totSel = 0;
1130
1131 if (fcu->bezt == nullptr) {
1132 return;
1133 }
1134
1135 /* first loop through - count how many verts are selected */
1136 BezTriple *bezt = fcu->bezt;
1137 for (int i = 0; i < fcu->totvert; i++, bezt++) {
1138 if (BEZT_ISSEL_ANY(bezt)) {
1139 totSel++;
1140 }
1141 }
1142
1143 /* if any points were selected, allocate tSmooth_Bezt points to work on */
1144 if (totSel >= 3) {
1145 tSmooth_Bezt *tarray, *tsb;
1146
1147 /* allocate memory in one go */
1148 tsb = tarray = static_cast<tSmooth_Bezt *>(
1149 MEM_callocN(totSel * sizeof(tSmooth_Bezt), "tSmooth_Bezt Array"));
1150
1151 /* populate tarray with data of selected points */
1152 bezt = fcu->bezt;
1153 for (int i = 0, x = 0; (i < fcu->totvert) && (x < totSel); i++, bezt++) {
1154 if (BEZT_ISSEL_ANY(bezt)) {
1155 /* tsb simply needs pointer to vec, and index */
1156 tsb->h1 = &bezt->vec[0][1];
1157 tsb->h2 = &bezt->vec[1][1];
1158 tsb->h3 = &bezt->vec[2][1];
1159
1160 /* advance to the next tsb to populate */
1161 if (x < totSel - 1) {
1162 tsb++;
1163 }
1164 else {
1165 break;
1166 }
1167 }
1168 }
1169
1170 /* calculate the new smoothed F-Curve's with weighted averages:
1171 * - this is done with two passes to avoid progressive corruption errors
1172 * - uses 5 points for each operation (which stores in the relevant handles)
1173 * - previous: w/a ratio = 3:5:2:1:1
1174 * - next: w/a ratio = 1:1:2:5:3
1175 */
1176
1177 /* round 1: calculate smoothing deltas and new values */
1178 tsb = tarray;
1179 for (int i = 0; i < totSel; i++, tsb++) {
1180 /* Don't touch end points (otherwise, curves slowly explode,
1181 * as we don't have enough data there). */
1182 if (ELEM(i, 0, (totSel - 1)) == 0) {
1183 const tSmooth_Bezt *tP1 = tsb - 1;
1184 const tSmooth_Bezt *tP2 = (i - 2 > 0) ? (tsb - 2) : (nullptr);
1185 const tSmooth_Bezt *tN1 = tsb + 1;
1186 const tSmooth_Bezt *tN2 = (i + 2 < totSel) ? (tsb + 2) : (nullptr);
1187
1188 const float p1 = *tP1->h2;
1189 const float p2 = (tP2) ? (*tP2->h2) : (*tP1->h2);
1190 const float c1 = *tsb->h2;
1191 const float n1 = *tN1->h2;
1192 const float n2 = (tN2) ? (*tN2->h2) : (*tN1->h2);
1193
1194 /* calculate previous and next, then new position by averaging these */
1195 tsb->y1 = (3 * p2 + 5 * p1 + 2 * c1 + n1 + n2) / 12;
1196 tsb->y3 = (p2 + p1 + 2 * c1 + 5 * n1 + 3 * n2) / 12;
1197
1198 tsb->y2 = (tsb->y1 + tsb->y3) / 2;
1199 }
1200 }
1201
1202 /* round 2: apply new values */
1203 tsb = tarray;
1204 for (int i = 0; i < totSel; i++, tsb++) {
1205 /* don't touch end points, as their values weren't touched above */
1206 if (ELEM(i, 0, (totSel - 1)) == 0) {
1207 /* y2 takes the average of the 2 points */
1208 *tsb->h2 = tsb->y2;
1209
1210 /* handles are weighted between their original values and the averaged values */
1211 *tsb->h1 = ((*tsb->h1) * 0.7f) + (tsb->y1 * 0.3f);
1212 *tsb->h3 = ((*tsb->h3) * 0.7f) + (tsb->y3 * 0.3f);
1213 }
1214 }
1215
1216 /* free memory required for tarray */
1217 MEM_freeN(tarray);
1218 }
1219
1220 /* recalculate handles */
1222}
1223
1226/* -------------------------------------------------------------------- */
1237/* globals for copy/paste data (like for other copy/paste buffers) */
1238static ListBase animcopybuf = {nullptr, nullptr};
1239static float animcopy_firstframe = 999999999.0f;
1240static float animcopy_lastframe = -999999999.0f;
1241static float animcopy_cfra = 0.0;
1242
1243/* datatype for use in copy/paste buffer */
1246
1247 ID *id; /* ID which owns the curve */
1248 bActionGroup *grp; /* Action Group */
1249 char *rna_path; /* RNA-Path */
1250 int array_index; /* array index */
1251
1252 int totvert; /* number of keyframes stored for this channel */
1253 BezTriple *bezt; /* keyframes in buffer */
1254
1255 short id_type; /* Result of `GS(id->name)`. */
1256 bool is_bone; /* special flag for armature bones */
1257};
1258
1260{
1261 tAnimCopybufItem *aci, *acn;
1262
1263 /* free each buffer element */
1264 for (aci = static_cast<tAnimCopybufItem *>(animcopybuf.first); aci; aci = acn) {
1265 acn = aci->next;
1266
1267 /* free keyframes */
1268 if (aci->bezt) {
1269 MEM_freeN(aci->bezt);
1270 }
1271
1272 /* free RNA-path */
1273 if (aci->rna_path) {
1274 MEM_freeN(aci->rna_path);
1275 }
1276
1277 /* free ourself */
1279 }
1280
1281 /* restore initial state */
1283 animcopy_firstframe = 999999999.0f;
1284 animcopy_lastframe = -999999999.0f;
1285}
1286
1287/* ------------------- */
1288
1290{
1291 Scene *scene = ac->scene;
1292
1293 /* clear buffer first */
1295
1296 /* assume that each of these is an F-Curve */
1297 LISTBASE_FOREACH (bAnimListElem *, ale, anim_data) {
1298 FCurve *fcu = (FCurve *)ale->key_data;
1299 tAnimCopybufItem *aci;
1300 BezTriple *bezt, *nbezt, *newbuf;
1301 int i;
1302
1303 /* firstly, check if F-Curve has any selected keyframes
1304 * - skip if no selected keyframes found (so no need to create unnecessary copy-buffer data)
1305 * - this check should also eliminate any problems associated with using sample-data
1306 */
1308 nullptr, fcu, nullptr, ANIM_editkeyframes_ok(BEZT_OK_SELECTED), nullptr) == 0)
1309 {
1310 continue;
1311 }
1312
1313 /* init copybuf item info */
1314 aci = static_cast<tAnimCopybufItem *>(
1315 MEM_callocN(sizeof(tAnimCopybufItem), "AnimCopybufItem"));
1316 aci->id = ale->id;
1317 aci->id_type = GS(ale->id->name);
1318 aci->grp = fcu->grp;
1319 aci->rna_path = static_cast<char *>(MEM_dupallocN(fcu->rna_path));
1320 aci->array_index = fcu->array_index;
1321
1322 /* Detect if this is a bone. We do that here rather than during pasting because ID pointers
1323 * will get invalidated if we undo.
1324 * Storing the relevant information here helps avoiding crashes if we undo-repaste. */
1325 if ((aci->id_type == ID_OB) && (((Object *)aci->id)->type == OB_ARMATURE) && aci->rna_path) {
1326 Object *ob = (Object *)aci->id;
1327
1328 bPoseChannel *pchan;
1329 char bone_name[sizeof(pchan->name)];
1330 if (BLI_str_quoted_substr(aci->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) {
1331 pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
1332 if (pchan) {
1333 aci->is_bone = true;
1334 }
1335 }
1336 }
1337
1338 BLI_addtail(&animcopybuf, aci);
1339
1340 /* add selected keyframes to buffer */
1341 /* TODO: currently, we resize array every time we add a new vert -
1342 * this works ok as long as it is assumed only a few keys are copied */
1343 for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
1344 if (BEZT_ISSEL_ANY(bezt)) {
1345 /* add to buffer */
1346 newbuf = static_cast<BezTriple *>(
1347 MEM_callocN(sizeof(BezTriple) * (aci->totvert + 1), "copybuf beztriple"));
1348
1349 /* assume that since we are just re-sizing the array, just copy all existing data across */
1350 if (aci->bezt) {
1351 memcpy(newbuf, aci->bezt, sizeof(BezTriple) * (aci->totvert));
1352 }
1353
1354 /* copy current beztriple across too */
1355 nbezt = &newbuf[aci->totvert];
1356 *nbezt = *bezt;
1357
1358 /* ensure copy buffer is selected so pasted keys are selected */
1359 BEZT_SEL_ALL(nbezt);
1360
1361 /* free old array and set the new */
1362 if (aci->bezt) {
1363 MEM_freeN(aci->bezt);
1364 }
1365 aci->bezt = newbuf;
1366 aci->totvert++;
1367
1368 /* check if this is the earliest frame encountered so far */
1369 if (bezt->vec[1][0] < animcopy_firstframe) {
1370 animcopy_firstframe = bezt->vec[1][0];
1371 }
1372 if (bezt->vec[1][0] > animcopy_lastframe) {
1373 animcopy_lastframe = bezt->vec[1][0];
1374 }
1375 }
1376 }
1377 }
1378
1379 /* check if anything ended up in the buffer */
1380 if (ELEM(nullptr, animcopybuf.first, animcopybuf.last)) {
1381 return -1;
1382 }
1383
1384 /* in case 'relative' paste method is used */
1385 animcopy_cfra = scene->r.cfra;
1386
1387 /* everything went fine */
1388 return 0;
1389}
1390
1391static void flip_names(tAnimCopybufItem *aci, char **r_name)
1392{
1393 if (!aci->is_bone) {
1394 return;
1395 }
1396 int ofs_start, ofs_end;
1397 if (!BLI_str_quoted_substr_range(aci->rna_path, "pose.bones[", &ofs_start, &ofs_end)) {
1398 return;
1399 }
1400
1401 char *str_start = aci->rna_path + ofs_start;
1402 const char *str_end = aci->rna_path + ofs_end;
1403
1404 /* Swap out the name.
1405 * NOTE: there is no need to un-escape the string to flip it.
1406 * However the buffer does need to be twice the size. */
1407 char bname_new[MAX_VGROUP_NAME * 2];
1408 char *str_iter;
1409 int len_old, prefix_l, postfix_l;
1410
1411 prefix_l = str_start - aci->rna_path;
1412
1413 len_old = str_end - str_start;
1414 postfix_l = strlen(str_end);
1415
1416 /* Temporary substitute with nullptr terminator. */
1417 BLI_assert(str_start[len_old] == '\"');
1418 str_start[len_old] = 0;
1419 const int len_new = BLI_string_flip_side_name(bname_new, str_start, false, sizeof(bname_new));
1420 str_start[len_old] = '\"';
1421
1422 str_iter = *r_name = static_cast<char *>(
1423 MEM_mallocN(sizeof(char) * (prefix_l + postfix_l + len_new + 1), "flipped_path"));
1424
1425 memcpy(str_iter, aci->rna_path, prefix_l);
1426 str_iter += prefix_l;
1427 memcpy(str_iter, bname_new, len_new);
1428 str_iter += len_new;
1429 memcpy(str_iter, str_end, postfix_l);
1430 str_iter[postfix_l] = '\0';
1431}
1432
1433/* ------------------- */
1434
1435/* most strict method: exact matches only */
1437 const short from_single,
1438 const short to_simple,
1439 bool flip)
1440{
1441 tAnimCopybufItem *aci;
1442
1443 for (aci = static_cast<tAnimCopybufItem *>(animcopybuf.first); aci; aci = aci->next) {
1444 if (to_simple || (aci->rna_path && fcu->rna_path)) {
1445 if (!to_simple && flip && aci->is_bone && fcu->rna_path) {
1446 if ((from_single) || (aci->array_index == fcu->array_index)) {
1447 char *name = nullptr;
1448 flip_names(aci, &name);
1449 if (STREQ(name, fcu->rna_path)) {
1450 MEM_freeN(name);
1451 break;
1452 }
1453 MEM_freeN(name);
1454 }
1455 }
1456 else if (to_simple || STREQ(aci->rna_path, fcu->rna_path)) {
1457 if ((from_single) || (aci->array_index == fcu->array_index)) {
1458 break;
1459 }
1460 }
1461 }
1462 }
1463
1464 return aci;
1465}
1466
1467/* medium match strictness: path match only (i.e. ignore ID) */
1469 const FCurve *fcu,
1470 const short from_single,
1471 const short /*to_simple*/)
1472{
1473 tAnimCopybufItem *aci;
1474
1475 for (aci = static_cast<tAnimCopybufItem *>(animcopybuf.first); aci; aci = aci->next) {
1476 /* check that paths exist */
1477 if (aci->rna_path && fcu->rna_path) {
1478 /* find the property of the fcurve and compare against the end of the tAnimCopybufItem
1479 * more involved since it needs to do path lookups.
1480 * This is not 100% reliable since the user could be editing the curves on a path that won't
1481 * resolve, or a bone could be renamed after copying for eg. but in normal copy & paste
1482 * this should work out ok.
1483 */
1484 if (BLI_findindex(which_libbase(bmain, aci->id_type), aci->id) == -1) {
1485 /* pedantic but the ID could have been removed, and beats crashing! */
1486 printf("paste_animedit_keys: error ID has been removed!\n");
1487 }
1488 else {
1489 PointerRNA rptr;
1490 PropertyRNA *prop;
1491
1492 PointerRNA id_ptr = RNA_id_pointer_create(aci->id);
1493
1494 if (RNA_path_resolve_property(&id_ptr, aci->rna_path, &rptr, &prop)) {
1495 const char *identifier = RNA_property_identifier(prop);
1496 int len_id = strlen(identifier);
1497 int len_path = strlen(fcu->rna_path);
1498 if (len_id <= len_path) {
1499 /* NOTE: paths which end with "] will fail with this test - Animated ID Props. */
1500 if (STREQ(identifier, fcu->rna_path + (len_path - len_id))) {
1501 if ((from_single) || (aci->array_index == fcu->array_index)) {
1502 break;
1503 }
1504 }
1505 }
1506 }
1507 else {
1508 printf("paste_animedit_keys: failed to resolve path id:%s, '%s'!\n",
1509 aci->id->name,
1510 aci->rna_path);
1511 }
1512 }
1513 }
1514 }
1515
1516 return aci;
1517}
1518
1519/* least strict matching heuristic: indices only */
1521 const short from_single,
1522 const short /*to_simple*/)
1523{
1524 tAnimCopybufItem *aci;
1525
1526 for (aci = static_cast<tAnimCopybufItem *>(animcopybuf.first); aci; aci = aci->next) {
1527 /* check that paths exist */
1528 if ((from_single) || (aci->array_index == fcu->array_index)) {
1529 break;
1530 }
1531 }
1532
1533 return aci;
1534}
1535
1536/* ................ */
1537
1539{
1540 if (aci->is_bone) {
1541 const size_t slength = strlen(aci->rna_path);
1542 bool flip = false;
1543 if (BLI_strn_endswith(aci->rna_path, "location", slength) && aci->array_index == 0) {
1544 flip = true;
1545 }
1546 else if (BLI_strn_endswith(aci->rna_path, "rotation_quaternion", slength) &&
1547 ELEM(aci->array_index, 2, 3))
1548 {
1549 flip = true;
1550 }
1551 else if (BLI_strn_endswith(aci->rna_path, "rotation_euler", slength) &&
1552 ELEM(aci->array_index, 1, 2))
1553 {
1554 flip = true;
1555 }
1556 else if (BLI_strn_endswith(aci->rna_path, "rotation_axis_angle", slength) &&
1557 ELEM(aci->array_index, 2, 3))
1558 {
1559 flip = true;
1560 }
1561
1562 if (flip) {
1563 bezt->vec[0][1] = -bezt->vec[0][1];
1564 bezt->vec[1][1] = -bezt->vec[1][1];
1565 bezt->vec[2][1] = -bezt->vec[2][1];
1566 }
1567 }
1568}
1569
1570/* helper for paste_animedit_keys() - performs the actual pasting */
1572 FCurve *fcu, tAnimCopybufItem *aci, float offset[2], const eKeyMergeMode merge_mode, bool flip)
1573{
1574 BezTriple *bezt;
1575 int i;
1576
1577 /* First de-select existing FCurve's keyframes */
1578 for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
1579 BEZT_DESEL_ALL(bezt);
1580 }
1581
1582 /* mix mode with existing data */
1583 switch (merge_mode) {
1585 /* do-nothing */
1586 break;
1587
1589 /* remove all keys */
1591 break;
1592
1595 float f_min;
1596 float f_max;
1597
1598 if (merge_mode == KEYFRAME_PASTE_MERGE_OVER_RANGE) {
1599 f_min = aci->bezt[0].vec[1][0] + offset[0];
1600 f_max = aci->bezt[aci->totvert - 1].vec[1][0] + offset[0];
1601 }
1602 else { /* Entire Range */
1603 f_min = animcopy_firstframe + offset[0];
1604 f_max = animcopy_lastframe + offset[0];
1605 }
1606
1607 /* remove keys in range */
1608 if (f_min < f_max) {
1609 /* select verts in range for removal */
1610 for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
1611 if ((f_min < bezt[0].vec[1][0]) && (bezt[0].vec[1][0] < f_max)) {
1612 bezt->f2 |= SELECT;
1613 }
1614 }
1615
1616 /* remove frames in the range */
1618 }
1619 break;
1620 }
1621 }
1622
1623 /* just start pasting, with the first keyframe on the current frame, and so on */
1624 for (i = 0, bezt = aci->bezt; i < aci->totvert; i++, bezt++) {
1625 /* temporarily apply offset to src beztriple while copying */
1626 if (flip) {
1627 do_curve_mirror_flippping(aci, bezt);
1628 }
1629
1630 add_v2_v2(bezt->vec[0], offset);
1631 add_v2_v2(bezt->vec[1], offset);
1632 add_v2_v2(bezt->vec[2], offset);
1633
1634 /* insert the keyframe
1635 * NOTE: we do not want to inherit handles from existing keyframes in this case!
1636 */
1637
1639
1640 /* un-apply offset from src beztriple after copying */
1641 sub_v2_v2(bezt->vec[0], offset);
1642 sub_v2_v2(bezt->vec[1], offset);
1643 sub_v2_v2(bezt->vec[2], offset);
1644
1645 if (flip) {
1646 do_curve_mirror_flippping(aci, bezt);
1647 }
1648 }
1649
1650 /* recalculate F-Curve's handles? */
1652}
1653
1656 "START",
1657 0,
1658 "Frame Start",
1659 "Paste keys starting at current frame"},
1660 {KEYFRAME_PASTE_OFFSET_CFRA_END, "END", 0, "Frame End", "Paste keys ending at current frame"},
1662 "RELATIVE",
1663 0,
1664 "Frame Relative",
1665 "Paste keys relative to the current frame when copying"},
1666 {KEYFRAME_PASTE_OFFSET_NONE, "NONE", 0, "No Offset", "Paste keys from original time"},
1667 {0, nullptr, 0, nullptr, nullptr},
1668};
1669
1672 "LEFT_KEY",
1673 0,
1674 "Left Key",
1675 "Paste keys with the first key matching the key left of the cursor"},
1677 "RIGHT_KEY",
1678 0,
1679 "Right Key",
1680 "Paste keys with the last key matching the key right of the cursor"},
1682 "CURRENT_FRAME",
1683 0,
1684 "Current Frame Value",
1685 "Paste keys relative to the value of the curve under the cursor"},
1687 "CURSOR_VALUE",
1688 0,
1689 "Cursor Value",
1690 "Paste keys relative to the Y-Position of the cursor"},
1692 "NONE",
1693 0,
1694 "No Offset",
1695 "Paste keys with the same value as they were copied"},
1696 {0, nullptr, 0, nullptr, nullptr},
1697};
1698
1700 {KEYFRAME_PASTE_MERGE_MIX, "MIX", 0, "Mix", "Overlay existing with new keys"},
1701 {KEYFRAME_PASTE_MERGE_OVER, "OVER_ALL", 0, "Overwrite All", "Replace all keys"},
1703 "OVER_RANGE",
1704 0,
1705 "Overwrite Range",
1706 "Overwrite keys in pasted range"},
1708 "OVER_RANGE_ALL",
1709 0,
1710 "Overwrite Entire Range",
1711 "Overwrite keys in pasted range, using the range of all copied keys"},
1712 {0, nullptr, 0, nullptr, nullptr},
1713};
1714
1716 tAnimCopybufItem *aci,
1717 bAnimListElem *ale,
1718 const eKeyPasteValueOffset value_offset_mode)
1719{
1720 FCurve *fcu = (FCurve *)ale->data;
1721 const float cfra = BKE_scene_frame_get(ac->scene);
1722
1723 switch (value_offset_mode) {
1725 SpaceGraph *sipo = (SpaceGraph *)ac->sl;
1726 const float offset = sipo->cursorVal - aci->bezt[0].vec[1][1];
1727 return offset;
1728 }
1729
1731 const float cfra_y = evaluate_fcurve(fcu, cfra);
1732 const float offset = cfra_y - aci->bezt[0].vec[1][1];
1733 return offset;
1734 }
1735
1737 bool replace;
1738 const int fcu_index = BKE_fcurve_bezt_binarysearch_index(
1739 fcu->bezt, cfra, fcu->totvert, &replace);
1740 BezTriple left_key = fcu->bezt[max_ii(fcu_index - 1, 0)];
1741 const float offset = left_key.vec[1][1] - aci->bezt[0].vec[1][1];
1742 return offset;
1743 }
1744
1746 bool replace;
1747 const int fcu_index = BKE_fcurve_bezt_binarysearch_index(
1748 fcu->bezt, cfra, fcu->totvert, &replace);
1749 BezTriple right_key = fcu->bezt[min_ii(fcu_index, fcu->totvert - 1)];
1750 const float offset = right_key.vec[1][1] - aci->bezt[aci->totvert - 1].vec[1][1];
1751 return offset;
1752 }
1753
1755 break;
1756 }
1757
1758 return 0.0f;
1759}
1760
1762 ListBase *anim_data,
1763 const eKeyPasteOffset offset_mode,
1764 const eKeyPasteValueOffset value_offset_mode,
1765 const eKeyMergeMode merge_mode,
1766 bool flip)
1767{
1768 bAnimListElem *ale;
1769
1770 const Scene *scene = (ac->scene);
1771
1772 const bool from_single = BLI_listbase_is_single(&animcopybuf);
1773 const bool to_simple = BLI_listbase_is_single(anim_data);
1774
1775 float offset[2];
1776 int pass;
1777
1778 /* check if buffer is empty */
1781 }
1782
1783 if (BLI_listbase_is_empty(anim_data)) {
1785 }
1786
1787 /* methods of offset */
1788 switch (offset_mode) {
1790 offset[0] = float(scene->r.cfra - animcopy_firstframe);
1791 break;
1793 offset[0] = float(scene->r.cfra - animcopy_lastframe);
1794 break;
1796 offset[0] = float(scene->r.cfra - animcopy_cfra);
1797 break;
1799 offset[0] = 0.0f;
1800 break;
1801 }
1802
1803 if (from_single && to_simple) {
1804 /* 1:1 match, no tricky checking, just paste */
1805 FCurve *fcu;
1806 tAnimCopybufItem *aci;
1807
1808 ale = static_cast<bAnimListElem *>(anim_data->first);
1809 fcu = (FCurve *)ale->data; /* destination F-Curve */
1810 aci = static_cast<tAnimCopybufItem *>(animcopybuf.first);
1811
1812 offset[1] = paste_get_y_offset(ac, aci, ale, value_offset_mode);
1813 paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode, false);
1815 }
1816 else {
1817 /* from selected channels
1818 * This "passes" system aims to try to find "matching" channels to paste keyframes
1819 * into with increasingly loose matching heuristics. The process finishes when at least
1820 * one F-Curve has been pasted into.
1821 */
1822 for (pass = 0; pass < 3; pass++) {
1823 uint totmatch = 0;
1824
1825 LISTBASE_FOREACH (bAnimListElem *, ale, anim_data) {
1826 /* Find buffer item to paste from:
1827 * - If names don't matter (i.e. only 1 channel in buffer), don't check id/group
1828 * - If names do matter, only check if id-type is ok for now
1829 * (group check is not that important).
1830 * - Most importantly, rna-paths should match (array indices are unimportant for now)
1831 */
1832 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1833 FCurve *fcu = (FCurve *)ale->data; /* destination F-Curve */
1834 tAnimCopybufItem *aci = nullptr;
1835
1836 switch (pass) {
1837 case 0:
1838 /* most strict, must be exact path match data_path & index */
1839 aci = pastebuf_match_path_full(fcu, from_single, to_simple, flip);
1840 break;
1841
1842 case 1:
1843 /* less strict, just compare property names */
1844 aci = pastebuf_match_path_property(ac->bmain, fcu, from_single, to_simple);
1845 break;
1846
1847 case 2:
1848 /* Comparing properties gave no results, so just do index comparisons */
1849 aci = pastebuf_match_index_only(fcu, from_single, to_simple);
1850 break;
1851 }
1852
1853 /* copy the relevant data from the matching buffer curve */
1854 if (aci) {
1855 totmatch++;
1856
1857 offset[1] = paste_get_y_offset(ac, aci, ale, value_offset_mode);
1858 if (adt) {
1859 ANIM_nla_mapping_apply_fcurve(adt, static_cast<FCurve *>(ale->key_data), false, false);
1860 paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode, flip);
1861 ANIM_nla_mapping_apply_fcurve(adt, static_cast<FCurve *>(ale->key_data), true, false);
1862 }
1863 else {
1864 paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode, flip);
1865 }
1866 }
1867
1869 }
1870
1871 /* don't continue if some fcurves were pasted */
1872 if (totmatch) {
1873 break;
1874 }
1875 }
1876 }
1877
1878 ANIM_animdata_update(ac, anim_data);
1879
1880 return KEYFRAME_PASTE_OK;
1881}
1882
Functions to work with AnimData.
Functions to modify FCurves.
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
unsigned int BKE_curve_decimate_bezt_array(BezTriple *bezt_array, unsigned int bezt_array_len, unsigned int resolu, bool is_cyclic, char flag_test, char flag_set, float error_sq_max, unsigned int error_target_len)
int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[], float frame, int arraylen, bool *r_replace)
void BKE_fcurve_handles_recalc(FCurve *fcu)
bool BKE_fcurve_is_empty(const FCurve *fcu)
bool BKE_fcurve_delete_keys_selected(FCurve *fcu)
void BKE_fcurve_keyframe_move_value_with_handles(BezTriple *keyframe, float new_value)
float evaluate_fcurve(const FCurve *fcu, float evaltime)
void BKE_fcurve_delete_keys_all(FCurve *fcu)
ListBase * which_libbase(Main *bmain, short type)
Definition main.cc:842
float BKE_scene_frame_get(const Scene *scene)
Definition scene.cc:2331
#define BLI_assert(a)
Definition BLI_assert.h:50
sqrt(x)+1/max(0
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void void BLI_INLINE bool BLI_listbase_is_single(const struct ListBase *lb)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int min_ii(int a, int b)
MINLINE float pow2f(float x)
MINLINE float clamp_f(float value, float min, float max)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
MINLINE float floored_fmod(float f, float n)
MINLINE float interpf(float target, float origin, float t)
#define M_PI
MINLINE void sub_v2_v2(float r[2], const float a[2])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE void add_v3_v3(float r[3], const float a[3])
int bool bool bool BLI_strn_endswith(const char *__restrict str, const char *__restrict end, size_t str_len) ATTR_NONNULL(1
bool BLI_str_quoted_substr_range(const char *__restrict str, const char *__restrict prefix, int *__restrict r_start, int *__restrict r_end) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
bool bool BLI_str_quoted_substr(const char *__restrict str, const char *__restrict prefix, char *result, size_t result_maxncpy)
Definition string.c:517
size_t BLI_string_flip_side_name(char *name_dst, const char *name_src, bool strip_number, size_t name_dst_maxncpy) ATTR_NONNULL(1
unsigned int uint
#define ELEM(...)
#define IS_EQF(a, b)
#define IS_EQT(a, b, c)
#define STREQ(a, b)
typedef double(DMatrix)[4][4]
@ ID_OB
eInsertKeyFlags
@ INSERTKEY_OVERWRITE_FULL
#define BEZT_SEL_ALL(bezt)
#define BEZT_ISSEL_ANY(bezt)
#define BEZT_DESEL_ALL(bezt)
@ HD_FREE
@ BEZT_IPO_BEZ
@ BEZT_IPO_LIN
@ BEZT_FLAG_TEMP_TAG
@ BEZT_FLAG_IGNORE_TAG
Object is a sort of wrapper for general info.
@ OB_ARMATURE
#define MAX_VGROUP_NAME
#define ANIM_UPDATE_DEFAULT
tShearDirection
@ SHEAR_FROM_LEFT
@ KEYFRAME_PASTE_MERGE_OVER_RANGE_ALL
@ KEYFRAME_PASTE_MERGE_OVER_RANGE
@ KEYFRAME_PASTE_MERGE_OVER
@ KEYFRAME_PASTE_MERGE_MIX
eKeyPasteValueOffset
@ KEYFRAME_PASTE_VALUE_OFFSET_RIGHT_KEY
@ KEYFRAME_PASTE_VALUE_OFFSET_NONE
@ KEYFRAME_PASTE_VALUE_OFFSET_CURSOR
@ KEYFRAME_PASTE_VALUE_OFFSET_CFRA
@ KEYFRAME_PASTE_VALUE_OFFSET_LEFT_KEY
eKeyPasteOffset
@ KEYFRAME_PASTE_OFFSET_NONE
@ KEYFRAME_PASTE_OFFSET_CFRA_END
@ KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE
@ KEYFRAME_PASTE_OFFSET_CFRA_START
@ BEZT_OK_SELECTED
@ KEYFRAME_PASTE_NOTHING_TO_PASTE
@ KEYFRAME_PASTE_OK
@ KEYFRAME_PASTE_NOWHERE_TO_PASTE
FCurveSegmentAnchor
Read Guarded memory(de)allocation.
@ PROP_FLOAT
Definition RNA_types.hh:67
@ PROP_BOOLEAN
Definition RNA_types.hh:65
@ PROP_INT
Definition RNA_types.hh:66
void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data)
Definition anim_deps.cc:350
AnimData * ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale)
Definition anim_draw.cc:210
void ANIM_nla_mapping_apply_fcurve(AnimData *adt, FCurve *fcu, bool restore, bool only_keys)
Definition anim_draw.cc:290
static T sum(const btAlignedObjectArray< T > &items)
pow(value.r - subtrahend, 2.0)") .do_static_compilation(true)
#define printf
#define SELECT
#define floorf(x)
int len
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define GS(x)
Definition iris.cc:202
short ANIM_fcurve_keyframes_loop(KeyframeEditData *ked, FCurve *fcu, KeyframeEditFunc key_ok, KeyframeEditFunc key_cb, FcuEditFunc fcu_cb)
KeyframeEditFunc ANIM_editkeyframes_ok(short mode)
static float animcopy_cfra
void ED_anim_calculate_butterworth_coefficients(const float cutoff_frequency, const float sampling_frequency, ButterworthCoefficients *bw_coeff)
static float butterworth_calculate_blend_value(float *samples, float *filtered_values, const int start_index, const int end_index, const int sample_index, const int blend_in_out)
void clean_fcurve(bAnimContext *ac, bAnimListElem *ale, float thresh, bool cleardefault, const bool only_selected_keys)
static bool prepare_for_decimate(FCurve *fcu, int i)
static tAnimCopybufItem * pastebuf_match_path_full(const FCurve *fcu, const short from_single, const short to_simple, bool flip)
static tAnimCopybufItem * pastebuf_match_path_property(Main *bmain, const FCurve *fcu, const short from_single, const short)
void butterworth_smooth_fcurve_segment(FCurve *fcu, FCurveSegment *segment, float *samples, const int sample_count, const float factor, const int blend_in_out, const int sample_rate, ButterworthCoefficients *bw_coeff)
const EnumPropertyItem rna_enum_keyframe_paste_offset_items[]
static const BezTriple * fcurve_segment_end_get(FCurve *fcu, int index)
float get_default_rna_value(const FCurve *fcu, PropertyRNA *prop, PointerRNA *ptr)
bool decimate_fcurve(bAnimListElem *ale, float remove_ratio, float error_sq_max)
void breakdown_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data)
static bool find_fcurve_segment(FCurve *fcu, const int start_index, int *r_segment_start_idx, int *r_segment_len)
const EnumPropertyItem rna_enum_keyframe_paste_merge_items[]
void ease_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor, const float width)
eKeyPasteError paste_animedit_keys(bAnimContext *ac, ListBase *anim_data, const eKeyPasteOffset offset_mode, const eKeyPasteValueOffset value_offset_mode, const eKeyMergeMode merge_mode, bool flip)
ButterworthCoefficients * ED_anim_allocate_butterworth_coefficients(const int filter_order)
static void flip_names(tAnimCopybufItem *aci, char **r_name)
void time_offset_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float frame_offset)
void ED_ANIM_get_1d_gauss_kernel(const float sigma, const int kernel_size, double *r_kernel)
static tAnimCopybufItem * pastebuf_match_index_only(const FCurve *fcu, const short from_single, const short)
void scale_average_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
void blend_offset_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
const EnumPropertyItem rna_enum_keyframe_paste_offset_value_items[]
void shear_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor, tShearDirection direction)
void ANIM_fcurves_copybuf_free()
static const BezTriple * fcurve_segment_start_get(FCurve *fcu, int index)
static float ease_sigmoid_function(const float x, const float width, const float shift)
void blend_to_ease_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
static void decimate_fcurve_segment(FCurve *fcu, int bezt_segment_start_idx, int bezt_segment_len, float remove_ratio, float error_sq_max)
static void paste_animedit_keys_fcurve(FCurve *fcu, tAnimCopybufItem *aci, float offset[2], const eKeyMergeMode merge_mode, bool flip)
void smooth_fcurve_segment(FCurve *fcu, FCurveSegment *segment, float *samples, const float factor, const int kernel_size, double *kernel)
static double butterworth_filter_value(double x, double *w0, double *w1, double *w2, ButterworthCoefficients *bw_coeff)
bool match_slope_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
void smooth_fcurve(FCurve *fcu)
void push_pull_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
void blend_to_default_fcurve(PointerRNA *id_ptr, FCurve *fcu, const float factor)
static void do_curve_mirror_flippping(tAnimCopybufItem *aci, BezTriple *bezt)
static ListBase animcopybuf
static float s_curve(float x, float slope, float width, float height, float xshift, float yshift)
ListBase find_fcurve_segments(FCurve *fcu)
bool duplicate_fcurve_keys(FCurve *fcu)
void scale_from_fcurve_segment_neighbor(FCurve *fcu, FCurveSegment *segment, const float factor, const FCurveSegmentAnchor anchor)
static float animcopy_firstframe
void blend_to_neighbor_fcurve_segment(FCurve *fcu, FCurveSegment *segment, const float factor)
static float paste_get_y_offset(bAnimContext *ac, tAnimCopybufItem *aci, bAnimListElem *ale, const eKeyPasteValueOffset value_offset_mode)
void ED_anim_free_butterworth_coefficients(ButterworthCoefficients *bw_coeff)
static float animcopy_lastframe
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
ccl_device_inline float2 fabs(const float2 a)
ccl_device_inline float3 exp(float3 v)
ccl_device_inline float3 ceil(const float3 a)
static ulong * next
int insert_bezt_fcurve(FCurve *fcu, const BezTriple *bezt, eInsertKeyFlags flag)
Lesser Key-framing API call.
void animdata_fcurve_delete(bAnimContext *ac, AnimData *adt, FCurve *fcu)
Definition animdata.cc:253
static bool point_is_selected(PTCacheEditPoint *point)
bool RNA_property_boolean_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index)
int RNA_property_int_get_default(PointerRNA *ptr, PropertyRNA *prop)
float RNA_property_float_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index)
PropertyType RNA_property_type(PropertyRNA *prop)
int RNA_property_int_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index)
float RNA_property_float_get_default(PointerRNA *ptr, PropertyRNA *prop)
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_property_boolean_get_default(PointerRNA *ptr, PropertyRNA *prop)
const char * RNA_property_identifier(const PropertyRNA *prop)
PointerRNA RNA_id_pointer_create(ID *id)
bool RNA_path_resolve_property(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
Definition rna_path.cc:553
float vec[3][3]
bActionGroup * grp
char * rna_path
BezTriple * bezt
int array_index
unsigned int totvert
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
void * last
void * first
struct bPose * pose
SpaceLink * sl
AnimData * adt
eAnim_Update_Flags update
tAnimCopybufItem * prev
tAnimCopybufItem * next
PointerRNA * ptr
Definition wm_files.cc:4126