Blender V5.0
transform_convert_sequencer_retiming.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include "DNA_screen_types.h"
12#include "DNA_sequence_types.h"
13#include "DNA_space_types.h"
14
15#include "BLI_math_matrix.h"
16#include "BLI_math_vector.h"
17#include "BLI_rect.h"
18
19#include "BKE_context.hh"
20
21#include "SEQ_iterator.hh"
22#include "SEQ_relations.hh"
23#include "SEQ_retiming.hh"
24#include "SEQ_sequencer.hh"
25#include "SEQ_transform.hh"
26
27#include "transform.hh"
28#include "transform_convert.hh"
29
30namespace blender::ed::transform {
31
33struct TransDataSeq {
36 int key_index; /* Some actions may need to destroy original data, use index to access it. */
37};
38
39struct TransSeq {
40 TransDataSeq *tdseq;
41 /* Maximum delta allowed before clamping selected retiming keys. Always active. */
43};
44
45static TransData *SeqToTransData(const Scene *scene,
46 Strip *strip,
47 const SeqRetimingKey *key,
48 TransData *td,
49 TransData2D *td2d,
50 TransDataSeq *tdseq)
51{
52
53 td2d->loc[0] = seq::retiming_key_timeline_frame_get(scene, strip, key);
54 td2d->loc[1] = key->retiming_factor;
55 td2d->loc2d = nullptr;
56 td->loc = td2d->loc;
57 copy_v3_v3(td->iloc, td->loc);
58 copy_v3_v3(td->center, td->loc);
59 memset(td->axismtx, 0, sizeof(td->axismtx));
60 td->axismtx[2][2] = 1.0f;
61 unit_m3(td->mtx);
62 unit_m3(td->smtx);
63
64 tdseq->strip = strip;
65 tdseq->orig_timeline_frame = seq::retiming_key_timeline_frame_get(scene, strip, key);
66 tdseq->key_index = seq::retiming_key_index_get(strip, key);
67
68 td->extra = static_cast<void *>(tdseq);
69 td->flag |= TD_SELECTED;
70 td->dist = 0.0;
71
72 return td;
73}
74
75static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data)
76{
77 const TransData *const td = tc->data;
78 Scene *scene = t->scene;
79 const Editing *ed = seq::editing_get(t->scene);
80
81 /* Handle overlapping strips. */
82
83 VectorSet<Strip *> transformed_strips;
84 for (int i = 0; i < tc->data_len; i++) {
85 Strip *strip = ((TransDataSeq *)(td + i)->extra)->strip;
86 transformed_strips.add(strip);
87 }
88
90 seq::iterator_set_expand(scene, seqbasep, transformed_strips, seq::query_strip_effect_chain);
91
92 VectorSet<Strip *> dependant;
93 dependant.add_multiple(transformed_strips);
94 dependant.remove_if([&](Strip *strip) { return seq::transform_strip_can_be_translated(strip); });
95
96 if (seq_transform_check_overlap(transformed_strips)) {
97 const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag &
98 SEQ_MARKER_TRANS) != 0;
100 scene, seqbasep, transformed_strips, dependant, use_sync_markers);
101 }
102
103 if ((custom_data->data != nullptr) && custom_data->use_free) {
104 TransSeq *ts = static_cast<TransSeq *>(custom_data->data);
105 MEM_freeN(ts->tdseq);
106 MEM_delete(ts);
107 custom_data->data = nullptr;
108 }
109}
110
111static void create_trans_seq_clamp_data(TransInfo *t, const Scene *scene)
112{
113 TransSeq *ts = (TransSeq *)TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data;
114 const Editing *ed = seq::editing_get(scene);
115
116 /* Prevent snaps and change in `values` past `offset_clamp` for all selected retiming keys. */
117 BLI_rcti_init(&ts->offset_clamp, -INT_MAX, INT_MAX, 0, 0);
118
120 for (auto item : selection.items()) {
121 SeqRetimingKey *key = item.key;
122
123 /* Transition retiming key. */
126 {
128 SeqRetimingKey *key_end = key_start + 1;
129 SeqRetimingKey *key_prev = key_start - 1;
130 SeqRetimingKey *key_next = key_end + 1;
131
132 const float midpoint = key_start->original_strip_frame_index;
133
134 /* Ensure start transition key cannot pass the previous key, or linked end transition key
135 * cannot pass the next key. This transform behavior is symmetrical and limited by the
136 * smallest distance between keys. */
137 const int max_offset = min_ii(key_start->strip_frame_index - key_prev->strip_frame_index - 1,
138 key_next->strip_frame_index - key_end->strip_frame_index - 1);
139
140 if (key_start->flag & SEQ_KEY_SELECTED) {
141 /* Ensure start transition key cannot pass the midpoint. */
142 ts->offset_clamp.xmax = min_ii(midpoint - key_start->strip_frame_index,
143 ts->offset_clamp.xmax);
144 ts->offset_clamp.xmin = max_ii(-max_offset, ts->offset_clamp.xmin);
145 }
146 else {
147 /* Ensure end transition key cannot pass the midpoint. */
148 ts->offset_clamp.xmin = max_ii(-(key_end->strip_frame_index - midpoint - 1),
149 ts->offset_clamp.xmin);
150 ts->offset_clamp.xmax = min_ii(max_offset, ts->offset_clamp.xmax);
151 }
152 }
153 /* Non-transition retiming key. */
154 else {
155 Strip *strip = item.value;
156 SeqRetimingKey *key_prev = key - 1, *key_next = key + 1;
157 if (!seq::retiming_is_last_key(strip, key)) {
158 /* Ensure that this key cannot pass the next key. */
159 ts->offset_clamp.xmax = min_ii(key_next->strip_frame_index - key->strip_frame_index - 1,
160 ts->offset_clamp.xmax);
161 /* XXX: There is an off-by-one error for the last "fake" key's `strip_frame_index`, which
162 * is 1 less than it should be. This is not an immediate issue but should be fixed. */
163 }
164 if (key->strip_frame_index != 0) {
165 /* Ensure that this key cannot pass the previous key. */
166 ts->offset_clamp.xmin = max_ii(-(key->strip_frame_index - key_prev->strip_frame_index - 1),
167 ts->offset_clamp.xmin);
168 }
169 }
170 }
171}
172
174{
175 const Editing *ed = seq::editing_get(t->scene);
176 if (ed == nullptr) {
177 return;
178 }
179
181
182 if (selection.is_empty()) {
183 return;
184 }
185
188 tc->data_len = selection.size();
189
190 TransSeq *ts = MEM_new<TransSeq>(__func__);
191 tc->custom.type.data = ts;
192 tc->custom.type.use_free = true;
193
194 TransData *td = MEM_calloc_arrayN<TransData>(tc->data_len, "TransSeq TransData");
195 TransData2D *td2d = MEM_calloc_arrayN<TransData2D>(tc->data_len, "TransSeq TransData2D");
196 TransDataSeq *tdseq = MEM_calloc_arrayN<TransDataSeq>(tc->data_len, "TransSeq TransDataSeq");
197 tc->data = td;
198 tc->data_2d = td2d;
199 ts->tdseq = tdseq;
200
201 for (auto item : selection.items()) {
202 SeqToTransData(t->scene, item.value, item.key, td++, td2d++, tdseq++);
203 }
204
206}
207
209{
211 const TransData *td = nullptr;
212 const TransData2D *td2d = nullptr;
213 int i;
214
215 VectorSet<Strip *> transformed_strips;
216
217 for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) {
218 const TransDataSeq *tdseq = static_cast<TransDataSeq *>(td->extra);
219 Strip *strip = tdseq->strip;
220
221 if (!seq::retiming_data_is_editable(strip)) {
222 continue;
223 }
224
225 float offset[2];
226 float offset_clamped[2];
227 sub_v2_v2v2(offset, td->loc, td->iloc);
228 copy_v2_v2(offset_clamped, offset);
229
230 transform_convert_sequencer_clamp(t, offset_clamped);
231 const int new_frame = round_fl_to_int(td->iloc[0] + offset_clamped[0]);
232
233 transformed_strips.add(strip);
234
235 /* Calculate translation. */
236
237 const MutableSpan keys = seq::retiming_keys_get(strip);
238 SeqRetimingKey *key = &keys[tdseq->key_index];
239
242 {
244 }
245 else {
246 seq::retiming_key_timeline_frame_set(t->scene, strip, key, new_frame, true);
247 }
248
250 }
251
252 /* Test overlap, displays red outline. */
256 for (Strip *strip : transformed_strips) {
257 strip->runtime.flag &= ~STRIP_OVERLAP;
259 strip->runtime.flag |= STRIP_OVERLAP;
260 }
261 }
262}
263
269
270} // namespace blender::ed::transform
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 sub_v2_v2v2(float r[2], const float a[2], const float b[2])
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.cc:414
@ SEQ_KEY_SELECTED
@ STRIP_OVERLAP
@ SEQ_MARKER_TRANS
Read Guarded memory(de)allocation.
int64_t size() const
Definition BLI_map.hh:976
bool is_empty() const
Definition BLI_map.hh:986
ItemIterator items() const &
Definition BLI_map.hh:902
bool add(const Key &key)
void add_multiple(Span< Key > keys)
int64_t remove_if(Predicate &&predicate)
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 TransData * SeqToTransData(Scene *scene, TransData *td, TransData2D *td2d, TransDataSeq *tdsq, Strip *strip, int flag, int sel_flag)
bool transform_convert_sequencer_clamp(const TransInfo *t, float r_val[2])
static void recalcData_sequencer_retiming(TransInfo *t)
static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data)
static void create_trans_seq_clamp_data(TransInfo *t, const Scene *scene)
bool seq_transform_check_overlap(Span< Strip * > transformed_strips)
static void createTransSeqRetimingData(bContext *, TransInfo *t)
int retiming_key_index_get(const Strip *strip, const SeqRetimingKey *key)
void relations_invalidate_cache(Scene *scene, Strip *strip)
bool retiming_selection_has_whole_transition(const Editing *ed, SeqRetimingKey *key)
blender::Map< SeqRetimingKey *, Strip * > retiming_selection_get(const Editing *ed)
int retiming_key_timeline_frame_get(const Scene *scene, const Strip *strip, const SeqRetimingKey *key)
bool transform_test_overlap(const Scene *scene, Strip *strip1, Strip *strip2)
Editing * editing_get(const Scene *scene)
Definition sequencer.cc:286
MutableSpan< SeqRetimingKey > retiming_keys_get(const Strip *strip)
SeqRetimingKey * retiming_transition_start_get(SeqRetimingKey *key)
void query_strip_effect_chain(const Scene *scene, Strip *reference_strip, ListBase *seqbase, VectorSet< Strip * > &r_strips)
Definition iterator.cc:254
bool retiming_key_is_transition_type(const SeqRetimingKey *key)
bool retiming_data_is_editable(const Strip *strip)
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
void retiming_key_timeline_frame_set(const Scene *scene, Strip *strip, SeqRetimingKey *key, int timeline_frame, bool keep_retiming)
void retiming_transition_key_frame_set(const Scene *scene, const Strip *strip, SeqRetimingKey *key, const 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
bool transform_strip_can_be_translated(const Strip *strip)
bool retiming_is_last_key(const Strip *strip, const SeqRetimingKey *key)
void * first
ListBase spacedata
double original_strip_frame_index
void(* free_cb)(TransInfo *, TransDataContainer *tc, TransCustomData *custom_data)
Definition transform.hh:633
int xmin
int xmax
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.