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