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