Blender V5.0
grease_pencil_interpolate.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_colortools.hh"
6#include "BKE_context.hh"
7#include "BKE_curves.hh"
8#include "BKE_deform.hh"
10#include "BKE_paint.hh"
11
12#include "BLI_array_utils.hh"
13#include "BLI_easing.h"
14#include "BLI_index_mask.hh"
16#include "BLI_math_geom.h"
17#include "BLI_math_rotation.h"
18#include "BLI_math_rotation.hh"
19#include "BLI_math_vector.hh"
20#include "BLI_offset_indices.hh"
21#include "BLI_task.hh"
22
23#include "BLT_translation.hh"
24
25#include "DEG_depsgraph.hh"
26
28
29#include "ED_curves.hh"
30#include "ED_grease_pencil.hh"
31#include "ED_numinput.hh"
32#include "ED_screen.hh"
33
35#include "GEO_smooth_curves.hh"
36
37#include "MEM_guardedalloc.h"
38
39#include "RNA_access.hh"
40#include "RNA_define.hh"
41#include "RNA_enum_types.hh"
42#include "RNA_prototypes.hh"
43
44#include "UI_interface.hh"
46#include "UI_resources.hh"
47
48#include <climits>
49
51
54
55/* -------------------------------------------------------------------- */
58
59/* Modes for the interpolation tool. */
77
83 /* Interpolation. */
85 N_("Standard transitions between keyframes")),
87 "LINEAR",
88 ICON_IPO_LINEAR,
89 "Linear",
90 "Straight-line interpolation between A and B (i.e. no ease in/out)"},
92 "CUSTOM",
93 ICON_IPO_BEZIER,
94 "Custom",
95 "Custom interpolation defined using a curve map"},
96
97 /* Easing. */
99 N_("Predefined inertial transitions, useful for motion graphics "
100 "(from least to most \"dramatic\")")),
102 "SINE",
103 ICON_IPO_SINE,
104 "Sinusoidal",
105 "Sinusoidal easing (weakest, almost linear but with a slight curvature)"},
106 {int(InterpolationType::Quadratic), "QUAD", ICON_IPO_QUAD, "Quadratic", "Quadratic easing"},
107 {int(InterpolationType::Cubic), "CUBIC", ICON_IPO_CUBIC, "Cubic", "Cubic easing"},
108 {int(InterpolationType::Quartic), "QUART", ICON_IPO_QUART, "Quartic", "Quartic easing"},
109 {int(InterpolationType::Quintic), "QUINT", ICON_IPO_QUINT, "Quintic", "Quintic easing"},
111 "EXPO",
112 ICON_IPO_EXPO,
113 "Exponential",
114 "Exponential easing (dramatic)"},
116 "CIRC",
117 ICON_IPO_CIRC,
118 "Circular",
119 "Circular easing (strongest and most dynamic)"},
120
122 N_("Simple physics-inspired easing effects")),
124 "BACK",
125 ICON_IPO_BACK,
126 "Back",
127 "Cubic easing with overshoot and settle"},
129 "BOUNCE",
130 ICON_IPO_BOUNCE,
131 "Bounce",
132 "Exponentially decaying parabolic bounce, like when objects collide"},
134 "ELASTIC",
135 ICON_IPO_ELASTIC,
136 "Elastic",
137 "Exponentially decaying sine wave, like an elastic band"},
138
139 {0, nullptr, 0, nullptr, nullptr},
140};
141
143 {int(InterpolateFlipMode::None), "NONE", 0, "No Flip", ""},
144 {int(InterpolateFlipMode::Flip), "FLIP", 0, "Flip", ""},
145 {int(InterpolateFlipMode::FlipAuto), "AUTO", 0, "Automatic", ""},
146 {0, nullptr, 0, nullptr, nullptr},
147};
148
150 {int(InterpolateLayerMode::Active), "ACTIVE", 0, "Active", ""},
151 {int(InterpolateLayerMode::All), "ALL", 0, "All Layers", ""},
152 {0, nullptr, 0, nullptr, nullptr},
153};
154
155constexpr float interpolate_factor_min = -1.0f;
156constexpr float interpolate_factor_max = 2.0f;
157
158/* Pair of curves in a layer that get interpolated. */
165
167 struct LayerData {
168 /* Curve pairs to interpolate from this layer. */
170
171 /* Geometry of the target frame before interpolation for restoring on cancel. */
172 std::optional<bke::CurvesGeometry> orig_curves;
173 };
174
175 /* Layers to include. */
178 /* Exclude breakdown keyframes when finding intervals. */
180
181 /* Interpolation factor bias controlled by the user. */
182 float shift;
183 /* Interpolation base factor for the active layer. */
188
192
193 static InterpolateOpData *from_operator(const bContext &C, const wmOperator &op);
194};
195
196using FramesMapKeyIntervalT = std::pair<int, int>;
197
198static std::optional<FramesMapKeyIntervalT> find_frames_interval(
199 const bke::greasepencil::Layer &layer, const int frame_number, const bool exclude_breakdowns)
200{
203 using SortedKeysIterator = Layer::SortedKeysIterator;
204
205 const Span<FramesMapKeyT> sorted_keys = layer.sorted_keys();
206 SortedKeysIterator prev_key_it = layer.sorted_keys_iterator_at(frame_number);
207 if (!prev_key_it) {
208 return std::nullopt;
209 }
210 SortedKeysIterator next_key_it = std::next(prev_key_it);
211
212 /* Skip over invalid keyframes on either side. */
213 auto is_valid_keyframe = [&](const FramesMapKeyT key) {
214 const GreasePencilFrame *frame = layer.frame_at(key);
215 if (!frame || frame->is_end()) {
216 return false;
217 }
218 if (exclude_breakdowns && frame->type == BEZT_KEYTYPE_BREAKDOWN) {
219 return false;
220 }
221 return true;
222 };
223
224 for (; next_key_it != sorted_keys.end(); ++next_key_it) {
225 if (is_valid_keyframe(*next_key_it)) {
226 break;
227 }
228 }
229 for (; prev_key_it != sorted_keys.begin(); --prev_key_it) {
230 if (is_valid_keyframe(*prev_key_it)) {
231 break;
232 }
233 }
234 if (next_key_it == sorted_keys.end() || !is_valid_keyframe(*prev_key_it)) {
235 return std::nullopt;
236 }
237
238 return std::make_pair(*prev_key_it, *next_key_it);
239}
240
241/* Build index lists for curve interpolation using index. */
242static bool find_curve_mapping_from_index(const GreasePencil &grease_pencil,
243 const bke::greasepencil::Layer &layer,
244 const int current_frame,
245 const bool exclude_breakdowns,
246 const bool only_selected,
247 InterpolationPairs &pairs)
248{
250
251 const std::optional<FramesMapKeyIntervalT> interval = find_frames_interval(
252 layer, current_frame, exclude_breakdowns);
253 if (!interval) {
254 return false;
255 }
256
257 BLI_assert(layer.has_drawing_at(interval->first));
258 BLI_assert(layer.has_drawing_at(interval->second));
259 const Drawing &from_drawing = *grease_pencil.get_drawing_at(layer, interval->first);
260 const Drawing &to_drawing = *grease_pencil.get_drawing_at(layer, interval->second);
261 /* In addition to interpolated pairs, the unselected original strokes are also included, making
262 * the total pair count the same as the "from" curve count. */
263 const int pairs_num = from_drawing.strokes().curves_num();
264
265 const int old_pairs_num = pairs.from_frames.size();
266 pairs.from_frames.append_n_times(interval->first, pairs_num);
267 pairs.to_frames.append_n_times(interval->second, pairs_num);
268 pairs.from_curves.resize(old_pairs_num + pairs_num);
269 pairs.to_curves.resize(old_pairs_num + pairs_num);
270 MutableSpan<int> from_curves = pairs.from_curves.as_mutable_span().slice(old_pairs_num,
271 pairs_num);
272 MutableSpan<int> to_curves = pairs.to_curves.as_mutable_span().slice(old_pairs_num, pairs_num);
273
274 /* Write source indices into the pair data. If one drawing has more selected curves than the
275 * other the remainder is ignored. */
276
277 IndexMaskMemory memory;
278 IndexMask from_selection, to_selection;
279 if (only_selected && ed::curves::has_anything_selected(from_drawing.strokes()) &&
281 {
282 from_selection = ed::curves::retrieve_selected_curves(from_drawing.strokes(), memory);
283 to_selection = ed::curves::retrieve_selected_curves(to_drawing.strokes(), memory);
284 }
285 else {
286 from_selection = from_drawing.strokes().curves_range();
287 to_selection = to_drawing.strokes().curves_range();
288 }
289 /* Discard additional elements of the larger selection. */
290 if (from_selection.size() > to_selection.size()) {
291 from_selection = from_selection.slice(0, to_selection.size());
292 }
293 else if (to_selection.size() > from_selection.size()) {
294 to_selection = to_selection.slice(0, from_selection.size());
295 }
296
297 /* By default: copy the "from" curve and ignore the "to" curve. */
299 to_curves.fill(-1);
300 /* Selected curves are interpolated. */
301 IndexMask::foreach_segment_zipped({from_selection, to_selection},
302 [&](Span<IndexMaskSegment> segments) {
303 const IndexMaskSegment &from_segment = segments[0];
304 const IndexMaskSegment &to_segment = segments[1];
305 BLI_assert(from_segment.size() == to_segment.size());
306 for (const int i : from_segment.index_range()) {
307 to_curves[from_segment[i]] = to_segment[i];
308 }
309 return true;
310 });
311
312 return true;
313}
314
316{
319
320 const Scene &scene = *CTX_data_scene(&C);
321 const int current_frame = scene.r.cfra;
322 const Object &object = *CTX_data_active_object(&C);
323 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
324
325 if (!grease_pencil.has_active_layer()) {
326 return nullptr;
327 }
328
329 const Layer &active_layer = *grease_pencil.get_active_layer();
330
331 InterpolateOpData *data = MEM_new<InterpolateOpData>(__func__);
332
333 if (RNA_struct_find_property(op.ptr, "shift") != nullptr) {
334 data->shift = RNA_float_get(op.ptr, "shift");
335 }
336 data->exclude_breakdowns = RNA_boolean_get(op.ptr, "exclude_breakdowns");
337 data->flipmode = InterpolateFlipMode(RNA_enum_get(op.ptr, "flip"));
338 data->smooth_factor = RNA_float_get(op.ptr, "smooth_factor");
339 data->smooth_steps = RNA_int_get(op.ptr, "smooth_steps");
340 data->active_layer_index = *grease_pencil.get_layer_index(active_layer);
341 const bool use_selection = RNA_boolean_get(op.ptr, "use_selection");
342
343 const auto layer_mode = InterpolateLayerMode(RNA_enum_get(op.ptr, "layers"));
344 switch (layer_mode) {
345 case InterpolateLayerMode::Active:
346 data->layer_mask = IndexRange::from_single(data->active_layer_index);
347 break;
348 case InterpolateLayerMode::All:
349 data->layer_mask = IndexMask::from_predicate(
350 grease_pencil.layers().index_range(),
351 GrainSize(1024),
352 data->layer_mask_memory,
353 [&](const int layer_index) { return grease_pencil.layer(layer_index).is_editable(); });
354 break;
355 }
356
357 bool found_mapping = false;
358 data->layer_data.reinitialize(grease_pencil.layers().size());
359 data->layer_mask.foreach_index([&](const int layer_index) {
360 const Layer &layer = grease_pencil.layer(layer_index);
361 InterpolateOpData::LayerData &layer_data = data->layer_data[layer_index];
362
363 /* Pair from/to curves by index. */
364 const bool has_curve_mapping = find_curve_mapping_from_index(grease_pencil,
365 layer,
366 current_frame,
367 data->exclude_breakdowns,
368 use_selection,
369 layer_data.curve_pairs);
370 found_mapping = found_mapping || has_curve_mapping;
371 });
372
373 /* No mapping between frames was found. */
374 if (!found_mapping) {
375 MEM_delete(data);
376 return nullptr;
377 }
378
379 const std::optional<FramesMapKeyIntervalT> active_layer_interval = find_frames_interval(
380 active_layer, current_frame, data->exclude_breakdowns);
381 data->init_factor = active_layer_interval ?
382 float(current_frame - active_layer_interval->first) /
383 (active_layer_interval->second - active_layer_interval->first + 1) :
384 0.5f;
385
386 return data;
387}
388
389/* Find ranges of sorted pairs with the same from/to frame intervals. */
391 const Span<int> order)
392{
393 Vector<int> pair_offsets;
394
395 int prev_from_frame = INT_MIN;
396 int prev_to_frame = INT_MIN;
397 int current_count = 0;
398 for (const int pair_index : order) {
399 const int from_frame = curve_pairs.from_frames[pair_index];
400 const int to_frame = curve_pairs.to_frames[pair_index];
401 if (from_frame != prev_from_frame || to_frame != prev_to_frame) {
402 /* New pair. */
403 if (current_count > 0) {
404 pair_offsets.append(current_count);
405 }
406 current_count = 0;
407 }
408 ++current_count;
409 }
410 if (current_count > 0) {
411 pair_offsets.append(current_count);
412 }
413
414 /* Last entry for overall size. */
415 if (pair_offsets.is_empty()) {
416 return {};
417 }
418
419 /* Extra element for the total size needed for OffsetIndices. */
420 pair_offsets.append(0);
422
423 return pair_offsets;
424}
425
426static bool compute_auto_flip(const Span<float3> from_positions, const Span<float3> to_positions)
427{
428 if (from_positions.size() < 2 || to_positions.size() < 2) {
429 return false;
430 }
431
432 constexpr float min_angle = DEG2RADF(15);
433
434 const float3 &from_first = from_positions.first();
435 const float3 &from_last = from_positions.last();
436 const float3 &to_first = to_positions.first();
437 const float3 &to_last = to_positions.last();
438
439 /* If lines intersect at a sharp angle check distances. */
440 if (isect_seg_seg_v2(from_first, to_first, from_last, to_last) == ISECT_LINE_LINE_CROSS) {
441 if (math::angle_between(math::normalize(to_first - from_first),
442 math::normalize(to_last - from_last))
443 .radian() < min_angle)
444 {
445 if (math::distance_squared(from_first, to_first) >=
446 math::distance_squared(from_last, to_first))
447 {
448 return math::distance_squared(from_last, to_first) >=
449 math::distance_squared(from_last, to_last);
450 }
451
452 return math::distance_squared(from_first, to_first) <
453 math::distance_squared(from_first, to_last);
454 }
455
456 return true;
457 }
458
459 return math::dot(from_last - from_first, to_last - to_first) < 0.0f;
460}
461
463 const bke::greasepencil::Layer &layer,
464 const InterpolationPairs &curve_pairs,
465 const float mix_factor,
466 const InterpolateFlipMode flip_mode)
467{
469
470 const int dst_curve_num = curve_pairs.from_curves.size();
471 BLI_assert(curve_pairs.to_curves.size() == dst_curve_num);
472 BLI_assert(curve_pairs.from_frames.size() == dst_curve_num);
473 BLI_assert(curve_pairs.to_frames.size() == dst_curve_num);
474
475 /* Sort pairs by unique to/from frame combinations.
476 * Curves for each frame pair are then interpolated together.
477 * Map entries are indices into the original curve_pairs array,
478 * so the order of strokes can be maintained. */
479 Array<int> sorted_pairs(dst_curve_num);
481 std::sort(sorted_pairs.begin(), sorted_pairs.end(), [&](const int a, const int b) {
482 const int from_frame_a = curve_pairs.from_frames[a];
483 const int to_frame_a = curve_pairs.to_frames[a];
484 const int from_frame_b = curve_pairs.from_frames[b];
485 const int to_frame_b = curve_pairs.to_frames[b];
486 return from_frame_a < from_frame_b ||
487 (from_frame_a == from_frame_b && to_frame_a < to_frame_b);
488 });
489
490 /* Find ranges of sorted pairs with the same from/to frame intervals. */
491 Vector<int> pair_offsets = find_curve_pair_offsets(curve_pairs, sorted_pairs);
492 const OffsetIndices<int> curves_by_pair(pair_offsets);
493
494 /* Compute curve length and flip mode for each pair. */
495 Array<int> dst_curve_offsets(curves_by_pair.size() + 1, 0);
496 Array<bool> dst_curve_flip(curves_by_pair.size(), false);
497 const OffsetIndices<int> dst_points_by_curve = [&]() {
498 /* Last entry for overall size. */
499 if (curves_by_pair.is_empty()) {
500 return OffsetIndices<int>{};
501 }
502
503 for (const int pair_range_i : curves_by_pair.index_range()) {
504 const IndexRange pair_range = curves_by_pair[pair_range_i];
505 BLI_assert(!pair_range.is_empty());
506
507 const int first_pair_index = sorted_pairs[pair_range.first()];
508 const int from_frame = curve_pairs.from_frames[first_pair_index];
509 const int to_frame = curve_pairs.to_frames[first_pair_index];
510 const Drawing *from_drawing = grease_pencil.get_drawing_at(layer, from_frame);
511 const Drawing *to_drawing = grease_pencil.get_drawing_at(layer, to_frame);
512 if (!from_drawing || !to_drawing) {
513 continue;
514 }
515 const OffsetIndices from_points_by_curve = from_drawing->strokes().points_by_curve();
516 const OffsetIndices to_points_by_curve = to_drawing->strokes().points_by_curve();
517 const Span<float3> from_positions = from_drawing->strokes().positions();
518 const Span<float3> to_positions = to_drawing->strokes().positions();
519
520 for (const int sorted_index : pair_range) {
521 const int pair_index = sorted_pairs[sorted_index];
522 const int from_curve = curve_pairs.from_curves[pair_index];
523 const int to_curve = curve_pairs.to_curves[pair_index];
524
525 int curve_size = 0;
526 bool curve_flip = false;
527 if (from_curve < 0 && to_curve < 0) {
528 /* No output curve. */
529 }
530 else if (from_curve < 0) {
531 const IndexRange to_points = to_points_by_curve[to_curve];
532 curve_size = to_points.size();
533 curve_flip = false;
534 }
535 else if (to_curve < 0) {
536 const IndexRange from_points = from_points_by_curve[from_curve];
537 curve_size = from_points.size();
538 curve_flip = false;
539 }
540 else {
541 const IndexRange from_points = from_points_by_curve[from_curve];
542 const IndexRange to_points = to_points_by_curve[to_curve];
543
544 curve_size = std::max(from_points.size(), to_points.size());
545 switch (flip_mode) {
546 case InterpolateFlipMode::None:
547 curve_flip = false;
548 break;
549 case InterpolateFlipMode::Flip:
550 curve_flip = true;
551 break;
552 case InterpolateFlipMode::FlipAuto: {
553 curve_flip = compute_auto_flip(from_positions.slice(from_points),
554 to_positions.slice(to_points));
555 break;
556 }
557 }
558 }
559
560 dst_curve_offsets[pair_index] = curve_size;
561 dst_curve_flip[pair_index] = curve_flip;
562 }
563 }
564 return offset_indices::accumulate_counts_to_offsets(dst_curve_offsets);
565 }();
566 const int dst_point_num = dst_points_by_curve.total_size();
567
568 bke::CurvesGeometry dst_curves(dst_point_num, dst_curve_num);
569 /* Offsets are empty when there are no curves. */
570 if (dst_curve_num > 0) {
571 dst_curves.offsets_for_write().copy_from(dst_curve_offsets);
572 }
573
574 /* Copy vertex group names since we still have other parts of the code depends on vertex group
575 * names to be available. */
577
578 /* Sorted map arrays that can be passed to the interpolation function directly.
579 * These index maps have the same order as the sorted indices, so slices of indices can be used
580 * for interpolating all curves of a frame pair at once. */
581 Array<int> from_curve_buffer(dst_curve_num);
582 Array<int> to_curve_buffer(dst_curve_num);
583 Array<int> from_sample_indices(dst_point_num);
584 Array<int> to_sample_indices(dst_point_num);
585 Array<float> from_sample_factors(dst_point_num);
586 Array<float> to_sample_factors(dst_point_num);
587 IndexMaskMemory memory;
588
589 for (const int pair_range_i : curves_by_pair.index_range()) {
590 const IndexRange pair_range = curves_by_pair[pair_range_i];
591 /* Subset of target curves that are filled by this frame pair. Selection is built from pair
592 * indices, which correspond to dst curve indices. */
593 const IndexMask dst_curve_mask = IndexMask::from_indices(
594 sorted_pairs.as_span().slice(pair_range), memory);
595 MutableSpan<int> from_indices = from_curve_buffer.as_mutable_span().slice(pair_range);
596 MutableSpan<int> to_indices = to_curve_buffer.as_mutable_span().slice(pair_range);
597
598 const int first_pair_index = sorted_pairs[pair_range.first()];
599 const int from_frame = curve_pairs.from_frames[first_pair_index];
600 const int to_frame = curve_pairs.to_frames[first_pair_index];
601 const Drawing *from_drawing = grease_pencil.get_drawing_at(layer, from_frame);
602 const Drawing *to_drawing = grease_pencil.get_drawing_at(layer, to_frame);
603 if (!from_drawing || !to_drawing) {
604 continue;
605 }
606 const OffsetIndices from_points_by_curve = from_drawing->strokes().points_by_curve();
607 const OffsetIndices to_points_by_curve = to_drawing->strokes().points_by_curve();
608 const VArray<bool> from_curves_cyclic = from_drawing->strokes().cyclic();
609 const VArray<bool> to_curves_cyclic = to_drawing->strokes().cyclic();
610
611 for (const int i : pair_range.index_range()) {
612 const int pair_index = sorted_pairs[pair_range[i]];
613 const IndexRange dst_points = dst_points_by_curve[pair_index];
614 from_indices[i] = curve_pairs.from_curves[pair_index];
615 to_indices[i] = curve_pairs.to_curves[pair_index];
616
617 const int from_curve = curve_pairs.from_curves[pair_index];
618 const int to_curve = curve_pairs.to_curves[pair_index];
619
620 BLI_assert(from_curve >= 0 || to_curve >= 0);
621 if (to_curve < 0) {
622 /* Copy "from" curve. */
623 array_utils::fill_index_range(from_sample_indices.as_mutable_span().slice(dst_points));
624 from_sample_factors.fill(0.0f);
625 continue;
626 }
627 if (from_curve < 0) {
628 /* Copy "to" curve. */
629 array_utils::fill_index_range(to_sample_indices.as_mutable_span().slice(dst_points));
630 to_sample_factors.fill(0.0f);
631 continue;
632 }
633
634 const IndexRange from_points = from_points_by_curve[from_curve];
635 const IndexRange to_points = to_points_by_curve[to_curve];
636 if (from_points.size() >= to_points.size()) {
637 /* Target curve samples match 'from' points. */
638 BLI_assert(from_points.size() == dst_points.size());
639 array_utils::fill_index_range(from_sample_indices.as_mutable_span().slice(dst_points));
640 from_sample_factors.as_mutable_span().slice(dst_points).fill(0.0f);
642 to_curve,
643 to_curves_cyclic[to_curve],
644 dst_curve_flip[pair_index],
645 to_sample_indices.as_mutable_span().slice(dst_points),
646 to_sample_factors.as_mutable_span().slice(dst_points));
647 }
648 else {
649 /* Target curve samples match 'to' points. */
650 BLI_assert(to_points.size() == dst_points.size());
652 from_curve,
653 from_curves_cyclic[from_curve],
654 dst_curve_flip[pair_index],
655 from_sample_indices.as_mutable_span().slice(dst_points),
656 from_sample_factors.as_mutable_span().slice(dst_points));
657 array_utils::fill_index_range(to_sample_indices.as_mutable_span().slice(dst_points));
658 to_sample_factors.fill(0.0f);
659 }
660 }
661
663 to_drawing->strokes(),
664 from_indices,
665 to_indices,
666 from_sample_indices,
667 to_sample_indices,
668 from_sample_factors,
669 to_sample_factors,
670 dst_curve_mask,
671 mix_factor,
672 dst_curves,
673 memory);
674 }
675
676 return dst_curves;
677}
678
680
681/* -------------------------------------------------------------------- */
684
686 const InterpolateOpData &opdata)
687{
688 Scene &scene = *CTX_data_scene(&C);
689 ScrArea &area = *CTX_wm_area(&C);
690
691 const StringRef msg = IFACE_("GPencil Interpolation: ");
692
693 std::string status;
694 if (hasNumInput(&opdata.numeric_input)) {
695 char str_ofs[NUM_STR_REP_LEN];
696 outputNumInput(&const_cast<NumInput &>(opdata.numeric_input), str_ofs, scene.unit);
697 status = msg + std::string(str_ofs);
698 }
699 else {
700 status = msg + std::to_string(int((opdata.init_factor + opdata.shift) * 100.0f)) + " %";
701 }
702
703 ED_area_status_text(&area, status.c_str());
705 &C, IFACE_("ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/MOVE to adjust factor"));
706}
707
708/* Utility function to get a drawing at the exact frame number. */
711 const int frame_number)
712{
714
715 const std::optional<int> start_frame = layer.start_frame_at(frame_number);
716 if (start_frame && *start_frame == frame_number) {
717 return grease_pencil.get_editable_drawing_at(layer, frame_number);
718 }
719 return nullptr;
720}
721
723 GreasePencil &grease_pencil,
726 const int frame_number)
727{
729
730 static constexpr eBezTriple_KeyframeType keyframe_type = BEZT_KEYTYPE_BREAKDOWN;
731
732 if (Drawing *drawing = get_drawing_at_exact_frame(grease_pencil, layer, frame_number)) {
733 layer_data.orig_curves = drawing->strokes();
734 return drawing;
735 }
736 return grease_pencil.insert_frame(layer, frame_number, 0, keyframe_type);
737}
738
740{
743
744 const auto &opdata = *static_cast<InterpolateOpData *>(op.customdata);
745 const Scene &scene = *CTX_data_scene(&C);
746 const int current_frame = scene.r.cfra;
747 Object &object = *CTX_data_active_object(&C);
748 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
749 const auto flip_mode = InterpolateFlipMode(RNA_enum_get(op.ptr, "flip"));
750
751 opdata.layer_mask.foreach_index([&](const int layer_index) {
752 Layer &layer = grease_pencil.layer(layer_index);
753 const InterpolateOpData::LayerData &layer_data = opdata.layer_data[layer_index];
754
755 /* Drawings must be created on operator invoke. */
756 Drawing *dst_drawing = get_drawing_at_exact_frame(grease_pencil, layer, current_frame);
757 if (dst_drawing == nullptr) {
758 return;
759 }
760
761 const float mix_factor = opdata.init_factor + opdata.shift;
763 grease_pencil, layer, layer_data.curve_pairs, mix_factor, flip_mode);
764
765 if (opdata.smooth_factor > 0.0f && opdata.smooth_steps > 0) {
766 MutableSpan<float3> positions = interpolated_curves.positions_for_write();
768 interpolated_curves.curves_range(),
769 interpolated_curves.points_by_curve(),
770 VArray<bool>::from_single(true, interpolated_curves.points_num()),
771 interpolated_curves.cyclic(),
772 opdata.smooth_steps,
773 opdata.smooth_factor,
774 false,
775 true,
776 positions);
777 interpolated_curves.tag_positions_changed();
778 }
779
780 dst_drawing->strokes_for_write() = std::move(interpolated_curves);
781 dst_drawing->tag_topology_changed();
782 });
783
785
788}
789
790/* Restore timeline changes when canceled. */
792{
795
796 if (op.customdata == nullptr) {
797 return;
798 }
799
800 const auto &opdata = *static_cast<InterpolateOpData *>(op.customdata);
801 const Scene &scene = *CTX_data_scene(&C);
802 const int current_frame = scene.r.cfra;
803 Object &object = *CTX_data_active_object(&C);
804 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
805
806 opdata.layer_mask.foreach_index([&](const int layer_index) {
807 Layer &layer = grease_pencil.layer(layer_index);
808 const InterpolateOpData::LayerData &layer_data = opdata.layer_data[layer_index];
809
810 if (layer_data.orig_curves) {
811 /* Keyframe existed before the operator, restore geometry. */
812 Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, current_frame);
813 if (drawing) {
814 drawing->strokes_for_write() = *layer_data.orig_curves;
815 drawing->tag_topology_changed();
818 }
819 }
820 else {
821 /* Frame was empty, remove the added drawing. */
822 grease_pencil.remove_frames(layer, {current_frame});
825 }
826 });
827}
828
830{
832
834 if (op.customdata == nullptr) {
835 return false;
836 }
837 InterpolateOpData &data = *static_cast<InterpolateOpData *>(op.customdata);
838
839 const Scene &scene = *CTX_data_scene(&C);
840 const int current_frame = scene.r.cfra;
841 Object &object = *CTX_data_active_object(&C);
842 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
843
844 /* Create target frames. */
845 data.layer_mask.foreach_index([&](const int layer_index) {
846 Layer &layer = grease_pencil.layer(layer_index);
847 InterpolateOpData::LayerData &layer_data = data.layer_data[layer_index];
848
849 ensure_drawing_at_exact_frame(grease_pencil, layer, layer_data, current_frame);
850 });
851
852 return true;
853}
854
855/* Exit and free memory. */
857{
858 ScrArea &area = *CTX_wm_area(&C);
859
860 if (op.customdata == nullptr) {
861 return;
862 }
863
864 ED_area_status_text(&area, nullptr);
865 ED_workspace_status_text(&C, nullptr);
866
867 MEM_delete(static_cast<InterpolateOpData *>(op.customdata));
868 op.customdata = nullptr;
869}
870
872{
874 return false;
875 }
877 if (!ts || !ts->gp_paint) {
878 return false;
879 }
880 /* Only 3D view */
881 ScrArea *area = CTX_wm_area(C);
882 if (area && area->spacetype != SPACE_VIEW3D) {
883 return false;
884 }
885
886 return true;
887}
888
889/* Invoke handler: Initialize the operator */
891 wmOperator *op,
892 const wmEvent * /*event*/)
893{
894 wmWindow &win = *CTX_wm_window(C);
895
896 if (!grease_pencil_interpolate_init(*C, *op)) {
898 return OPERATOR_CANCELLED;
899 }
900 InterpolateOpData &opdata = *static_cast<InterpolateOpData *>(op->customdata);
901
902 /* Set cursor to indicate modal operator. */
904
906
908
910
912}
913
920
921/* Modal handler: Events handling during interactive part */
923 wmOperator *op,
924 const wmEvent *event)
925{
926 wmWindow &win = *CTX_wm_window(C);
927 const ARegion &region = *CTX_wm_region(C);
928 ScrArea &area = *CTX_wm_area(C);
929 InterpolateOpData &opdata = *static_cast<InterpolateOpData *>(op->customdata);
930 const bool has_numinput = hasNumInput(&opdata.numeric_input);
931
932 switch (event->type) {
933 case EVT_MODAL_MAP: {
934 switch (InterpolateToolModalEvent(event->val)) {
936 ED_area_status_text(&area, nullptr);
937 ED_workspace_status_text(C, nullptr);
939
942 return OPERATOR_CANCELLED;
944 ED_area_status_text(&area, nullptr);
945 ED_workspace_status_text(C, nullptr);
947
948 /* Write current factor to properties for the next execution. */
949 RNA_float_set(op->ptr, "shift", opdata.shift);
950
952 return OPERATOR_FINISHED;
954 opdata.shift = std::clamp(opdata.init_factor + opdata.shift + 0.01f,
957 opdata.init_factor;
959 break;
961 opdata.shift = std::clamp(opdata.init_factor + opdata.shift - 0.01f,
964 opdata.init_factor;
966 break;
967 }
968 break;
969 }
970 case MOUSEMOVE:
971 /* Only handle mouse-move if not doing numeric-input. */
972 if (!has_numinput) {
973 const float mouse_pos = event->mval[0];
974 const float factor = std::clamp(
976 opdata.shift = factor - opdata.init_factor;
977
979 }
980 break;
981 default: {
982 if ((event->val == KM_PRESS) && handleNumInput(C, &opdata.numeric_input, event)) {
983 float value = (opdata.init_factor + opdata.shift) * 100.0f;
984 applyNumInput(&opdata.numeric_input, &value);
985 opdata.shift = std::clamp(value * 0.01f, interpolate_factor_min, interpolate_factor_max) -
986 opdata.init_factor;
987
989 break;
990 }
991 /* Unhandled event, allow to pass through. */
993 }
994 }
995
997}
998
1004
1006{
1007 ot->name = "Grease Pencil Interpolation";
1008 ot->idname = "GREASE_PENCIL_OT_interpolate";
1009 ot->description = "Interpolate Grease Pencil strokes between frames";
1010
1015
1016 ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
1017
1019 ot->srna,
1020 "shift",
1021 0.0f,
1022 -1.0f,
1023 1.0f,
1024 "Shift",
1025 "Bias factor for which frame has more influence on the interpolated strokes",
1026 -0.9f,
1027 0.9f);
1028
1029 RNA_def_enum(ot->srna,
1030 "layers",
1032 0,
1033 "Layer",
1034 "Layers included in the interpolation");
1035
1036 RNA_def_boolean(ot->srna,
1037 "exclude_breakdowns",
1038 false,
1039 "Exclude Breakdowns",
1040 "Exclude existing Breakdowns keyframes as interpolation extremes");
1041
1042 RNA_def_boolean(ot->srna,
1043 "use_selection",
1044 false,
1045 "Use Selection",
1046 "Use only selected strokes for interpolating");
1047
1048 RNA_def_enum(ot->srna,
1049 "flip",
1051 int(InterpolateFlipMode::FlipAuto),
1052 "Flip Mode",
1053 "Invert destination stroke to match start and end with source stroke");
1054
1055 RNA_def_int(ot->srna,
1056 "smooth_steps",
1057 1,
1058 1,
1059 3,
1060 "Iterations",
1061 "Number of times to smooth newly created strokes",
1062 1,
1063 3);
1064
1065 RNA_def_float(ot->srna,
1066 "smooth_factor",
1067 0.0f,
1068 0.0f,
1069 2.0f,
1070 "Smooth",
1071 "Amount of smoothing to apply to interpolated strokes, to reduce jitter/noise",
1072 0.0f,
1073 2.0f);
1074}
1075
1077
1078/* -------------------------------------------------------------------- */
1081
1082/* Helper: Perform easing equation calculations for GP interpolation operator. */
1084 const InterpolationType type,
1085 const float back_easing,
1086 const float amplitude,
1087 const float period,
1088 const CurveMapping &custom_ipo,
1089 const float time)
1090{
1091 constexpr float begin = 0.0f;
1092 constexpr float change = 1.0f;
1093 constexpr float duration = 1.0f;
1094
1095 switch (type) {
1097 return time;
1098
1100 return BKE_curvemapping_evaluateF(&custom_ipo, 0, time);
1101
1103 switch (easing) {
1104 case BEZT_IPO_EASE_IN:
1105 return BLI_easing_back_ease_in(time, begin, change, duration, back_easing);
1106 case BEZT_IPO_EASE_OUT:
1107 return BLI_easing_back_ease_out(time, begin, change, duration, back_easing);
1109 return BLI_easing_back_ease_in_out(time, begin, change, duration, back_easing);
1110
1111 default:
1112 return BLI_easing_back_ease_out(time, begin, change, duration, back_easing);
1113 }
1114 break;
1115
1117 switch (easing) {
1118 case BEZT_IPO_EASE_IN:
1119 return BLI_easing_bounce_ease_in(time, begin, change, duration);
1120 case BEZT_IPO_EASE_OUT:
1121 return BLI_easing_bounce_ease_out(time, begin, change, duration);
1123 return BLI_easing_bounce_ease_in_out(time, begin, change, duration);
1124
1125 default:
1126 return BLI_easing_bounce_ease_out(time, begin, change, duration);
1127 }
1128 break;
1129
1131 switch (easing) {
1132 case BEZT_IPO_EASE_IN:
1133 return BLI_easing_circ_ease_in(time, begin, change, duration);
1134 case BEZT_IPO_EASE_OUT:
1135 return BLI_easing_circ_ease_out(time, begin, change, duration);
1137 return BLI_easing_circ_ease_in_out(time, begin, change, duration);
1138
1139 default:
1140 return BLI_easing_circ_ease_in(time, begin, change, duration);
1141 }
1142 break;
1143
1145 switch (easing) {
1146 case BEZT_IPO_EASE_IN:
1147 return BLI_easing_cubic_ease_in(time, begin, change, duration);
1148 case BEZT_IPO_EASE_OUT:
1149 return BLI_easing_cubic_ease_out(time, begin, change, duration);
1151 return BLI_easing_cubic_ease_in_out(time, begin, change, duration);
1152
1153 default:
1154 return BLI_easing_cubic_ease_in(time, begin, change, duration);
1155 }
1156 break;
1157
1159 switch (easing) {
1160 case BEZT_IPO_EASE_IN:
1161 return BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period);
1162 case BEZT_IPO_EASE_OUT:
1163 return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period);
1165 return BLI_easing_elastic_ease_in_out(time, begin, change, duration, amplitude, period);
1166
1167 default:
1168 return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period);
1169 }
1170 break;
1171
1173 switch (easing) {
1174 case BEZT_IPO_EASE_IN:
1175 return BLI_easing_expo_ease_in(time, begin, change, duration);
1176 case BEZT_IPO_EASE_OUT:
1177 return BLI_easing_expo_ease_out(time, begin, change, duration);
1179 return BLI_easing_expo_ease_in_out(time, begin, change, duration);
1180
1181 default:
1182 return BLI_easing_expo_ease_in(time, begin, change, duration);
1183 }
1184 break;
1185
1187 switch (easing) {
1188 case BEZT_IPO_EASE_IN:
1189 return BLI_easing_quad_ease_in(time, begin, change, duration);
1190 case BEZT_IPO_EASE_OUT:
1191 return BLI_easing_quad_ease_out(time, begin, change, duration);
1193 return BLI_easing_quad_ease_in_out(time, begin, change, duration);
1194
1195 default:
1196 return BLI_easing_quad_ease_in(time, begin, change, duration);
1197 }
1198 break;
1199
1201 switch (easing) {
1202 case BEZT_IPO_EASE_IN:
1203 return BLI_easing_quart_ease_in(time, begin, change, duration);
1204 case BEZT_IPO_EASE_OUT:
1205 return BLI_easing_quart_ease_out(time, begin, change, duration);
1207 return BLI_easing_quart_ease_in_out(time, begin, change, duration);
1208
1209 default:
1210 return BLI_easing_quart_ease_in(time, begin, change, duration);
1211 }
1212 break;
1213
1215 switch (easing) {
1216 case BEZT_IPO_EASE_IN:
1217 return BLI_easing_quint_ease_in(time, begin, change, duration);
1218 case BEZT_IPO_EASE_OUT:
1219 return BLI_easing_quint_ease_out(time, begin, change, duration);
1221 return BLI_easing_quint_ease_in_out(time, begin, change, duration);
1222
1223 default:
1224 return BLI_easing_quint_ease_in(time, begin, change, duration);
1225 }
1226 break;
1227
1229 switch (easing) {
1230 case BEZT_IPO_EASE_IN:
1231 return BLI_easing_sine_ease_in(time, begin, change, duration);
1232 case BEZT_IPO_EASE_OUT:
1233 return BLI_easing_sine_ease_out(time, begin, change, duration);
1235 return BLI_easing_sine_ease_in_out(time, begin, change, duration);
1236
1237 default:
1238 return BLI_easing_sine_ease_in(time, begin, change, duration);
1239 }
1240 break;
1241
1242 default:
1244 break;
1245 }
1246
1247 return time;
1248}
1249
1251{
1254
1256 if (op->customdata == nullptr) {
1257 return OPERATOR_FINISHED;
1258 }
1259 InterpolateOpData &opdata = *static_cast<InterpolateOpData *>(op->customdata);
1260
1261 const Scene &scene = *CTX_data_scene(C);
1262 const int current_frame = scene.r.cfra;
1263 Object &object = *CTX_data_active_object(C);
1264 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
1266 const InterpolationType type = InterpolationType(RNA_enum_get(op->ptr, "type"));
1267 const eBezTriple_Easing easing = eBezTriple_Easing(RNA_enum_get(op->ptr, "easing"));
1268 const float back_easing = RNA_float_get(op->ptr, "back");
1269 const float amplitude = RNA_float_get(op->ptr, "amplitude");
1270 const float period = RNA_float_get(op->ptr, "period");
1271 const int step = RNA_int_get(op->ptr, "step");
1272
1273 GP_Interpolate_Settings &ipo_settings = ts.gp_interpolate;
1274 if (ipo_settings.custom_ipo == nullptr) {
1275 ipo_settings.custom_ipo = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
1276 }
1277 BKE_curvemapping_init(ipo_settings.custom_ipo);
1278
1279 opdata.layer_mask.foreach_index([&](const int layer_index) {
1280 Layer &layer = grease_pencil.layer(layer_index);
1281 InterpolateOpData::LayerData &layer_data = opdata.layer_data[layer_index];
1282
1283 std::optional<FramesMapKeyIntervalT> interval = find_frames_interval(
1284 layer, current_frame, opdata.exclude_breakdowns);
1285 if (!interval) {
1286 return;
1287 }
1288
1289 const int frame_range_size = interval->second - interval->first + 1;
1290
1291 /* First and last frame are ignored. */
1292 for (int cframe = interval->first + step; cframe < interval->second; cframe += step) {
1293 ensure_drawing_at_exact_frame(grease_pencil, layer, layer_data, cframe);
1294 Drawing *dst_drawing = get_drawing_at_exact_frame(grease_pencil, layer, cframe);
1295 if (dst_drawing == nullptr) {
1296 return;
1297 }
1298
1299 const float base_factor = float(cframe - interval->first) /
1300 std::max(frame_range_size - 1, 1);
1301 const float mix_factor = grease_pencil_interpolate_sequence_easing_calc(
1302 easing, type, back_easing, amplitude, period, *ipo_settings.custom_ipo, base_factor);
1303
1304 bke::CurvesGeometry interpolated_curves = interpolate_between_curves(
1305 grease_pencil, layer, layer_data.curve_pairs, mix_factor, opdata.flipmode);
1306
1307 if (opdata.smooth_factor > 0.0f && opdata.smooth_steps > 0) {
1308 MutableSpan<float3> positions = interpolated_curves.positions_for_write();
1310 interpolated_curves.curves_range(),
1311 interpolated_curves.points_by_curve(),
1312 VArray<bool>::from_single(true, interpolated_curves.points_num()),
1313 interpolated_curves.cyclic(),
1314 opdata.smooth_steps,
1315 opdata.smooth_factor,
1316 false,
1317 true,
1318 positions);
1319 interpolated_curves.tag_positions_changed();
1320 }
1321
1322 dst_drawing->strokes_for_write() = std::move(interpolated_curves);
1323 dst_drawing->tag_topology_changed();
1324 }
1325 });
1326
1327 /* Notifiers */
1330
1331 MEM_delete(static_cast<InterpolateOpData *>(op->customdata));
1332 op->customdata = nullptr;
1333
1334 return OPERATOR_FINISHED;
1335}
1336
1338{
1339 uiLayout *layout = op->layout;
1340 uiLayout *col, *row;
1341
1342 const InterpolationType type = InterpolationType(RNA_enum_get(op->ptr, "type"));
1343
1344 layout->use_property_split_set(true);
1345 layout->use_property_decorate_set(false);
1346 row = &layout->row(true);
1347 row->prop(op->ptr, "step", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1348
1349 row = &layout->row(true);
1350 row->prop(op->ptr, "layers", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1351
1353 row = &layout->row(true);
1354 row->prop(op->ptr, "interpolate_selected_only", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1355 }
1356
1357 row = &layout->row(true);
1358 row->prop(op->ptr, "exclude_breakdowns", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1359
1360 row = &layout->row(true);
1361 row->prop(op->ptr, "use_selection", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1362
1363 row = &layout->row(true);
1364 row->prop(op->ptr, "flip", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1365
1366 col = &layout->column(true);
1367 col->prop(op->ptr, "smooth_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1368 col->prop(op->ptr, "smooth_steps", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1369
1370 row = &layout->row(true);
1371 row->prop(op->ptr, "type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1372
1373 if (type == InterpolationType::CurveMap) {
1374 /* Get an RNA pointer to ToolSettings to give to the custom curve. */
1375 Scene *scene = CTX_data_scene(C);
1376 ToolSettings *ts = scene->toolsettings;
1377 PointerRNA gpsettings_ptr = RNA_pointer_create_discrete(
1378 &scene->id, &RNA_GPencilInterpolateSettings, &ts->gp_interpolate);
1380 layout, &gpsettings_ptr, "interpolation_curve", 0, false, true, true, false, false);
1381 }
1382 else if (type != InterpolationType::Linear) {
1383 row = &layout->row(false);
1384 row->prop(op->ptr, "easing", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1385 if (type == InterpolationType::Back) {
1386 row = &layout->row(false);
1387 row->prop(op->ptr, "back", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1388 }
1389 else if (type == InterpolationType::Elastic) {
1390 row = &layout->row(false);
1391 row->prop(op->ptr, "amplitude", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1392 row = &layout->row(false);
1393 row->prop(op->ptr, "period", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1394 }
1395 }
1396}
1397
1399{
1400 PropertyRNA *prop;
1401
1402 ot->name = "Interpolate Sequence";
1403 ot->idname = "GREASE_PENCIL_OT_interpolate_sequence";
1404 ot->translation_context = BLT_I18NCONTEXT_ID_GPENCIL;
1405 ot->description = "Generate 'in-betweens' to smoothly interpolate between Grease Pencil frames";
1406
1410
1411 RNA_def_int(ot->srna,
1412 "step",
1413 1,
1414 1,
1415 MAXFRAME,
1416 "Step",
1417 "Number of frames between generated interpolated frames",
1418 1,
1419 MAXFRAME);
1420
1421 RNA_def_enum(ot->srna,
1422 "layers",
1424 0,
1425 "Layer",
1426 "Layers included in the interpolation");
1427
1428 RNA_def_boolean(ot->srna,
1429 "exclude_breakdowns",
1430 false,
1431 "Exclude Breakdowns",
1432 "Exclude existing Breakdowns keyframes as interpolation extremes");
1433
1434 RNA_def_boolean(ot->srna,
1435 "use_selection",
1436 false,
1437 "Use Selection",
1438 "Use only selected strokes for interpolating");
1439
1440 RNA_def_enum(ot->srna,
1441 "flip",
1443 int(InterpolateFlipMode::FlipAuto),
1444 "Flip Mode",
1445 "Invert destination stroke to match start and end with source stroke");
1446
1447 RNA_def_int(ot->srna,
1448 "smooth_steps",
1449 1,
1450 1,
1451 3,
1452 "Iterations",
1453 "Number of times to smooth newly created strokes",
1454 1,
1455 3);
1456
1457 RNA_def_float(ot->srna,
1458 "smooth_factor",
1459 0.0f,
1460 0.0f,
1461 2.0f,
1462 "Smooth",
1463 "Amount of smoothing to apply to interpolated strokes, to reduce jitter/noise",
1464 0.0f,
1465 2.0f);
1466
1467 prop = RNA_def_enum(ot->srna,
1468 "type",
1470 0,
1471 "Type",
1472 "Interpolation method to use the next time 'Interpolate Sequence' is run");
1474
1475 prop = RNA_def_enum(
1476 ot->srna,
1477 "easing",
1480 "Easing",
1481 "Which ends of the segment between the preceding and following Grease Pencil frames "
1482 "easing interpolation is applied to");
1484
1485 prop = RNA_def_float(ot->srna,
1486 "back",
1487 1.702f,
1488 0.0f,
1489 FLT_MAX,
1490 "Back",
1491 "Amount of overshoot for 'back' easing",
1492 0.0f,
1493 FLT_MAX);
1495
1496 RNA_def_float(ot->srna,
1497 "amplitude",
1498 0.15f,
1499 0.0f,
1500 FLT_MAX,
1501 "Amplitude",
1502 "Amount to boost elastic bounces for 'elastic' easing",
1503 0.0f,
1504 FLT_MAX);
1505
1506 RNA_def_float(ot->srna,
1507 "period",
1508 0.15f,
1509 -FLT_MAX,
1510 FLT_MAX,
1511 "Period",
1512 "Time between bounces for elastic easing",
1513 -FLT_MAX,
1514 FLT_MAX);
1515
1516 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1517}
1518
1520
1521} // namespace blender::ed::sculpt_paint::greasepencil
1522
1523/* -------------------------------------------------------------------- */
1526
1533
1535{
1537 static const EnumPropertyItem modal_items[] = {
1538 {int(InterpolateToolModalEvent::Cancel), "CANCEL", 0, "Cancel", ""},
1539 {int(InterpolateToolModalEvent::Confirm), "CONFIRM", 0, "Confirm", ""},
1540 {int(InterpolateToolModalEvent::Increase), "INCREASE", 0, "Increase", ""},
1541 {int(InterpolateToolModalEvent::Decrease), "DECREASE", 0, "Decrease", ""},
1542 {0, nullptr, 0, nullptr, nullptr},
1543 };
1544
1545 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Interpolate Tool Modal Map");
1546
1547 /* This function is called for each space-type, only needs to add map once. */
1548 if (keymap && keymap->modal_items) {
1549 return;
1550 }
1551
1552 keymap = WM_modalkeymap_ensure(keyconf, "Interpolate Tool Modal Map", modal_items);
1553
1554 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_interpolate");
1555}
1556
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
void BKE_curvemapping_init(CurveMapping *cumap)
CurveMapping * BKE_curvemapping_add(int tot, float minx, float miny, float maxx, float maxy)
Definition colortools.cc:89
@ CTX_MODE_EDIT_GPENCIL_LEGACY
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
enum eContextObjectMode CTX_data_mode_enum(const bContext *C)
Low-level operations for curves.
support for deformation groups and hooks.
void BKE_defgroup_copy_list(ListBase *outbase, const ListBase *inbase)
Definition deform.cc:73
Low-level operations for grease pencil.
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
float BLI_easing_sine_ease_in(float time, float begin, float change, float duration)
Definition easing.cc:348
float BLI_easing_back_ease_out(float time, float begin, float change, float duration, float overshoot)
Definition easing.cc:25
float BLI_easing_bounce_ease_in_out(float time, float begin, float change, float duration)
Definition easing.cc:66
float BLI_easing_quint_ease_out(float time, float begin, float change, float duration)
Definition easing.cc:334
float BLI_easing_quart_ease_in_out(float time, float begin, float change, float duration)
Definition easing.cc:320
float BLI_easing_circ_ease_in_out(float time, float begin, float change, float duration)
Definition easing.cc:87
float BLI_easing_quart_ease_out(float time, float begin, float change, float duration)
Definition easing.cc:314
float BLI_easing_bounce_ease_in(float time, float begin, float change, float duration)
Definition easing.cc:61
float BLI_easing_circ_ease_in(float time, float begin, float change, float duration)
Definition easing.cc:75
float BLI_easing_expo_ease_in(float time, float begin, float change, float duration)
Definition easing.cc:255
float BLI_easing_expo_ease_out(float time, float begin, float change, float duration)
Definition easing.cc:263
float BLI_easing_elastic_ease_in(float time, float begin, float change, float duration, float amplitude, float period)
Definition easing.cc:146
float BLI_easing_quad_ease_in_out(float time, float begin, float change, float duration)
Definition easing.cc:299
float BLI_easing_elastic_ease_out(float time, float begin, float change, float duration, float amplitude, float period)
Definition easing.cc:179
float BLI_easing_cubic_ease_in(float time, float begin, float change, float duration)
Definition easing.cc:96
float BLI_easing_elastic_ease_in_out(float time, float begin, float change, float duration, float amplitude, float period)
Definition easing.cc:211
float BLI_easing_quint_ease_in(float time, float begin, float change, float duration)
Definition easing.cc:329
float BLI_easing_sine_ease_out(float time, float begin, float change, float duration)
Definition easing.cc:353
float BLI_easing_bounce_ease_out(float time, float begin, float change, float duration)
Definition easing.cc:43
float BLI_easing_quad_ease_in(float time, float begin, float change, float duration)
Definition easing.cc:287
float BLI_easing_quad_ease_out(float time, float begin, float change, float duration)
Definition easing.cc:293
float BLI_easing_circ_ease_out(float time, float begin, float change, float duration)
Definition easing.cc:81
float BLI_easing_cubic_ease_out(float time, float begin, float change, float duration)
Definition easing.cc:102
float BLI_easing_back_ease_in(float time, float begin, float change, float duration, float overshoot)
Definition easing.cc:18
float BLI_easing_quint_ease_in_out(float time, float begin, float change, float duration)
Definition easing.cc:339
float BLI_easing_expo_ease_in_out(float time, float begin, float change, float duration)
Definition easing.cc:271
float BLI_easing_quart_ease_in(float time, float begin, float change, float duration)
Definition easing.cc:308
float BLI_easing_back_ease_in_out(float time, float begin, float change, float duration, float overshoot)
Definition easing.cc:32
float BLI_easing_cubic_ease_in_out(float time, float begin, float change, float duration)
Definition easing.cc:108
float BLI_easing_sine_ease_in_out(float time, float begin, float change, float duration)
Definition easing.cc:358
#define DEG2RADF(_deg)
int isect_seg_seg_v2(const float v1[2], const float v2[2], const float v3[2], const float v4[2])
#define ISECT_LINE_LINE_CROSS
#define CTX_N_(context, msgid)
#define BLT_I18NCONTEXT_ID_GPENCIL
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ BEZT_IPO_LIN
eBezTriple_Easing
@ BEZT_IPO_EASE_OUT
@ BEZT_IPO_EASE_IN
@ BEZT_IPO_EASE_IN_OUT
eBezTriple_KeyframeType
@ BEZT_KEYTYPE_BREAKDOWN
#define MAXFRAME
@ SPACE_VIEW3D
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_interpolatetool_modal_keymap(wmKeyConfig *keyconf)
void ED_operatortypes_grease_pencil_interpolate()
#define NUM_STR_REP_LEN
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
Definition numinput.cc:312
bool applyNumInput(NumInput *n, float *vec)
Definition numinput.cc:190
void outputNumInput(NumInput *n, char *str, const UnitSettings &unit_settings)
Definition numinput.cc:88
bool hasNumInput(const NumInput *n)
Definition numinput.cc:171
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:851
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1024
Read Guarded memory(de)allocation.
#define RNA_ENUM_ITEM_HEADING(name, description)
Definition RNA_types.hh:673
#define C
Definition RandGen.cpp:29
void uiTemplateCurveMapping(uiLayout *layout, PointerRNA *ptr, blender::StringRefNull propname, int type, bool levels, bool brush, bool neg_slope, bool tone, bool presets)
#define UI_ITEM_NONE
#define ND_DATA
Definition WM_types.hh:509
@ KM_PRESS
Definition WM_types.hh:311
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
#define NC_GPENCIL
Definition WM_types.hh:399
iter begin(iter)
BMesh const char void * data
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
Span< T > as_span() const
Definition BLI_array.hh:243
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
const T * end() const
Definition BLI_array.hh:325
void fill(const T &value) const
Definition BLI_array.hh:272
const T * begin() const
Definition BLI_array.hh:321
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
static void foreach_segment_zipped(Span< IndexMask > masks, FunctionRef< bool(Span< IndexMaskSegment > segments)> fn)
constexpr int64_t first() const
constexpr int64_t size() const
constexpr bool is_empty() const
static constexpr IndexRange from_single(const int64_t index)
constexpr IndexRange index_range() const
const int * SortedKeysIterator
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
int64_t size() const
IndexRange index_range() const
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr const T * begin() const
Definition BLI_span.hh:220
static VArray from_single(T value, const int64_t size)
int64_t size() const
void append(const T &value)
bool is_empty() const
void resize(const int64_t new_size)
MutableSpan< T > as_mutable_span()
void append_n_times(const T &value, const int64_t n)
MutableSpan< float3 > positions_for_write()
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
Span< float3 > positions() const
MutableSpan< int > offsets_for_write()
VArray< bool > cyclic() const
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
SortedKeysIterator sorted_keys_iterator_at(int frame_number) const
const GreasePencilFrame * frame_at(const int frame_number) const
bool has_drawing_at(const int frame_number) const
std::optional< int > start_frame_at(int frame_number) const
Span< FramesMapKeyT > sorted_keys() const
IndexMask slice(IndexRange range) const
void foreach_index(Fn &&fn) const
nullptr float
uint col
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
void fill_index_range(MutableSpan< T > span, const T start=0)
static bool has_anything_selected(const Span< Curves * > curves_ids)
IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
bool active_grease_pencil_poll(bContext *C)
static float grease_pencil_interpolate_sequence_easing_calc(const eBezTriple_Easing easing, const InterpolationType type, const float back_easing, const float amplitude, const float period, const CurveMapping &custom_ipo, const float time)
static void grease_pencil_interpolate_exit(bContext &C, wmOperator &op)
static bool find_curve_mapping_from_index(const GreasePencil &grease_pencil, const bke::greasepencil::Layer &layer, const int current_frame, const bool exclude_breakdowns, const bool only_selected, InterpolationPairs &pairs)
static bke::CurvesGeometry interpolate_between_curves(const GreasePencil &grease_pencil, const bke::greasepencil::Layer &layer, const InterpolationPairs &curve_pairs, const float mix_factor, const InterpolateFlipMode flip_mode)
static void grease_pencil_interpolate_cancel(bContext *C, wmOperator *op)
static wmOperatorStatus grease_pencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void grease_pencil_interpolate_status_indicators(bContext &C, const InterpolateOpData &opdata)
static wmOperatorStatus grease_pencil_interpolate_sequence_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem grease_pencil_interpolate_layer_items[]
static bool grease_pencil_interpolate_init(const bContext &C, wmOperator &op)
static const EnumPropertyItem grease_pencil_interpolate_flip_mode_items[]
static bool compute_auto_flip(const Span< float3 > from_positions, const Span< float3 > to_positions)
static const EnumPropertyItem grease_pencil_interpolation_type_items[]
static void grease_pencil_interpolate_sequence_ui(bContext *C, wmOperator *op)
static wmOperatorStatus grease_pencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent *event)
static bke::greasepencil::Drawing * get_drawing_at_exact_frame(GreasePencil &grease_pencil, bke::greasepencil::Layer &layer, const int frame_number)
static void grease_pencil_interpolate_update(bContext &C, const wmOperator &op)
static void GREASE_PENCIL_OT_interpolate_sequence(wmOperatorType *ot)
static std::optional< FramesMapKeyIntervalT > find_frames_interval(const bke::greasepencil::Layer &layer, const int frame_number, const bool exclude_breakdowns)
static void grease_pencil_interpolate_restore(bContext &C, wmOperator &op)
static void GREASE_PENCIL_OT_interpolate(wmOperatorType *ot)
static Vector< int > find_curve_pair_offsets(const InterpolationPairs &curve_pairs, const Span< int > order)
static bke::greasepencil::Drawing * ensure_drawing_at_exact_frame(GreasePencil &grease_pencil, bke::greasepencil::Layer &layer, InterpolateOpData::LayerData &layer_data, const int frame_number)
void interpolate_curves_with_samples(const bke::CurvesGeometry &from_curves, const bke::CurvesGeometry &to_curves, Span< int > from_curve_indices, Span< int > to_curve_indices, Span< int > from_sample_indices, Span< int > to_sample_indices, Span< float > from_sample_factors, Span< float > to_sample_factors, const IndexMask &dst_curve_mask, float mix_factor, bke::CurvesGeometry &dst_curves, IndexMaskMemory &memory)
void sample_curve_padded(const Span< float3 > positions, bool cyclic, MutableSpan< int > r_indices, MutableSpan< float > r_factors)
void smooth_curve_attribute(const IndexMask &curves_to_smooth, const OffsetIndices< int > points_by_curve, const VArray< bool > &point_selection, const VArray< bool > &cyclic, int iterations, float influence, bool smooth_ends, bool keep_shape, GMutableSpan attribute_data)
AngleRadianBase< T > angle_between(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
VecBase< float, 3 > float3
const int status
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float_factor(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
const EnumPropertyItem rna_enum_beztriple_interpolation_easing_items[]
#define FLT_MAX
Definition stdcycles.h:14
ListBase vertex_group_names
struct CurveMapping * custom_ipo
struct ToolSettings * toolsettings
struct RenderData r
struct UnitSettings unit
struct GP_Interpolate_Settings gp_interpolate
static InterpolateOpData * from_operator(const bContext &C, const wmOperator &op)
void use_property_decorate_set(bool is_sep)
uiLayout & column(bool align)
uiLayout & row(bool align)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
const void * modal_items
struct uiLayout * layout
struct PointerRNA * ptr
i
Definition text_draw.cc:230
#define N_(msgid)
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_EW_SCROLL
Definition wm_cursors.hh:54
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ EVT_MODAL_MAP
@ MOUSEMOVE
wmOperatorType * ot
Definition wm_files.cc:4237
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:932
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:959
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))