Blender V5.0
transform_convert_sequencer.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_screen_types.h"
10#include "DNA_sequence_types.h"
11#include "DNA_space_types.h"
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_listbase.h"
16#include "BLI_math_matrix.h"
17#include "BLI_math_vector.h"
18
19#include "BKE_context.hh"
20
21#include "ED_markers.hh"
22
23#include "SEQ_animation.hh"
24#include "SEQ_channels.hh"
25#include "SEQ_edit.hh"
26#include "SEQ_effects.hh"
27#include "SEQ_iterator.hh"
28#include "SEQ_relations.hh"
29#include "SEQ_sequencer.hh"
30#include "SEQ_time.hh"
31#include "SEQ_transform.hh"
32
33#include "UI_view2d.hh"
34
35#include "transform.hh"
36#include "transform_convert.hh"
37#include "transform_mode.hh"
38
39namespace blender::ed::transform {
40
41#define STRIP_EDGE_PAN_INSIDE_PAD 3.5
42#define STRIP_EDGE_PAN_OUTSIDE_PAD 0 /* Disable clamping for panning, use whole screen. */
43#define STRIP_EDGE_PAN_SPEED_RAMP 1
44#define STRIP_EDGE_PAN_MAX_SPEED 4 /* In UI units per second, slower than default. */
45#define STRIP_EDGE_PAN_DELAY 1.0f
46#define STRIP_EDGE_PAN_ZOOM_INFLUENCE 0.5f
47
48namespace {
49
51struct TransDataSeq {
52 Strip *strip;
54 int flag;
57 int start_offset;
59 short sel_flag;
60};
61
65struct TransSeq {
66 TransDataSeq *tdseq;
67 /* Maximum delta allowed along x and y before clamping selected strips/handles. Always active. */
68 rcti offset_clamp;
69 /* Maximum delta before clamping handles to the bounds of underlying content. May be disabled. */
70 int hold_clamp_min, hold_clamp_max;
71
72 /* Initial rect of the view2d, used for computing offset during edge panning. */
73 rctf initial_v2d_cur;
74 View2DEdgePanData edge_pan;
75
76 /* Strips that aren't selected, but their position entirely depends on transformed strips. */
77 VectorSet<Strip *> time_dependent_strips;
78};
79
80} // namespace
81
82/* -------------------------------------------------------------------- */
85
86/* This function applies the rules for transforming a strip so duplicate
87 * checks don't need to be added in multiple places.
88 *
89 * count and flag MUST be set.
90 */
91static void SeqTransInfo(TransInfo *t, Strip *strip, int *r_count, int *r_flag)
92{
94 Editing *ed = seq::editing_get(scene);
96
97 /* For extend we need to do some tricks. */
98 if (t->mode == TFM_TIME_EXTEND) {
99
100 /* *** Extend Transform *** */
101 int cfra = scene->r.cfra;
102 int left = seq::time_left_handle_frame_get(scene, strip);
103 int right = seq::time_right_handle_frame_get(scene, strip);
104
105 if ((strip->flag & SELECT) == 0 || seq::transform_is_locked(channels, strip)) {
106 *r_count = 0;
107 *r_flag = 0;
108 }
109 else {
110 *r_count = 1; /* Unless its set to 0, extend will never set 2 handles at once. */
111 *r_flag = (strip->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
112
113 if (t->frame_side == 'R') {
114 if (right <= cfra) {
115 *r_count = *r_flag = 0;
116 } /* Ignore. */
117 else if (left > cfra) {
118 } /* Keep the selection. */
119 else {
120 *r_flag |= SEQ_RIGHTSEL;
121 }
122 }
123 else {
124 if (left >= cfra) {
125 *r_count = *r_flag = 0;
126 } /* Ignore. */
127 else if (right < cfra) {
128 } /* Keep the selection. */
129 else {
130 *r_flag |= SEQ_LEFTSEL;
131 }
132 }
133 }
134 }
135 else {
136
137 t->frame_side = 'B';
138
139 /* *** Normal Transform *** */
140
141 /* Count. */
142
143 /* Non nested strips (reset selection and handles). */
144 if ((strip->flag & SELECT) == 0 || seq::transform_is_locked(channels, strip)) {
145 *r_count = 0;
146 *r_flag = 0;
147 }
148 else {
149 if ((strip->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) == (SEQ_LEFTSEL | SEQ_RIGHTSEL)) {
150 *r_flag = strip->flag;
151 *r_count = 2; /* We need 2 transdata's. */
152 }
153 else {
154 *r_flag = strip->flag;
155 *r_count = 1; /* Selected or with a handle selected. */
156 }
157 }
158 }
159}
160
161static int SeqTransCount(TransInfo *t, ListBase *seqbase)
162{
163 int tot = 0, count, flag;
164
165 LISTBASE_FOREACH (Strip *, strip, seqbase) {
166 SeqTransInfo(t, strip, &count, &flag); /* Ignore the flag. */
167 tot += count;
168 }
169
170 return tot;
171}
172
174 TransData *td,
175 TransData2D *td2d,
176 TransDataSeq *tdsq,
177 Strip *strip,
178 int flag,
179 int sel_flag)
180{
181 int start_left;
182
183 switch (sel_flag) {
184 case SELECT:
185 /* Use seq_tx_get_final_left() and an offset here
186 * so transform has the left hand location of the strip.
187 * `tdsq->start_offset` is used when flushing the tx data back. */
188 start_left = seq::time_left_handle_frame_get(scene, strip);
189 td2d->loc[0] = start_left;
190 tdsq->start_offset = start_left - strip->start; /* Use to apply the original location. */
191 break;
192 case SEQ_LEFTSEL:
193 start_left = seq::time_left_handle_frame_get(scene, strip);
194 td2d->loc[0] = start_left;
195 break;
196 case SEQ_RIGHTSEL:
197 td2d->loc[0] = seq::time_right_handle_frame_get(scene, strip);
198 break;
199 }
200
201 td2d->loc[1] = strip->channel; /* Channel - Y location. */
202 td2d->loc[2] = 0.0f;
203 td2d->loc2d = nullptr;
204
205 tdsq->strip = strip;
206
207 /* Use instead of strip->flag for nested strips and other
208 * cases where the selection may need to be modified. */
209 tdsq->flag = flag;
210 tdsq->sel_flag = sel_flag;
211
212 td->extra = (void *)tdsq; /* Allow us to update the strip from here. */
213
214 td->flag = 0;
215 td->loc = td2d->loc;
216 copy_v3_v3(td->center, td->loc);
217 copy_v3_v3(td->iloc, td->loc);
218
219 memset(td->axismtx, 0, sizeof(td->axismtx));
220 td->axismtx[2][2] = 1.0f;
221
222 td->val = nullptr;
223
224 td->flag |= TD_SELECTED;
225 td->dist = 0.0;
226
227 unit_m3(td->mtx);
228 unit_m3(td->smtx);
229
230 /* Time Transform (extend). */
231 td->val = td2d->loc;
232 td->ival = td2d->loc[0];
233
234 return td;
235}
236
238 TransInfo *t, ListBase *seqbase, TransData *td, TransData2D *td2d, TransDataSeq *tdsq)
239{
241 int count, flag;
242 int tot = 0;
243
244 LISTBASE_FOREACH (Strip *, strip, seqbase) {
245
246 SeqTransInfo(t, strip, &count, &flag);
247
248 /* Use 'flag' which is derived from strip->flag but modified for special cases. */
249 if (flag & SELECT) {
250 if (flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) {
251 if (flag & SEQ_LEFTSEL) {
252 SeqToTransData(scene, td++, td2d++, tdsq++, strip, flag, SEQ_LEFTSEL);
253 tot++;
254 }
255 if (flag & SEQ_RIGHTSEL) {
256 SeqToTransData(scene, td++, td2d++, tdsq++, strip, flag, SEQ_RIGHTSEL);
257 tot++;
258 }
259 }
260 else {
261 SeqToTransData(scene, td++, td2d++, tdsq++, strip, flag, SELECT);
262 tot++;
263 }
264 }
265 }
266 return tot;
267}
268
270{
271 if ((custom_data->data != nullptr) && custom_data->use_free) {
272 TransSeq *ts = static_cast<TransSeq *>(custom_data->data);
273 MEM_freeN(ts->tdseq);
274 MEM_delete(ts);
275 custom_data->data = nullptr;
276 }
277}
278
279/* Canceled, need to update the strips display. */
280static void seq_transform_cancel(TransInfo *t, Span<Strip *> transformed_strips)
281{
284
285 if (t->remove_on_cancel) {
286 for (Strip *strip : transformed_strips) {
287 seq::edit_flag_for_removal(scene, seqbase, strip);
288 }
289 seq::edit_remove_flagged_strips(scene, seqbase);
290 return;
291 }
292
293 for (Strip *strip : transformed_strips) {
294 /* Handle pre-existing overlapping strips even when operator is canceled.
295 * This is necessary for #SEQUENCER_OT_duplicate_move macro for example. */
296 if (seq::transform_test_overlap(scene, seqbase, strip)) {
297 seq::transform_seqbase_shuffle(seqbase, strip, scene);
298 }
299 }
300}
301
303{
305 Editing *ed = seq::editing_get(scene);
307}
308
310{
311 for (Strip *strip : transformed_strips) {
312 if (strip->runtime.flag & STRIP_OVERLAP) {
313 return true;
314 }
315 }
316 return false;
317}
318
320{
321 VectorSet<Strip *> strips;
322 TransData *td = tc->data;
323 for (int a = 0; a < tc->data_len; a++, td++) {
324 Strip *strip = ((TransDataSeq *)td->extra)->strip;
325 strips.add(strip);
326 }
327 return strips;
328}
329
330static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data)
331{
333 Editing *ed = seq::editing_get(scene);
334 if (ed == nullptr) {
335 free_transform_custom_data(custom_data);
336 return;
337 }
338
339 VectorSet transformed_strips = seq_transform_collection_from_transdata(tc);
341 scene, seqbase_active_get(t), transformed_strips, seq::query_strip_effect_chain);
342
343 for (Strip *strip : transformed_strips) {
344 strip->runtime.flag &= ~(STRIP_CLAMPED_LH | STRIP_CLAMPED_RH);
345 strip->runtime.flag &= ~STRIP_IGNORE_CHANNEL_LOCK;
346 }
347
348 if (t->state == TRANS_CANCEL) {
349 seq_transform_cancel(t, transformed_strips);
350 free_transform_custom_data(custom_data);
351 return;
352 }
353
354 TransSeq *ts = static_cast<TransSeq *>(tc->custom.type.data);
355 ListBase *seqbasep = seqbase_active_get(t);
356 const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag &
357 SEQ_MARKER_TRANS) != 0;
358 if (seq_transform_check_overlap(transformed_strips)) {
360 scene, seqbasep, transformed_strips, ts->time_dependent_strips, use_sync_markers);
361 }
362
364 free_transform_custom_data(custom_data);
365}
366
368{
369 VectorSet<Strip *> strips;
370 LISTBASE_FOREACH (Strip *, strip, seqbase) {
371 if ((strip->flag & SELECT) != 0 && ((strip->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) == 0)) {
372 strips.add(strip);
373 }
374 }
375 return strips;
376}
377
382
383static Strip *effect_input_get(const Scene *scene, Strip *effect, SeqInputSide side)
384{
385 Strip *input = effect->input1;
386 if (effect->input2 && (seq::time_left_handle_frame_get(scene, effect->input2) -
387 seq::time_left_handle_frame_get(scene, effect->input1)) *
388 side >
389 0)
390 {
391 input = effect->input2;
392 }
393 return input;
394}
395
396static Strip *effect_base_input_get(const Scene *scene, Strip *effect, SeqInputSide side)
397{
398 Strip *input = effect, *strip_iter = effect;
399 while (strip_iter != nullptr) {
400 input = strip_iter;
401 strip_iter = effect_input_get(scene, strip_iter, side);
402 }
403 return input;
404}
405
411 VectorSet<Strip *> &time_dependent_strips)
412{
414 ListBase *seqbase = seqbase_active_get(t);
415
416 /* Query dependent strips where used strips do not have handles selected.
417 * If all inputs of any effect even indirectly(through another effect) points to selected strip,
418 * its position will change. */
419
420 VectorSet<Strip *> strips_no_handles = query_selected_strips_no_handles(seqbase);
421 time_dependent_strips.add_multiple(strips_no_handles);
422
423 seq::iterator_set_expand(scene, seqbase, strips_no_handles, seq::query_strip_effect_chain);
424 bool strip_added = true;
425
426 while (strip_added) {
427 strip_added = false;
428
429 for (Strip *strip : strips_no_handles) {
430 if (time_dependent_strips.contains(strip)) {
431 continue; /* Strip is already in collection, skip it. */
432 }
433
434 /* If both input1 and input2 exist, both must be selected. */
435 if (strip->input1 && time_dependent_strips.contains(strip->input1)) {
436 if (strip->input2 && !time_dependent_strips.contains(strip->input2)) {
437 continue;
438 }
439 strip_added = true;
440 time_dependent_strips.add(strip);
441 }
442 }
443 }
444
445 /* Query dependent strips where used strips do have handles selected.
446 * If any 2-input effect changes position because handles were moved, animation should be offset.
447 * With single input effect, it is less likely desirable to move animation. */
448
449 VectorSet selected_strips = seq::query_selected_strips(seqbase);
450 seq::iterator_set_expand(scene, seqbase, selected_strips, seq::query_strip_effect_chain);
451 for (Strip *strip : selected_strips) {
452 /* Check only 2 input effects. */
453 if (strip->input1 == nullptr || strip->input2 == nullptr) {
454 continue;
455 }
456
457 /* Find immediate base inputs(left and right side). */
458 Strip *input_left = effect_base_input_get(scene, strip, SEQ_INPUT_LEFT);
459 Strip *input_right = effect_base_input_get(scene, strip, SEQ_INPUT_RIGHT);
460
461 if ((input_left->flag & SEQ_RIGHTSEL) != 0 && (input_right->flag & SEQ_LEFTSEL) != 0) {
462 time_dependent_strips.add(strip);
463 }
464 }
465
466 /* Remove all non-effects. */
467 time_dependent_strips.remove_if(
468 [&](Strip *strip) { return seq::transform_strip_can_be_translated(strip); });
469}
470
471static void create_trans_seq_clamp_data(TransInfo *t, const Scene *scene)
472{
473 TransSeq *ts = (TransSeq *)TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data;
474 const Editing *ed = seq::editing_get(scene);
475
476 bool only_handles_selected = true;
477
478 /* Prevent snaps and change in `values` past `offset_clamp` for all selected strips. */
479 BLI_rcti_init(&ts->offset_clamp, -INT_MAX, INT_MAX, -seq::MAX_CHANNELS, seq::MAX_CHANNELS);
480
482 for (Strip *strip : strips) {
483 if (!strip->is_effect() || seq::effect_get_num_inputs(strip->type) == 0) {
484 continue;
485 }
486 /* If there is an effect strip with no inputs selected, prevent any x-direction movement,
487 * since these strips are tied to their inputs and can only move up and down. */
488 if (!(strip->input1->flag & SELECT) && (!strip->input2 || !(strip->input2->flag & SELECT))) {
489 ts->offset_clamp.xmin = 0;
490 ts->offset_clamp.xmax = 0;
491 }
492 }
493
494 /* Try to clamp handles by default. */
496 ts->hold_clamp_min = -INT_MAX;
497 ts->hold_clamp_max = INT_MAX;
498 for (Strip *strip : strips) {
500 continue;
501 }
502
503 bool left_sel = (strip->flag & SEQ_LEFTSEL);
504 bool right_sel = (strip->flag & SEQ_RIGHTSEL);
505
506 /* If any strips start out with hold offsets visible, disable handle clamping on init. */
507 if ((strip->startofs < 0 || strip->endofs < 0) && !seq::transform_single_image_check(strip)) {
509 }
510
511 /* If both handles are selected, there must be enough underlying content to clamp holds. */
512 bool can_clamp_holds = !(left_sel && right_sel) ||
513 (strip->len >= seq::time_right_handle_frame_get(scene, strip) -
514 seq::time_left_handle_frame_get(scene, strip));
515 can_clamp_holds &= !seq::transform_single_image_check(strip);
516
517 /* A handle is selected. Update x-axis clamping data. */
518 if (left_sel || right_sel) {
519 if (left_sel) {
520 /* Ensure that this strip's left handle cannot pass its right handle. */
521 if (!(left_sel && right_sel)) {
522 int offset = (seq::time_right_handle_frame_get(scene, strip) - 1) -
524 ts->offset_clamp.xmax = min_ii(ts->offset_clamp.xmax, offset);
525 }
526
527 if (can_clamp_holds) {
528 /* Ensure that the left handle's frame is greater than or equal to the content start. */
529 ts->hold_clamp_min = max_ii(ts->hold_clamp_min, -strip->startofs);
530 }
531 }
532 if (right_sel) {
533 if (!(left_sel && right_sel)) {
534 /* Ensure that this strip's right handle cannot pass its left handle. */
535 int offset = (seq::time_left_handle_frame_get(scene, strip) + 1) -
537 ts->offset_clamp.xmin = max_ii(ts->offset_clamp.xmin, offset);
538 }
539
540 if (can_clamp_holds) {
541 /* Ensure that the right handle's frame is less than or equal to the content end. */
542 ts->hold_clamp_max = min_ii(ts->hold_clamp_max, strip->endofs);
543 }
544 }
545 }
546 /* No handles are selected. Update y-axis channel clamping data. */
547 else {
548 ts->offset_clamp.ymin = max_ii(ts->offset_clamp.ymin, 1 - strip->channel);
549 ts->offset_clamp.ymax = min_ii(ts->offset_clamp.ymax, seq::MAX_CHANNELS - strip->channel);
550 only_handles_selected = false;
551 }
552 }
553
554 /* TODO(john): This ensures that y-axis movement is restricted only if all of the selected items
555 * are handles, since currently it is possible to select whole strips and handles at the same
556 * time. This should be removed for 5.0 when we make this behavior impossible. */
557 if (only_handles_selected) {
558 ts->offset_clamp.ymin = 0;
559 ts->offset_clamp.ymax = 0;
560 }
561}
562
564{
566 if (!scene) {
567 return;
568 }
569 Editing *ed = seq::editing_get(scene);
570 TransData *td = nullptr;
571 TransData2D *td2d = nullptr;
572 TransDataSeq *tdsq = nullptr;
573 TransSeq *ts = nullptr;
574
575 int count = 0;
576
578
579 if (ed == nullptr) {
580 tc->data_len = 0;
581 return;
582 }
583
584 /* Disable cursor wrapping for edge pan. */
585 if (t->mode == TFM_TRANSLATION) {
587 }
588
591
592 count = SeqTransCount(t, ed->current_strips());
593
594 /* Allocate memory for data. */
595 tc->data_len = count;
596
597 /* Stop if trying to build list if nothing selected. */
598 if (count == 0) {
599 return;
600 }
601
602 tc->custom.type.data = ts = MEM_new<TransSeq>(__func__);
603 tc->custom.type.use_free = true;
604 td = tc->data = MEM_calloc_arrayN<TransData>(tc->data_len, "TransSeq TransData");
605 td2d = tc->data_2d = MEM_calloc_arrayN<TransData2D>(tc->data_len, "TransSeq TransData2D");
606 ts->tdseq = tdsq = MEM_calloc_arrayN<TransDataSeq>(tc->data_len, "TransSeq TransDataSeq");
607
608 /* Custom data to enable edge panning during transformation. */
610 &ts->edge_pan,
618 ts->initial_v2d_cur = t->region->v2d.cur;
619
620 /* Loop 2: build transdata array. */
621 SeqToTransData_build(t, ed->current_strips(), td, td2d, tdsq);
622
624
625 query_time_dependent_strips_strips(t, ts->time_dependent_strips);
626}
627
629
630/* -------------------------------------------------------------------- */
633
634static void view2d_edge_pan_loc_compensate(TransInfo *t, float r_offset[2])
635{
636 TransSeq *ts = (TransSeq *)TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data;
637
638 const rctf rect_prev = t->region->v2d.cur;
639
640 if (t->options & CTX_VIEW2D_EDGE_PAN) {
641 if (t->state == TRANS_CANCEL) {
642 UI_view2d_edge_pan_cancel(t->context, &ts->edge_pan);
643 }
644 else {
645 /* Edge panning functions expect window coordinates, mval is relative to region. */
646 const int xy[2] = {
647 t->region->winrct.xmin + int(t->mval[0]),
648 t->region->winrct.ymin + int(t->mval[1]),
649 };
650 UI_view2d_edge_pan_apply(t->context, &ts->edge_pan, xy);
651 }
652 }
653
654 if (t->state != TRANS_CANCEL) {
655 if (!BLI_rctf_compare(&rect_prev, &t->region->v2d.cur, FLT_EPSILON)) {
656 /* Additional offset due to change in view2D rect. */
657 BLI_rctf_transform_pt_v(&t->region->v2d.cur, &rect_prev, r_offset, r_offset);
659 }
660 }
661}
662
664{
665 /* Editing null check already done. */
666 ListBase *seqbasep = seqbase_active_get(t);
668
670 TransData *td = tc->data;
671 TransData2D *td2d = tc->data_2d;
672
673 /* This is calculated for offsetting animation of effects that change position with inputs.
674 * Maximum(positive or negative) value is used, because individual strips can be clamped. This
675 * works fairly well in most scenarios, but there can be some edge cases.
676 *
677 * Better solution would be to store effect position and calculate real offset. However with many
678 * (>5) effects in chain, there is visible lag in strip position update, because during
679 * recalculation, hierarchy is not taken into account. */
680 int max_offset = 0;
681
682 float edge_pan_offset[2] = {0.0f, 0.0f};
683 view2d_edge_pan_loc_compensate(t, edge_pan_offset);
684
685 /* Flush to 2D vector from internally used 3D vector. */
686 for (int a = 0; a < tc->data_len; a++, td++, td2d++) {
687 TransDataSeq *tdsq = (TransDataSeq *)td->extra;
688 Strip *strip = tdsq->strip;
689
690 /* Apply extra offset caused by edge panning. */
691 add_v2_v2(td->loc, edge_pan_offset);
692
693 float offset[2];
694 float offset_clamped[2];
695 sub_v2_v2v2(offset, td->loc, td->iloc);
696 copy_v2_v2(offset_clamped, offset);
697
698 if (t->state != TRANS_CANCEL) {
699 transform_convert_sequencer_clamp(t, offset_clamped);
700 }
701 const int new_frame = round_fl_to_int(td->iloc[0] + offset_clamped[0]);
702 const int new_channel = round_fl_to_int(td->iloc[1] + offset_clamped[1]);
703
704 /* Compute handle clamping state to be drawn. */
705 if (tdsq->sel_flag & SEQ_LEFTSEL) {
707 }
708 if (tdsq->sel_flag & SEQ_RIGHTSEL) {
710 }
711 if (!seq::transform_single_image_check(strip) && !strip->is_effect()) {
712 if (offset_clamped[0] > offset[0] && new_frame == seq::time_start_frame_get(strip)) {
713 strip->runtime.flag |= STRIP_CLAMPED_LH;
714 }
715 else if (offset_clamped[0] < offset[0] &&
716 new_frame == seq::time_content_end_frame_get(scene, strip))
717 {
718 strip->runtime.flag |= STRIP_CLAMPED_RH;
719 }
720 }
721
722 switch (tdsq->sel_flag) {
723 case SELECT: {
724 int offset = new_frame - tdsq->start_offset - strip->start;
726 seq::transform_translate_strip(scene, strip, offset);
727 if (abs(offset) > abs(max_offset)) {
728 max_offset = offset;
729 }
730 }
731 seq::strip_channel_set(strip, new_channel);
732 break;
733 }
734 case SEQ_LEFTSEL: { /* No vertical transform. */
735 /* Update right handle first if both handles are selected and the `new_frame` is right of
736 * the old one to avoid unexpected left handle clamping when canceling. See #126191. */
737 const bool both_handles_selected = (tdsq->flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) ==
739 if (both_handles_selected && new_frame >= seq::time_right_handle_frame_get(scene, strip)) {
740 /* For now, move the right handle far enough to avoid the left handle getting clamped.
741 * The final, correct position will be calculated later. */
742 seq::time_right_handle_frame_set(scene, strip, new_frame + 1);
743 }
744
745 int old_startdisp = seq::time_left_handle_frame_get(scene, strip);
746 seq::time_left_handle_frame_set(scene, strip, new_frame);
747
748 if (abs(seq::time_left_handle_frame_get(scene, strip) - old_startdisp) > abs(max_offset)) {
749 max_offset = seq::time_left_handle_frame_get(scene, strip) - old_startdisp;
750 }
751 break;
752 }
753 case SEQ_RIGHTSEL: { /* No vertical transform. */
754 int old_enddisp = seq::time_right_handle_frame_get(scene, strip);
755 seq::time_right_handle_frame_set(scene, strip, new_frame);
756
757 if (abs(seq::time_right_handle_frame_get(scene, strip) - old_enddisp) > abs(max_offset)) {
758 max_offset = seq::time_right_handle_frame_get(scene, strip) - old_enddisp;
759 }
760 break;
761 }
762 }
763 }
764
765 TransSeq *ts = (TransSeq *)TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data;
766
767 /* Update animation for effects. */
768 for (Strip *strip : ts->time_dependent_strips) {
769 seq::offset_animdata(scene, strip, max_offset);
770 }
771
772 /* Need to do the overlap check in a new loop otherwise adjacent strips
773 * will not be updated and we'll get false positives. */
774 VectorSet transformed_strips = seq_transform_collection_from_transdata(tc);
776 scene, seqbase_active_get(t), transformed_strips, seq::query_strip_effect_chain);
777
778 for (Strip *strip : transformed_strips) {
779 /* Test overlap, displays red outline. */
780 strip->runtime.flag &= ~STRIP_OVERLAP;
781 if (seq::transform_test_overlap(scene, seqbasep, strip)) {
782 strip->runtime.flag |= STRIP_OVERLAP;
783 }
784 }
785}
786
788{
789 TransData *td;
790 int a;
791 Strip *strip_prev = nullptr;
792
795
796 for (a = 0, td = tc->data; a < tc->data_len; a++, td++) {
797 TransDataSeq *tdsq = (TransDataSeq *)td->extra;
798 Strip *strip = tdsq->strip;
799
800 if (strip != strip_prev) {
802 }
803
804 strip_prev = strip;
805 }
806
808
809 flushTransSeq(t);
810}
811
813
814/* -------------------------------------------------------------------- */
817
819{
821 SpaceSeq *sseq = (SpaceSeq *)t->area->spacedata.first;
822 if ((sseq->flag & SPACE_SEQ_DESELECT_STRIP_HANDLE) != 0 &&
824 {
827 for (Strip *strip : strips) {
828 strip->flag &= ~(SEQ_LEFTSEL | SEQ_RIGHTSEL);
829 }
830 }
831
833
834 /* #freeSeqData in `transform_conversions.cc` does this
835 * keep here so the `else` at the end won't run. */
836 if (t->state == TRANS_CANCEL) {
837 return;
838 }
839
840 /* Marker transform, not especially nice but we may want to move markers
841 * at the same time as strips in the Video Sequencer. */
842 if (sseq->flag & SEQ_MARKER_TRANS) {
843 /* Can't use #TFM_TIME_EXTEND
844 * for some reason EXTEND is changed into TRANSLATE, so use frame_side instead. */
845
846 if (t->mode == TFM_SEQ_SLIDE) {
847 if (t->frame_side == 'B') {
849 &scene->markers, scene, TFM_TIME_TRANSLATE, t->values_final[0], t->frame_side);
850 }
851 }
852 else if (ELEM(t->frame_side, 'L', 'R')) {
854 &scene->markers, scene, TFM_TIME_EXTEND, t->values_final[0], t->frame_side);
855 }
856 }
857}
858
859bool transform_convert_sequencer_clamp(const TransInfo *t, float r_val[2])
860{
861 const TransSeq *ts = (TransSeq *)TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data;
862 int val[2] = {round_fl_to_int(r_val[0]), round_fl_to_int(r_val[1])};
863 bool clamped = false;
864
865 /* Unconditional channel, retiming key, and handle clamping. Should never be ignored. */
866 if (BLI_rcti_clamp_pt_v(&ts->offset_clamp, val)) {
867 r_val[0] = float(val[0]);
868 r_val[1] = float(val[1]);
869 clamped = true;
870 }
871
872 /* Optional clamping of handles to underlying holds. Can be disabled by the user. */
874 if (val[0] < ts->hold_clamp_min) {
875 r_val[0] = float(ts->hold_clamp_min);
876 clamped = true;
877 }
878 else if (val[0] > ts->hold_clamp_max) {
879 r_val[0] = float(ts->hold_clamp_max);
880 clamped = true;
881 }
882 }
883
884 return clamped;
885}
886
888
890 /*flags*/ (T_POINTS | T_2D_EDIT),
891 /*create_trans_data*/ createTransSeqData,
892 /*recalc_data*/ recalcData_sequencer,
893 /*special_aftertrans_update*/ special_aftertrans_update__sequencer,
894};
895
896} // namespace blender::ed::transform
Scene * CTX_data_sequencer_scene(const bContext *C)
#define LISTBASE_FOREACH(type, var, list)
MINLINE int round_fl_to_int(float a)
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
void unit_m3(float m[3][3])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
void BLI_rctf_transform_pt_v(const rctf *dst, const rctf *src, float xy_dst[2], const float xy_src[2])
Definition rct.cc:526
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.cc:414
bool BLI_rcti_clamp_pt_v(const struct rcti *rect, int xy[2])
bool BLI_rctf_compare(const struct rctf *rect_a, const struct rctf *rect_b, float limit)
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SEQUENCER_STRIPS
Definition DNA_ID.h:1122
@ STRIP_IGNORE_CHANNEL_LOCK
@ STRIP_OVERLAP
@ STRIP_CLAMPED_LH
@ STRIP_CLAMPED_RH
@ SEQ_RIGHTSEL
@ SEQ_LEFTSEL
@ SPACE_SEQ_DESELECT_STRIP_HANDLE
@ SEQ_MARKER_TRANS
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
void UI_view2d_edge_pan_set_limits(View2DEdgePanData *vpd, float xmin, float xmax, float ymin, float ymax)
void UI_view2d_edge_pan_cancel(bContext *C, View2DEdgePanData *vpd)
void UI_view2d_edge_pan_init(bContext *C, View2DEdgePanData *vpd, float inside_pad, float outside_pad, float speed_ramp, float max_speed, float delay, float zoom_influence)
void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, const int xy[2]) ATTR_NONNULL(1
int ED_markers_post_apply_transform(ListBase *markers, Scene *scene, int mode, float value, char side)
bool add(const Key &key)
void add_multiple(Span< Key > keys)
bool contains(const Key &key) const
int64_t remove_if(Predicate &&predicate)
nullptr float
#define SELECT
#define input
#define abs
int count
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static int left
static void free_transform_custom_data(TransCustomData *custom_data)
TransConvertTypeInfo TransConvertType_Sequencer
static void query_time_dependent_strips_strips(TransInfo *t, VectorSet< Strip * > &time_dependent_strips)
static void special_aftertrans_update__sequencer(bContext *C, TransInfo *t)
static void flushTransSeq(TransInfo *t)
char transform_convert_frame_side_dir_get(TransInfo *t, float cframe)
static void view2d_edge_pan_loc_compensate(TransInfo *t, float r_offset[2])
static TransData * SeqToTransData(Scene *scene, TransData *td, TransData2D *td2d, TransDataSeq *tdsq, Strip *strip, int flag, int sel_flag)
static void seq_transform_cancel(TransInfo *t, Span< Strip * > transformed_strips)
static Strip * effect_base_input_get(const Scene *scene, Strip *effect, SeqInputSide side)
bool transform_mode_edge_seq_slide_use_restore_handle_selection(const TransInfo *t)
static VectorSet< Strip * > seq_transform_collection_from_transdata(TransDataContainer *tc)
bool transform_convert_sequencer_clamp(const TransInfo *t, float r_val[2])
static void SeqTransInfo(TransInfo *t, Strip *strip, int *r_count, int *r_flag)
void transformViewUpdate(TransInfo *t)
static void createTransSeqData(bContext *C, TransInfo *t)
static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data)
static void recalcData_sequencer(TransInfo *t)
static void create_trans_seq_clamp_data(TransInfo *t, const Scene *scene)
static VectorSet< Strip * > query_selected_strips_no_handles(ListBase *seqbase)
static int SeqTransCount(TransInfo *t, ListBase *seqbase)
static Strip * effect_input_get(const Scene *scene, Strip *effect, SeqInputSide side)
static int SeqToTransData_build(TransInfo *t, ListBase *seqbase, TransData *td, TransData2D *td2d, TransDataSeq *tdsq)
bool seq_transform_check_overlap(Span< Strip * > transformed_strips)
static ListBase * seqbase_active_get(const TransInfo *t)
int time_right_handle_frame_get(const Scene *scene, const Strip *strip)
void relations_invalidate_cache(Scene *scene, Strip *strip)
ListBase * channels_displayed_get(const Editing *ed)
Definition channels.cc:28
void edit_remove_flagged_strips(Scene *scene, ListBase *seqbase)
float time_content_end_frame_get(const Scene *scene, const Strip *strip)
void edit_flag_for_removal(Scene *scene, ListBase *seqbase, Strip *strip)
VectorSet< Strip * > query_selected_strips(ListBase *seqbase)
Definition iterator.cc:152
void transform_translate_strip(Scene *evil_scene, Strip *strip, int delta)
void strip_channel_set(Strip *strip, int channel)
bool transform_test_overlap(const Scene *scene, Strip *strip1, Strip *strip2)
Editing * editing_get(const Scene *scene)
Definition sequencer.cc:286
int time_left_handle_frame_get(const Scene *, const Strip *strip)
void offset_animdata(const Scene *scene, Strip *strip, float ofs)
Definition animation.cc:42
float time_start_frame_get(const Strip *strip)
void query_strip_effect_chain(const Scene *scene, Strip *reference_strip, ListBase *seqbase, VectorSet< Strip * > &r_strips)
Definition iterator.cc:254
bool transform_single_image_check(const Strip *strip)
constexpr int MAX_CHANNELS
void iterator_set_expand(const Scene *scene, ListBase *seqbase, VectorSet< Strip * > &strips, void strip_query_func(const Scene *scene, Strip *strip_reference, ListBase *seqbase, VectorSet< Strip * > &strips))
Definition iterator.cc:82
bool transform_is_locked(ListBase *channels, const Strip *strip)
void time_left_handle_frame_set(const Scene *scene, Strip *strip, int timeline_frame)
void transform_handle_overlap(Scene *scene, ListBase *seqbasep, blender::Span< Strip * > transformed_strips, bool use_sync_markers)
ListBase * active_seqbase_get(const Editing *ed)
Definition sequencer.cc:433
void time_right_handle_frame_set(const Scene *scene, Strip *strip, int timeline_frame)
bool transform_strip_can_be_translated(const Strip *strip)
bool transform_seqbase_shuffle(ListBase *seqbasep, Strip *test, Scene *evil_scene)
int effect_get_num_inputs(int strip_type)
Definition effects.cc:327
#define FLT_MAX
Definition stdcycles.h:14
void * first
struct RenderData r
ListBase markers
ListBase spacedata
struct Strip * input1
StripRuntime runtime
struct Strip * input2
void(* free_cb)(TransInfo *, TransDataContainer *tc, TransCustomData *custom_data)
Definition transform.hh:633
int ymin
int xmin
#define TRANS_DATA_CONTAINER_FIRST_SINGLE(t)
Definition transform.hh:39
conversion and adaptation of different datablocks to a common struct.
#define STRIP_EDGE_PAN_OUTSIDE_PAD
#define STRIP_EDGE_PAN_SPEED_RAMP
#define STRIP_EDGE_PAN_INSIDE_PAD
#define STRIP_EDGE_PAN_MAX_SPEED
#define STRIP_EDGE_PAN_DELAY
#define STRIP_EDGE_PAN_ZOOM_INFLUENCE
transform modes used by different operators.
int xy[2]
Definition wm_draw.cc:178
uint8_t flag
Definition wm_window.cc:145