Blender V4.5
transform_convert_graph.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "DNA_anim_types.h"
10#include "DNA_space_types.h"
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_listbase.h"
15#include "BLI_map.hh"
16#include "BLI_math_matrix.h"
17#include "BLI_math_vector.h"
18#include "BLI_set.hh"
19
20#include "BKE_context.hh"
21#include "BKE_fcurve.hh"
22#include "BKE_layer.hh"
23#include "BKE_nla.hh"
24
25#include "ED_anim_api.hh"
26#include "ED_keyframes_edit.hh"
27
28#include "UI_view2d.hh"
29
30#include "transform.hh"
32#include "transform_convert.hh"
33#include "transform_snap.hh"
34
35namespace blender::ed::transform {
36
39 float offset;
40};
41
42/* -------------------------------------------------------------------- */
45
51 TransData2D *td2d,
52 TransDataGraph *tdg,
53 bAnimListElem *ale,
54 BezTriple *bezt,
55 int bi,
56 bool selected,
57 bool ishandle,
58 bool intvals,
59 const float mtx[3][3],
60 const float smtx[3][3],
61 float unit_scale,
62 float offset)
63{
64 float *loc = bezt->vec[bi];
65 const float *cent = bezt->vec[1];
66
67 /* New location from td gets dumped onto the old-location of td2d, which then
68 * gets copied to the actual data at td2d->loc2d (bezt->vec[n])
69 *
70 * Due to NLA mapping, we apply NLA mapping to some of the verts here,
71 * and then that mapping will be undone after transform is done.
72 */
73
74 if (ANIM_nla_mapping_allowed(ale)) {
75 td2d->loc[0] = ANIM_nla_tweakedit_remap(ale, loc[0], NLATIME_CONVERT_MAP);
76 td2d->loc[1] = (loc[1] + offset) * unit_scale;
77 td2d->loc[2] = 0.0f;
78 td2d->loc2d = loc;
79
80 td->loc = td2d->loc;
82 td->center[1] = (cent[1] + offset) * unit_scale;
83 td->center[2] = 0.0f;
84
85 copy_v3_v3(td->iloc, td->loc);
86 }
87 else {
88 td2d->loc[0] = loc[0];
89 td2d->loc[1] = (loc[1] + offset) * unit_scale;
90 td2d->loc[2] = 0.0f;
91 td2d->loc2d = loc;
92
93 td->loc = td2d->loc;
94 copy_v3_v3(td->center, cent);
95 td->center[1] = (td->center[1] + offset) * unit_scale;
96 copy_v3_v3(td->iloc, td->loc);
97 }
98
99 if (!ishandle) {
100 td2d->h1 = bezt->vec[0];
101 td2d->h2 = bezt->vec[2];
102 copy_v2_v2(td2d->ih1, td2d->h1);
103 copy_v2_v2(td2d->ih2, td2d->h2);
104 }
105 else {
106 td2d->h1 = nullptr;
107 td2d->h2 = nullptr;
108 }
109
110 memset(td->axismtx, 0, sizeof(td->axismtx));
111 td->axismtx[2][2] = 1.0f;
112
113 td->ext = nullptr;
114 td->val = nullptr;
115
116 /* Store AnimData info in td->extra, for applying mapping when flushing.
117 *
118 * We do this conditionally as a hacky way of indicating whether NLA remapping
119 * should be done. This is left over from old code, most of which was changed
120 * in #130440 to avoid using `adt == nullptr` as an indicator for that. This
121 * was left that way because updating it cleanly was more involved than made
122 * sense for the bug fix in #130440. */
123 if (ANIM_nla_mapping_allowed(ale)) {
124 td->extra = ale->adt;
125 }
126
127 if (selected) {
128 td->flag |= TD_SELECTED;
129 td->dist = 0.0f;
130 }
131 else {
132 td->dist = FLT_MAX;
133 }
134
135 if (ishandle) {
136 td->flag |= TD_NOTIMESNAP;
137 }
138 if (intvals) {
139 td->flag |= TD_INTVALUES;
140 }
141
142 /* Copy space-conversion matrices for dealing with non-uniform scales. */
143 copy_m3_m3(td->mtx, mtx);
144 copy_m3_m3(td->smtx, smtx);
145
146 tdg->unit_scale = unit_scale;
147 tdg->offset = offset;
148}
149
154
156{
157 return ((t->around == V3D_AROUND_LOCAL_ORIGINS) && (graph_edit_is_translation_mode(t) == false));
158}
159
160static void enable_autolock(TransInfo *t, SpaceGraph *space_graph)
161{
162 /* Locking the axis makes most sense for translation. We may want to enable it for scaling as
163 * well if artists require that. */
164 if (t->mode != TFM_TRANSLATION) {
165 return;
166 }
167
168 /* These flags are set when using tweak mode on handles. */
171 {
172 return;
173 }
174
176}
177
183 const BezTriple *bezt,
184 const bool use_handle,
185 bool *r_left_handle,
186 bool *r_key,
187 bool *r_right_handle)
188{
190 bool key = (bezt->f2 & SELECT) != 0;
191 bool left = use_handle ? ((bezt->f1 & SELECT) != 0) : key;
192 bool right = use_handle ? ((bezt->f3 & SELECT) != 0) : key;
193
194 if (use_handle && t->is_launch_event_drag) {
196 key = right = false;
197 }
199 left = key = false;
200 }
201 }
202
203 /* Whenever we move the key, we also move both handles. */
204 if (key) {
205 left = right = true;
206 }
207
208 *r_key = key;
209 *r_left_handle = left;
210 *r_right_handle = right;
211}
212
214 TransInfo *t, FCurve *fcu, TransData *td_start, TransData *td, int cfra, bool use_handle)
215{
216 int j = 0;
217 TransData *td_iter = td_start;
218 bool sel_key, sel_left, sel_right;
219
220 float dist = FLT_MAX;
221 for (; j < fcu->totvert; j++) {
222 BezTriple *bezt = fcu->bezt + j;
223 if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
224 graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right);
225
226 if (sel_left || sel_key || sel_right) {
227 dist = min_fff(dist, td->dist, fabs(td_iter->center[0] - td->center[0]));
228 }
229
230 td_iter += 3;
231 }
232 }
233
234 return dist;
235}
236
247{
249 Scene *scene = t->scene;
250 ARegion *region = t->region;
251 View2D *v2d = &region->v2d;
252
253 TransData *td = nullptr;
254 TransData2D *td2d = nullptr;
255 TransDataGraph *tdg = nullptr;
256
257 bAnimContext ac;
258 ListBase anim_data = {nullptr, nullptr};
259 int filter;
260
261 BezTriple *bezt;
262 int count = 0, i;
263 float mtx[3][3], smtx[3][3];
264 const bool use_handle = !(sipo->flag & SIPO_NOHANDLES);
265 const bool use_local_center = graph_edit_use_local_center(t);
266 const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
267 short anim_map_flag = ANIM_UNITCONV_ONLYSEL | ANIM_UNITCONV_SELVERTS;
268 bool sel_key, sel_left, sel_right;
269
270 /* Determine what type of data we are operating on. */
271 if (ANIM_animdata_get_context(C, &ac) == 0) {
272 return;
273 }
274
275 anim_map_flag |= ANIM_get_normalization_flags(ac.sl);
276
277 /* Filter data. */
281 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
282
283 /* Which side of the current frame should be allowed. */
284 /* XXX we still want this mode, but how to get this using standard transform too? */
285 if (t->mode == TFM_TIME_EXTEND) {
287 }
288 else {
289 /* Normal transform - both sides of current frame are considered. */
290 t->frame_side = 'B';
291 }
292
293 /* Loop 1: count how many BezTriples (specifically their verts)
294 * are selected (or should be edited). */
295 Set<FCurve *> visited_fcurves;
296 Vector<bAnimListElem *> unique_fcu_anim_list_elements;
297 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
298 FCurve *fcu = (FCurve *)ale->key_data;
299 /* If 2 or more objects share the same action, multiple bAnimListElem might reference the same
300 * FCurve. */
301 if (!visited_fcurves.add(fcu)) {
302 continue;
303 }
304 unique_fcu_anim_list_elements.append(ale);
305 int curvecount = 0;
306 bool selected = false;
307
308 /* F-Curve may not have any keyframes. */
309 if (fcu->bezt == nullptr) {
310 continue;
311 }
312
313 /* Convert current-frame to action-time (slightly less accurate, especially under
314 * higher scaling ratios, but is faster than converting all points). */
315 const float cfra = ANIM_nla_tweakedit_remap(ale, float(scene->r.cfra), NLATIME_CONVERT_UNMAP);
316
317 for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
318 /* Only include BezTriples whose 'keyframe'
319 * occurs on the same side of the current frame as mouse. */
320 if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
321 graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right);
322
323 if (is_prop_edit) {
324 curvecount += 3;
325 if (sel_key || sel_left || sel_right) {
326 selected = true;
327 }
328 }
329 else {
330 if (sel_left) {
331 count++;
332 }
333
334 if (sel_right) {
335 count++;
336 }
337
338 /* Only include main vert if selected. */
339 if (sel_key && !use_local_center) {
340 count++;
341 }
342 }
343 }
344 }
345
346 if (is_prop_edit) {
347 if (selected) {
348 count += curvecount;
349 ale->tag = true;
350 }
351 }
352 }
353
354 /* Stop if trying to build list if nothing selected. */
355 if (count == 0) {
356 /* Cleanup temp list. */
357 ANIM_animdata_freelist(&anim_data);
358 return;
359 }
360
362
363 /* Allocate memory for data. */
364 tc->data_len = count;
365
366 tc->data = MEM_calloc_arrayN<TransData>(tc->data_len, "TransData (Graph Editor)");
367 /* For each 2d vert a 3d vector is allocated,
368 * so that they can be treated just as if they were 3d verts. */
369 tc->data_2d = MEM_calloc_arrayN<TransData2D>(tc->data_len, "TransData2D (Graph Editor)");
370 tc->custom.type.data = MEM_callocN(tc->data_len * sizeof(TransDataGraph), "TransDataGraph");
371 tc->custom.type.use_free = true;
372
373 td = tc->data;
374 td2d = tc->data_2d;
375 tdg = static_cast<TransDataGraph *>(tc->custom.type.data);
376
377 /* Precompute space-conversion matrices for dealing with non-uniform scaling of Graph Editor. */
378 unit_m3(mtx);
379 unit_m3(smtx);
380
381 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
382 float xscale, yscale;
383
384 /* Apply scale factors to x and y axes of space-conversion matrices. */
385 UI_view2d_scale_get(v2d, &xscale, &yscale);
386
387 /* `mtx` is data to global (i.e. view) conversion. */
388 mul_v3_fl(mtx[0], xscale);
389 mul_v3_fl(mtx[1], yscale);
390
391 /* `smtx` is global (i.e. view) to data conversion. */
392 if (IS_EQF(xscale, 0.0f) == 0) {
393 mul_v3_fl(smtx[0], 1.0f / xscale);
394 }
395 if (IS_EQF(yscale, 0.0f) == 0) {
396 mul_v3_fl(smtx[1], 1.0f / yscale);
397 }
398 }
399
400 bool at_least_one_key_selected = false;
401
402 /* Loop 2: build transdata arrays. */
403 for (bAnimListElem *ale : unique_fcu_anim_list_elements) {
404 FCurve *fcu = (FCurve *)ale->key_data;
405 bool intvals = (fcu->flag & FCURVE_INT_VALUES) != 0;
406 float unit_scale, offset;
407
408 /* F-Curve may not have any keyframes. */
409 if (fcu->bezt == nullptr || (is_prop_edit && ale->tag == 0)) {
410 continue;
411 }
412
413 /* Convert current-frame to action-time (slightly less accurate, especially under
414 * higher scaling ratios, but is faster than converting all points). */
415 const float cfra = ANIM_nla_tweakedit_remap(ale, float(scene->r.cfra), NLATIME_CONVERT_UNMAP);
416
417 unit_scale = ANIM_unit_mapping_get_factor(
418 ac.scene, ale->id, static_cast<FCurve *>(ale->key_data), anim_map_flag, &offset);
419
420 for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
421 /* Ensure temp flag is cleared for all triples, we use it. */
422 bezt->f1 &= ~BEZT_FLAG_TEMP_TAG;
423 bezt->f2 &= ~BEZT_FLAG_TEMP_TAG;
424 bezt->f3 &= ~BEZT_FLAG_TEMP_TAG;
425
426 /* Only include BezTriples whose 'keyframe' occurs on the same side
427 * of the current frame as mouse (if applicable). */
428 if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
429 TransDataCurveHandleFlags *hdata = nullptr;
430
431 graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right);
432 at_least_one_key_selected |= sel_key;
433 if (is_prop_edit) {
434 bool is_sel = (sel_key || sel_left || sel_right);
435 /* We always select all handles for proportional editing * if central handle is
436 * selected. */
439 td2d++,
440 tdg++,
441 ale,
442 bezt,
443 0,
444 is_sel,
445 true,
446 intvals,
447 mtx,
448 smtx,
449 unit_scale,
450 offset);
453 td2d++,
454 tdg++,
455 ale,
456 bezt,
457 1,
458 is_sel,
459 false,
460 intvals,
461 mtx,
462 smtx,
463 unit_scale,
464 offset);
467 td2d++,
468 tdg++,
469 ale,
470 bezt,
471 2,
472 is_sel,
473 true,
474 intvals,
475 mtx,
476 smtx,
477 unit_scale,
478 offset);
479
480 if (is_sel) {
481 bezt->f1 |= BEZT_FLAG_TEMP_TAG;
482 bezt->f2 |= BEZT_FLAG_TEMP_TAG;
483 bezt->f3 |= BEZT_FLAG_TEMP_TAG;
484 }
485 }
486 else {
487 /* Only include handles if selected, irrespective of the interpolation modes.
488 * also, only treat handles specially if the center point isn't selected. */
489 if (sel_left) {
490 hdata = initTransDataCurveHandles(td, bezt);
492 td2d++,
493 tdg++,
494 ale,
495 bezt,
496 0,
497 sel_left,
498 true,
499 intvals,
500 mtx,
501 smtx,
502 unit_scale,
503 offset);
504 bezt->f1 |= BEZT_FLAG_TEMP_TAG;
505 }
506
507 if (sel_right) {
508 if (hdata == nullptr) {
509 hdata = initTransDataCurveHandles(td, bezt);
510 }
512 td2d++,
513 tdg++,
514 ale,
515 bezt,
516 2,
517 sel_right,
518 true,
519 intvals,
520 mtx,
521 smtx,
522 unit_scale,
523 offset);
524 bezt->f3 |= BEZT_FLAG_TEMP_TAG;
525 }
526
527 /* Only include main vert if selected. */
528 if (sel_key && !use_local_center) {
529 /* Move handles relative to center. */
531 if (sel_left) {
532 td->flag |= TD_MOVEHANDLE1;
533 }
534 if (sel_right) {
535 td->flag |= TD_MOVEHANDLE2;
536 }
537 }
538
539 /* If handles were not selected, store their selection status. */
540 if (!(sel_left) || !(sel_right)) {
541 if (hdata == nullptr) {
542 hdata = initTransDataCurveHandles(td, bezt);
543 }
544 }
545
547 td2d++,
548 tdg++,
549 ale,
550 bezt,
551 1,
552 sel_key,
553 false,
554 intvals,
555 mtx,
556 smtx,
557 unit_scale,
558 offset);
559 bezt->f2 |= BEZT_FLAG_TEMP_TAG;
560 }
561 /* Special hack (must be done after #initTransDataCurveHandles(),
562 * as that stores handle settings to restore...):
563 *
564 * - Check if we've got entire BezTriple selected and we're scaling/rotating that point,
565 * then check if we're using auto-handles.
566 * - If so, change them auto-handles to aligned handles so that handles get affected too.
567 */
568 if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) && ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM) &&
570 {
571 if (hdata && (sel_left) && (sel_right)) {
572 bezt->h1 = HD_ALIGN;
573 bezt->h2 = HD_ALIGN;
574 }
575 }
576 }
577 }
578 }
579
580 /* Sets handles based on the selection. */
581 testhandles_fcurve(fcu, BEZT_FLAG_TEMP_TAG, use_handle);
582 }
583
584 if (is_prop_edit) {
585 /* Loop 3: build proportional edit distances. */
586 td = tc->data;
587
588 for (bAnimListElem *ale : unique_fcu_anim_list_elements) {
589 FCurve *fcu = (FCurve *)ale->key_data;
590 TransData *td_start = td;
591
592 /* F-Curve may not have any keyframes. */
593 if (fcu->bezt == nullptr || (ale->tag == 0)) {
594 continue;
595 }
596
597 /* Convert current-frame to action-time (slightly less accurate, especially under
598 * higher scaling ratios, but is faster than converting all points). */
599 const float cfra = ANIM_nla_tweakedit_remap(
600 ale, float(scene->r.cfra), NLATIME_CONVERT_UNMAP);
601
602 for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
603 /* Only include BezTriples whose 'keyframe' occurs on the
604 * same side of the current frame as mouse (if applicable). */
605 if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
606 graph_bezt_get_transform_selection(t, bezt, use_handle, &sel_left, &sel_key, &sel_right);
607
608 /* Now determine to distance for proportional editing for all three TransData
609 * (representing the key as well as both handles). Note though that the way
610 * #bezt_to_transdata sets up the TransData, the td->center[0] will always be based on
611 * the key (bezt->vec[1]) which means that #graph_key_shortest_dist will return the
612 * same for all of them and we can reuse that (expensive) result if needed. Might be
613 * worth looking into using a 2D KDTree in the future as well. */
614
615 float dist = FLT_MAX;
616 if (sel_left || sel_key || sel_right) {
617 /* If either left handle or key or right handle is selected, all will move fully. */
618 dist = 0.0f;
619 }
620 else {
621 /* If nothing is selected, left handle and key and right handle will share the same (to
622 * be calculated) distance. */
623 dist = graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
624 }
625
626 td->dist = td->rdist = dist;
627 (td + 1)->dist = (td + 1)->rdist = dist;
628 (td + 2)->dist = (td + 2)->rdist = dist;
629 td += 3;
630 }
631 }
632 }
633 }
634
635 if (sipo->flag & SIPO_AUTOLOCK_AXIS && at_least_one_key_selected) {
636 enable_autolock(t, sipo);
637 }
638
639 /* Cleanup temp list. */
640 ANIM_animdata_freelist(&anim_data);
641}
642
644
645/* -------------------------------------------------------------------- */
648
649static bool fcu_test_selected(FCurve *fcu)
650{
651 BezTriple *bezt = fcu->bezt;
652 uint i;
653
654 if (bezt == nullptr) { /* Ignore baked. */
655 return false;
656 }
657
658 for (i = 0; i < fcu->totvert; i++, bezt++) {
659 if (BEZT_ISSEL_ANY(bezt)) {
660 return true;
661 }
662 }
663
664 return false;
665}
666
672{
673 TransData *td;
674 TransData2D *td2d;
675 TransDataGraph *tdg;
676 int a;
677
678 eSnapMode snap_mode = t->tsnap.mode;
679
681 /* Flush to 2d vector from internally used 3d vector. */
682 for (a = 0,
683 td = tc->data,
684 td2d = tc->data_2d,
685 tdg = static_cast<TransDataGraph *>(tc->custom.type.data);
686 a < tc->data_len;
687 a++, td++, td2d++, tdg++)
688 {
689 /* Pointers to relevant AnimData blocks are stored in the `td->extra` pointers. */
690 AnimData *adt = (AnimData *)td->extra;
691
692 float inv_unit_scale = 1.0f / tdg->unit_scale;
693
694 /* Handle snapping for time values:
695 * - We should still be in NLA-mapping time-space.
696 * - Only apply to keyframes (but never to handles).
697 * - Don't do this when canceling, or else these changes won't go away.
698 */
699 if ((t->tsnap.flag & SCE_SNAP) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) {
700 transform_snap_anim_flush_data(t, td, snap_mode, td->loc);
701 }
702
703 /* We need to unapply the nla-mapping from the time in some situations. */
704 if (adt) {
705 td2d->loc2d[0] = BKE_nla_tweakedit_remap(adt, td2d->loc[0], NLATIME_CONVERT_UNMAP);
706 }
707 else {
708 td2d->loc2d[0] = td2d->loc[0];
709 }
710
711 /* If int-values only, truncate to integers. */
712 if (td->flag & TD_INTVALUES) {
713 td2d->loc2d[1] = floorf(td2d->loc[1] * inv_unit_scale - tdg->offset + 0.5f);
714 }
715 else {
716 td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale - tdg->offset;
717 }
718
719 transform_convert_flush_handle2D(td, td2d, inv_unit_scale);
720 }
721}
722
731
735static Vector<BeztMap> bezt_to_beztmaps(BezTriple *bezts, const int totvert)
736{
737 if (totvert == 0 || bezts == nullptr) {
738 return {};
739 }
740
741 Vector<BeztMap> bezms = Vector<BeztMap>(totvert);
742
743 for (const int i : bezms.index_range()) {
744 BezTriple *bezt = &bezts[i];
745 BeztMap &bezm = bezms[i];
746 bezm.bezt = bezt;
747 bezm.swap_handles = false;
748 bezm.oldIndex = i;
749 }
750
751 return bezms;
752}
753
754/* This function copies the code of sort_time_ipocurve, but acts on BeztMap structs instead. */
756{
757 /* Check if handles need to be swapped. */
758 for (BeztMap &bezm : bezms) {
759 /* Handles are only swapped if they are both on the wrong side of the key. Otherwise the one
760 * handle out of place is just clamped at the key position later. */
761 bezm.swap_handles = (bezm.bezt->vec[0][0] > bezm.bezt->vec[1][0] &&
762 bezm.bezt->vec[2][0] < bezm.bezt->vec[1][0]);
763 }
764
765 bool ok = true;
766 const int bezms_size = bezms.size();
767 if (bezms_size < 2) {
768 /* No sorting is needed with only 0 or 1 entries. */
769 return;
770 }
771 const IndexRange bezm_range = bezms.index_range().drop_back(1);
772
773 /* Keep repeating the process until nothing is out of place anymore. */
774 while (ok) {
775 ok = false;
776 for (const int i : bezm_range) {
777 BeztMap *bezm = &bezms[i];
778 /* Is current bezm out of order (i.e. occurs later than next)? */
779 if (bezm->bezt->vec[1][0] > (bezm + 1)->bezt->vec[1][0]) {
780 std::swap(*bezm, *(bezm + 1));
781 ok = true;
782 }
783 }
784 }
785}
786
787static inline void update_trans_data(TransData *td,
788 const FCurve *fcu,
789 const int new_index,
790 const bool swap_handles)
791{
792 if (td->flag & TD_BEZTRIPLE && td->hdata) {
793 if (swap_handles) {
794 td->hdata->h1 = &fcu->bezt[new_index].h2;
795 td->hdata->h2 = &fcu->bezt[new_index].h1;
796 }
797 else {
798 td->hdata->h1 = &fcu->bezt[new_index].h1;
799 td->hdata->h2 = &fcu->bezt[new_index].h2;
800 }
801 }
802}
803
804/* Adjust the pointers that the transdata has to each BezTriple. */
806 const Map<float *, int> &trans_data_map,
807 const FCurve *fcu,
808 const Span<BeztMap> bezms)
809{
810 /* At this point, beztmaps are already sorted, so their current index is assumed to be what the
811 * BezTriple index will be after sorting. */
812 for (const int new_index : bezms.index_range()) {
813 const BeztMap &bezm = bezms[new_index];
814 if (new_index == bezm.oldIndex && !bezm.swap_handles) {
815 /* If the index is the same, any pointers to BezTriple will still point to the correct data.
816 * Handles might need to be swapped though. */
817 continue;
818 }
819
820 TransData2D *td2d;
821 TransData *td;
822
823 if (const int *trans_data_index = trans_data_map.lookup_ptr(bezm.bezt->vec[0])) {
824 td2d = &tc->data_2d[*trans_data_index];
825 if (bezm.swap_handles) {
826 td2d->loc2d = fcu->bezt[new_index].vec[2];
827 }
828 else {
829 td2d->loc2d = fcu->bezt[new_index].vec[0];
830 }
831 td = &tc->data[*trans_data_index];
832 update_trans_data(td, fcu, new_index, bezm.swap_handles);
833 }
834 if (const int *trans_data_index = trans_data_map.lookup_ptr(bezm.bezt->vec[2])) {
835 td2d = &tc->data_2d[*trans_data_index];
836 if (bezm.swap_handles) {
837 td2d->loc2d = fcu->bezt[new_index].vec[0];
838 }
839 else {
840 td2d->loc2d = fcu->bezt[new_index].vec[2];
841 }
842 td = &tc->data[*trans_data_index];
843 update_trans_data(td, fcu, new_index, bezm.swap_handles);
844 }
845 if (const int *trans_data_index = trans_data_map.lookup_ptr(bezm.bezt->vec[1])) {
846 td2d = &tc->data_2d[*trans_data_index];
847 td2d->loc2d = fcu->bezt[new_index].vec[1];
848
849 /* If only control point is selected, the handle pointers need to be updated as well. */
850 if (td2d->h1) {
851 td2d->h1 = fcu->bezt[new_index].vec[0];
852 }
853 if (td2d->h2) {
854 td2d->h2 = fcu->bezt[new_index].vec[2];
855 }
856 td = &tc->data[*trans_data_index];
857 update_trans_data(td, fcu, new_index, bezm.swap_handles);
858 }
859 }
860}
861
862/* This function is called by recalc_data during the Transform loop to recalculate
863 * the handles of curves and sort the keyframes so that the curves draw correctly.
864 * The Span of FCurves should only contain those that need sorting.
865 */
866static void remake_graph_transdata(TransInfo *t, const Span<FCurve *> fcurves)
867{
869 const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0;
870
872
873 /* Build a map from the data that is being modified to its index. This is used to quickly update
874 * the pointers to where the data ends up after sorting. */
875 Map<float *, int> trans_data_map;
876 for (int i = 0; i < tc->data_len; i++) {
877 trans_data_map.add(tc->data_2d[i].loc2d, i);
878 }
879
880 /* The grain size of 8 was chosen based on measured runtimes of this function. While 1 is the
881 * fastest, larger grain sizes are generally preferred and the difference between 1 and 8 was
882 * only minimal (~330ms to ~336ms). */
883 threading::parallel_for(fcurves.index_range(), 8, [&](const IndexRange range) {
884 for (const int i : range) {
885 FCurve *fcu = fcurves[i];
886
887 if (!fcu->bezt) {
888 continue;
889 }
890
891 /* Adjust transform-data pointers. */
892 /* NOTE: none of these functions use 'use_handle', it could be removed. */
893 Vector<BeztMap> bezms = bezt_to_beztmaps(fcu->bezt, fcu->totvert);
894 sort_time_beztmaps(bezms);
895 update_transdata_bezt_pointers(tc, trans_data_map, fcu, bezms);
896
897 /* Re-sort actual beztriples
898 * (perhaps this could be done using the beztmaps to save time?). */
899 sort_time_fcurve(fcu);
900
901 testhandles_fcurve(fcu, BEZT_FLAG_TEMP_TAG, use_handle);
902 }
903 });
904}
905
907{
909 ViewLayer *view_layer = t->view_layer;
910
911 ListBase anim_data = {nullptr, nullptr};
912 bAnimContext ac = {nullptr};
913 int filter;
914
916
917 /* Initialize relevant anim-context 'context' data from TransInfo data. */
918 /* NOTE: sync this with the code in #ANIM_animdata_get_context(). */
919 ac.bmain = CTX_data_main(t->context);
920 ac.scene = t->scene;
921 ac.view_layer = t->view_layer;
923 ac.area = t->area;
924 ac.region = t->region;
925 ac.sl = static_cast<SpaceLink *>((t->area) ? t->area->spacedata.first : nullptr);
926 ac.spacetype = eSpace_Type((t->area) ? t->area->spacetype : 0);
927 ac.regiontype = eRegion_Type((t->region) ? t->region->regiontype : 0);
928
930
931 /* Do the flush first. */
933
934 /* Get curves to check if a re-sort is needed. */
938 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
939
940 Vector<FCurve *> unsorted_fcurves;
941 /* Now test if there is a need to re-sort. */
942 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
943 FCurve *fcu = (FCurve *)ale->key_data;
944
945 /* Ignore FC-Curves without any selected verts. */
946 if (!fcu_test_selected(fcu)) {
947 continue;
948 }
949
950 /* Watch it: if the time is wrong: do not correct handles yet. */
951 if (test_time_fcurve(fcu)) {
952 unsorted_fcurves.append(fcu);
953 }
954 else {
956 }
957
958 /* Set refresh tags for objects using this animation,
959 * BUT only if realtime updates are enabled. */
960 if ((sipo->flag & SIPO_NOREALTIMEUPDATES) == 0) {
962 }
963 }
964
965 /* Do resort and other updates? */
966 if (!unsorted_fcurves.is_empty()) {
967 remake_graph_transdata(t, unsorted_fcurves);
968 }
969
970 /* Now free temp channels. */
971 ANIM_animdata_freelist(&anim_data);
972}
973
975
976/* -------------------------------------------------------------------- */
979
981{
983 bAnimContext ac;
984 const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0;
985
986 const bool canceled = (t->state == TRANS_CANCEL);
987 const bool duplicate = (t->flag & T_DUPLICATED_KEYFRAMES) != 0;
988
989 /* Initialize relevant anim-context 'context' data. */
990 if (ANIM_animdata_get_context(C, &ac) == 0) {
991 return;
992 }
993
994 if (ac.datatype) {
995 ListBase anim_data = {nullptr, nullptr};
998
999 /* Get channels to work on. */
1001 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
1002
1003 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1004 FCurve *fcu = (FCurve *)ale->key_data;
1005
1006 /* 3 cases here for curve cleanups:
1007 * 1) NOTRANSKEYCULL on -> cleanup of duplicates shouldn't be done.
1008 * 2) canceled == 0 -> user confirmed the transform,
1009 * so duplicates should be removed.
1010 * 3) canceled + duplicate -> user canceled the transform,
1011 * but we made duplicates, so get rid of these.
1012 */
1013 if ((sipo->flag & SIPO_NOTRANSKEYCULL) == 0 && ((canceled == 0) || (duplicate))) {
1014 ANIM_nla_mapping_apply_if_needed_fcurve(ale, fcu, false, false);
1016 ANIM_nla_mapping_apply_if_needed_fcurve(ale, fcu, true, false);
1017 }
1018 }
1019
1020 /* Free temp memory. */
1021 ANIM_animdata_freelist(&anim_data);
1022 }
1023
1024 /* Make sure all F-Curves are set correctly, but not if transform was
1025 * canceled, since then curves were already restored to initial state.
1026 * NOTE: if the refresh is really needed after cancel then some way
1027 * has to be added to not update handle types, see #22289.
1028 */
1029 if (!canceled) {
1031 }
1032}
1033
1035
1037 /*flags*/ (T_POINTS | T_2D_EDIT),
1038 /*create_trans_data*/ createTransGraphEditData,
1039 /*recalc_data*/ recalcData_graphedit,
1040 /*special_aftertrans_update*/ special_aftertrans_update__graph,
1041};
1042
1043} // namespace blender::ed::transform
Main * CTX_data_main(const bContext *C)
void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, bool use_handle)
bool test_time_fcurve(FCurve *fcu)
void BKE_fcurve_handles_recalc_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag)
void BKE_fcurve_merge_duplicate_keys(FCurve *fcu, const int sel_flag, const bool use_handle)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
@ NLATIME_CONVERT_MAP
Definition BKE_nla.hh:543
@ NLATIME_CONVERT_UNMAP
Definition BKE_nla.hh:540
float BKE_nla_tweakedit_remap(AnimData *adt, float cframe, eNlaTime_ConvertModes mode)
#define LISTBASE_FOREACH(type, var, list)
MINLINE float min_fff(float a, float b, float c)
void copy_m3_m3(float m1[3][3], const float m2[3][3])
void unit_m3(float m[3][3])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
unsigned int uint
#define ELEM(...)
#define IS_EQF(a, b)
float[3] Vector
@ FCURVE_INT_VALUES
@ HD_AUTO_ANIM
@ HD_AUTO
@ HD_ALIGN
@ BEZT_FLAG_TEMP_TAG
#define BEZT_ISSEL_ANY(bezt)
@ SCE_SNAP
eRegion_Type
eSpace_Type
@ SIPO_AUTOLOCK_AXIS
@ SIPO_NOREALTIMEUPDATES
@ SIPO_NOTRANSKEYCULL
@ SIPO_NOHANDLES
@ SIPO_RUNTIME_FLAG_TWEAK_HANDLES_RIGHT
@ SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT
@ V3D_AROUND_LOCAL_ORIGINS
eAnimCont_Types
eAnimFilter_Flags
@ ANIMFILTER_FOREDIT
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_CURVE_VISIBLE
@ ANIMFILTER_FCURVESONLY
@ ANIM_UNITCONV_ONLYSEL
@ ANIM_UNITCONV_SELVERTS
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
void UI_view2d_scale_get(const View2D *v2d, float *r_x, float *r_y)
Definition view2d.cc:1911
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:463
void ANIM_list_elem_update(Main *bmain, Scene *scene, bAnimListElem *ale)
Definition anim_deps.cc:52
short ANIM_get_normalization_flags(SpaceLink *space_link)
Definition anim_draw.cc:353
void ANIM_nla_mapping_apply_if_needed_fcurve(bAnimListElem *ale, FCurve *fcu, const bool restore, const bool only_keys)
Definition anim_draw.cc:339
bool ANIM_nla_mapping_allowed(const bAnimListElem *ale)
Definition anim_draw.cc:212
float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag, float *r_offset)
Definition anim_draw.cc:562
float ANIM_nla_tweakedit_remap(bAnimListElem *ale, const float cframe, const eNlaTime_ConvertModes mode)
Definition anim_draw.cc:262
bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
bool ANIM_animdata_context_getdata(bAnimContext *ac)
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
constexpr IndexRange drop_back(int64_t n) const
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
#define SELECT
#define floorf(x)
#define filter
int count
void ANIM_editkeyframes_refresh(bAnimContext *ac)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
ccl_device_inline float2 fabs(const float2 a)
static int left
TransConvertTypeInfo TransConvertType_Graph
char transform_convert_frame_side_dir_get(TransInfo *t, float cframe)
static void update_trans_data(TransData *td, const FCurve *fcu, const int new_index, const bool swap_handles)
static void remake_graph_transdata(TransInfo *t, const Span< FCurve * > fcurves)
static void special_aftertrans_update__graph(bContext *C, TransInfo *t)
TransDataCurveHandleFlags * initTransDataCurveHandles(TransData *td, BezTriple *bezt)
static void enable_autolock(TransInfo *t, SpaceGraph *space_graph)
static void graph_bezt_get_transform_selection(const TransInfo *t, const BezTriple *bezt, const bool use_handle, bool *r_left_handle, bool *r_key, bool *r_right_handle)
static void recalcData_graphedit(TransInfo *t)
bool FrameOnMouseSide(char side, float frame, float cframe)
void transform_snap_anim_flush_data(TransInfo *t, TransData *td, eSnapMode snap_mode, float *r_val_final)
static void bezt_to_transdata(TransData *td, TransData2D *td2d, TransDataGraph *tdg, bAnimListElem *ale, BezTriple *bezt, int bi, bool selected, bool ishandle, bool intvals, const float mtx[3][3], const float smtx[3][3], float unit_scale, float offset)
static float graph_key_shortest_dist(TransInfo *t, FCurve *fcu, TransData *td_start, TransData *td, int cfra, bool use_handle)
static void sort_time_beztmaps(const MutableSpan< BeztMap > bezms)
static bool fcu_test_selected(FCurve *fcu)
void initSelectConstraint(TransInfo *t)
static void update_transdata_bezt_pointers(TransDataContainer *tc, const Map< float *, int > &trans_data_map, const FCurve *fcu, const Span< BeztMap > bezms)
static bool graph_edit_is_translation_mode(TransInfo *t)
void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const float y_fac)
static Vector< BeztMap > bezt_to_beztmaps(BezTriple *bezts, const int totvert)
static bool graph_edit_use_local_center(TransInfo *t)
static void flushTransGraphData(TransInfo *t)
static void createTransGraphEditData(bContext *C, TransInfo *t)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
#define FLT_MAX
Definition stdcycles.h:14
float vec[3][3]
BezTriple * bezt
unsigned int totvert
void * first
struct RenderData r
ListBase spacedata
SpaceGraph_Runtime runtime
SpaceLink * sl
eAnimCont_Types datatype
eSpace_Type spacetype
ViewLayer * view_layer
ARegion * region
Object * obact
eRegion_Type regiontype
ScrArea * area
AnimData * adt
TransDataCurveHandleFlags * hdata
Definition transform.hh:514
i
Definition text_draw.cc:230
#define TRANS_DATA_CONTAINER_FIRST_SINGLE(t)
Definition transform.hh:39
conversion and adaptation of different datablocks to a common struct.