Blender V4.5
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"
45#include "UI_resources.hh"
46
47#include <climits>
48
50
53
54/* -------------------------------------------------------------------- */
57
58/* Modes for the interpolation tool. */
76
82 /* Interpolation. */
84 N_("Standard transitions between keyframes")),
86 "LINEAR",
87 ICON_IPO_LINEAR,
88 "Linear",
89 "Straight-line interpolation between A and B (i.e. no ease in/out)"},
91 "CUSTOM",
92 ICON_IPO_BEZIER,
93 "Custom",
94 "Custom interpolation defined using a curve map"},
95
96 /* Easing. */
98 N_("Predefined inertial transitions, useful for motion graphics "
99 "(from least to most \"dramatic\")")),
101 "SINE",
102 ICON_IPO_SINE,
103 "Sinusoidal",
104 "Sinusoidal easing (weakest, almost linear but with a slight curvature)"},
105 {int(InterpolationType::Quadratic), "QUAD", ICON_IPO_QUAD, "Quadratic", "Quadratic easing"},
106 {int(InterpolationType::Cubic), "CUBIC", ICON_IPO_CUBIC, "Cubic", "Cubic easing"},
107 {int(InterpolationType::Quartic), "QUART", ICON_IPO_QUART, "Quartic", "Quartic easing"},
108 {int(InterpolationType::Quintic), "QUINT", ICON_IPO_QUINT, "Quintic", "Quintic easing"},
110 "EXPO",
111 ICON_IPO_EXPO,
112 "Exponential",
113 "Exponential easing (dramatic)"},
115 "CIRC",
116 ICON_IPO_CIRC,
117 "Circular",
118 "Circular easing (strongest and most dynamic)"},
119
121 N_("Simple physics-inspired easing effects")),
123 "BACK",
124 ICON_IPO_BACK,
125 "Back",
126 "Cubic easing with overshoot and settle"},
128 "BOUNCE",
129 ICON_IPO_BOUNCE,
130 "Bounce",
131 "Exponentially decaying parabolic bounce, like when objects collide"},
133 "ELASTIC",
134 ICON_IPO_ELASTIC,
135 "Elastic",
136 "Exponentially decaying sine wave, like an elastic band"},
137
138 {0, nullptr, 0, nullptr, nullptr},
139};
140
142 {int(InterpolateFlipMode::None), "NONE", 0, "No Flip", ""},
143 {int(InterpolateFlipMode::Flip), "FLIP", 0, "Flip", ""},
144 {int(InterpolateFlipMode::FlipAuto), "AUTO", 0, "Automatic", ""},
145 {0, nullptr, 0, nullptr, nullptr},
146};
147
149 {int(InterpolateLayerMode::Active), "ACTIVE", 0, "Active", ""},
150 {int(InterpolateLayerMode::All), "ALL", 0, "All Layers", ""},
151 {0, nullptr, 0, nullptr, nullptr},
152};
153
154constexpr float interpolate_factor_min = -1.0f;
155constexpr float interpolate_factor_max = 2.0f;
156
157/* Pair of curves in a layer that get interpolated. */
164
166 struct LayerData {
167 /* Curve pairs to interpolate from this layer. */
169
170 /* Geometry of the target frame before interpolation for restoring on cancel. */
171 std::optional<bke::CurvesGeometry> orig_curves;
172 };
173
174 /* Layers to include. */
177 /* Exclude breakdown keyframes when finding intervals. */
179
180 /* Interpolation factor bias controlled by the user. */
181 float shift;
182 /* Interpolation base factor for the active layer. */
187
191
192 static InterpolateOpData *from_operator(const bContext &C, const wmOperator &op);
193};
194
195using FramesMapKeyIntervalT = std::pair<int, int>;
196
197static std::optional<FramesMapKeyIntervalT> find_frames_interval(
198 const bke::greasepencil::Layer &layer, const int frame_number, const bool exclude_breakdowns)
199{
202 using SortedKeysIterator = Layer::SortedKeysIterator;
203
204 const Span<FramesMapKeyT> sorted_keys = layer.sorted_keys();
205 SortedKeysIterator prev_key_it = layer.sorted_keys_iterator_at(frame_number);
206 if (!prev_key_it) {
207 return std::nullopt;
208 }
209 SortedKeysIterator next_key_it = std::next(prev_key_it);
210
211 /* Skip over invalid keyframes on either side. */
212 auto is_valid_keyframe = [&](const FramesMapKeyT key) {
213 const GreasePencilFrame *frame = layer.frame_at(key);
214 if (!frame || frame->is_end()) {
215 return false;
216 }
217 if (exclude_breakdowns && frame->type == BEZT_KEYTYPE_BREAKDOWN) {
218 return false;
219 }
220 return true;
221 };
222
223 for (; next_key_it != sorted_keys.end(); ++next_key_it) {
224 if (is_valid_keyframe(*next_key_it)) {
225 break;
226 }
227 }
228 for (; prev_key_it != sorted_keys.begin(); --prev_key_it) {
229 if (is_valid_keyframe(*prev_key_it)) {
230 break;
231 }
232 }
233 if (next_key_it == sorted_keys.end() || !is_valid_keyframe(*prev_key_it)) {
234 return std::nullopt;
235 }
236
237 return std::make_pair(*prev_key_it, *next_key_it);
238}
239
240/* Build index lists for curve interpolation using index. */
241static bool find_curve_mapping_from_index(const GreasePencil &grease_pencil,
242 const bke::greasepencil::Layer &layer,
243 const int current_frame,
244 const bool exclude_breakdowns,
245 const bool only_selected,
246 InterpolationPairs &pairs)
247{
249
250 const std::optional<FramesMapKeyIntervalT> interval = find_frames_interval(
251 layer, current_frame, exclude_breakdowns);
252 if (!interval) {
253 return false;
254 }
255
256 BLI_assert(layer.has_drawing_at(interval->first));
257 BLI_assert(layer.has_drawing_at(interval->second));
258 const Drawing &from_drawing = *grease_pencil.get_drawing_at(layer, interval->first);
259 const Drawing &to_drawing = *grease_pencil.get_drawing_at(layer, interval->second);
260 /* In addition to interpolated pairs, the unselected original strokes are also included, making
261 * the total pair count the same as the "from" curve count. */
262 const int pairs_num = from_drawing.strokes().curves_num();
263
264 const int old_pairs_num = pairs.from_frames.size();
265 pairs.from_frames.append_n_times(interval->first, pairs_num);
266 pairs.to_frames.append_n_times(interval->second, pairs_num);
267 pairs.from_curves.resize(old_pairs_num + pairs_num);
268 pairs.to_curves.resize(old_pairs_num + pairs_num);
269 MutableSpan<int> from_curves = pairs.from_curves.as_mutable_span().slice(old_pairs_num,
270 pairs_num);
271 MutableSpan<int> to_curves = pairs.to_curves.as_mutable_span().slice(old_pairs_num, pairs_num);
272
273 /* Write source indices into the pair data. If one drawing has more selected curves than the
274 * other the remainder is ignored. */
275
276 IndexMaskMemory memory;
277 IndexMask from_selection, to_selection;
278 if (only_selected && ed::curves::has_anything_selected(from_drawing.strokes()) &&
280 {
281 from_selection = ed::curves::retrieve_selected_curves(from_drawing.strokes(), memory);
282 to_selection = ed::curves::retrieve_selected_curves(to_drawing.strokes(), memory);
283 }
284 else {
285 from_selection = from_drawing.strokes().curves_range();
286 to_selection = to_drawing.strokes().curves_range();
287 }
288 /* Discard additional elements of the larger selection. */
289 if (from_selection.size() > to_selection.size()) {
290 from_selection = from_selection.slice(0, to_selection.size());
291 }
292 else if (to_selection.size() > from_selection.size()) {
293 to_selection = to_selection.slice(0, from_selection.size());
294 }
295
296 /* By default: copy the "from" curve and ignore the "to" curve. */
298 to_curves.fill(-1);
299 /* Selected curves are interpolated. */
300 IndexMask::foreach_segment_zipped({from_selection, to_selection},
301 [&](Span<IndexMaskSegment> segments) {
302 const IndexMaskSegment &from_segment = segments[0];
303 const IndexMaskSegment &to_segment = segments[1];
304 BLI_assert(from_segment.size() == to_segment.size());
305 for (const int i : from_segment.index_range()) {
306 to_curves[from_segment[i]] = to_segment[i];
307 }
308 return true;
309 });
310
311 return true;
312}
313
315{
318
319 const Scene &scene = *CTX_data_scene(&C);
320 const int current_frame = scene.r.cfra;
321 const Object &object = *CTX_data_active_object(&C);
322 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
323
324 if (!grease_pencil.has_active_layer()) {
325 return nullptr;
326 }
327
328 const Layer &active_layer = *grease_pencil.get_active_layer();
329
330 InterpolateOpData *data = MEM_new<InterpolateOpData>(__func__);
331
332 if (RNA_struct_find_property(op.ptr, "shift") != nullptr) {
333 data->shift = RNA_float_get(op.ptr, "shift");
334 }
335 data->exclude_breakdowns = RNA_boolean_get(op.ptr, "exclude_breakdowns");
336 data->flipmode = InterpolateFlipMode(RNA_enum_get(op.ptr, "flip"));
337 data->smooth_factor = RNA_float_get(op.ptr, "smooth_factor");
338 data->smooth_steps = RNA_int_get(op.ptr, "smooth_steps");
339 data->active_layer_index = *grease_pencil.get_layer_index(active_layer);
340 const bool use_selection = RNA_boolean_get(op.ptr, "use_selection");
341
342 const auto layer_mode = InterpolateLayerMode(RNA_enum_get(op.ptr, "layers"));
343 switch (layer_mode) {
344 case InterpolateLayerMode::Active:
345 data->layer_mask = IndexRange::from_single(data->active_layer_index);
346 break;
347 case InterpolateLayerMode::All:
348 data->layer_mask = IndexMask::from_predicate(
349 grease_pencil.layers().index_range(),
350 GrainSize(1024),
351 data->layer_mask_memory,
352 [&](const int layer_index) { return grease_pencil.layer(layer_index).is_editable(); });
353 break;
354 }
355
356 bool found_mapping = false;
357 data->layer_data.reinitialize(grease_pencil.layers().size());
358 data->layer_mask.foreach_index([&](const int layer_index) {
359 const Layer &layer = grease_pencil.layer(layer_index);
360 InterpolateOpData::LayerData &layer_data = data->layer_data[layer_index];
361
362 /* Pair from/to curves by index. */
363 const bool has_curve_mapping = find_curve_mapping_from_index(grease_pencil,
364 layer,
365 current_frame,
366 data->exclude_breakdowns,
367 use_selection,
368 layer_data.curve_pairs);
369 found_mapping = found_mapping || has_curve_mapping;
370 });
371
372 /* No mapping between frames was found. */
373 if (!found_mapping) {
374 MEM_delete(data);
375 return nullptr;
376 }
377
378 const std::optional<FramesMapKeyIntervalT> active_layer_interval = find_frames_interval(
379 active_layer, current_frame, data->exclude_breakdowns);
380 data->init_factor = active_layer_interval ?
381 float(current_frame - active_layer_interval->first) /
382 (active_layer_interval->second - active_layer_interval->first + 1) :
383 0.5f;
384
385 return data;
386}
387
388/* Find ranges of sorted pairs with the same from/to frame intervals. */
390 const Span<int> order)
391{
392 Vector<int> pair_offsets;
393
394 int prev_from_frame = INT_MIN;
395 int prev_to_frame = INT_MIN;
396 int current_count = 0;
397 for (const int pair_index : order) {
398 const int from_frame = curve_pairs.from_frames[pair_index];
399 const int to_frame = curve_pairs.to_frames[pair_index];
400 if (from_frame != prev_from_frame || to_frame != prev_to_frame) {
401 /* New pair. */
402 if (current_count > 0) {
403 pair_offsets.append(current_count);
404 }
405 current_count = 0;
406 }
407 ++current_count;
408 }
409 if (current_count > 0) {
410 pair_offsets.append(current_count);
411 }
412
413 /* Last entry for overall size. */
414 if (pair_offsets.is_empty()) {
415 return {};
416 }
417
418 /* Extra element for the total size needed for OffsetIndices. */
419 pair_offsets.append(0);
421
422 return pair_offsets;
423}
424
425static bool compute_auto_flip(const Span<float3> from_positions, const Span<float3> to_positions)
426{
427 if (from_positions.size() < 2 || to_positions.size() < 2) {
428 return false;
429 }
430
431 constexpr float min_angle = DEG2RADF(15);
432
433 const float3 &from_first = from_positions.first();
434 const float3 &from_last = from_positions.last();
435 const float3 &to_first = to_positions.first();
436 const float3 &to_last = to_positions.last();
437
438 /* If lines intersect at a sharp angle check distances. */
439 if (isect_seg_seg_v2(from_first, to_first, from_last, to_last) == ISECT_LINE_LINE_CROSS) {
440 if (math::angle_between(math::normalize(to_first - from_first),
441 math::normalize(to_last - from_last))
442 .radian() < min_angle)
443 {
444 if (math::distance_squared(from_first, to_first) >=
445 math::distance_squared(from_last, to_first))
446 {
447 return math::distance_squared(from_last, to_first) >=
448 math::distance_squared(from_last, to_last);
449 }
450
451 return math::distance_squared(from_first, to_first) <
452 math::distance_squared(from_first, to_last);
453 }
454
455 return true;
456 }
457
458 return math::dot(from_last - from_first, to_last - to_first) < 0.0f;
459}
460
462 const bke::greasepencil::Layer &layer,
463 const InterpolationPairs &curve_pairs,
464 const float mix_factor,
465 const InterpolateFlipMode flip_mode)
466{
468
469 const int dst_curve_num = curve_pairs.from_curves.size();
470 BLI_assert(curve_pairs.to_curves.size() == dst_curve_num);
471 BLI_assert(curve_pairs.from_frames.size() == dst_curve_num);
472 BLI_assert(curve_pairs.to_frames.size() == dst_curve_num);
473
474 /* Sort pairs by unique to/from frame combinations.
475 * Curves for each frame pair are then interpolated together.
476 * Map entries are indices into the original curve_pairs array,
477 * so the order of strokes can be maintained. */
478 Array<int> sorted_pairs(dst_curve_num);
480 std::sort(sorted_pairs.begin(), sorted_pairs.end(), [&](const int a, const int b) {
481 const int from_frame_a = curve_pairs.from_frames[a];
482 const int to_frame_a = curve_pairs.to_frames[a];
483 const int from_frame_b = curve_pairs.from_frames[b];
484 const int to_frame_b = curve_pairs.to_frames[b];
485 return from_frame_a < from_frame_b ||
486 (from_frame_a == from_frame_b && to_frame_a < to_frame_b);
487 });
488
489 /* Find ranges of sorted pairs with the same from/to frame intervals. */
490 Vector<int> pair_offsets = find_curve_pair_offsets(curve_pairs, sorted_pairs);
491 const OffsetIndices<int> curves_by_pair(pair_offsets);
492
493 /* Compute curve length and flip mode for each pair. */
494 Array<int> dst_curve_offsets(curves_by_pair.size() + 1, 0);
495 Array<bool> dst_curve_flip(curves_by_pair.size(), false);
496 const OffsetIndices<int> dst_points_by_curve = [&]() {
497 /* Last entry for overall size. */
498 if (curves_by_pair.is_empty()) {
499 return OffsetIndices<int>{};
500 }
501
502 for (const int pair_range_i : curves_by_pair.index_range()) {
503 const IndexRange pair_range = curves_by_pair[pair_range_i];
504 BLI_assert(!pair_range.is_empty());
505
506 const int first_pair_index = sorted_pairs[pair_range.first()];
507 const int from_frame = curve_pairs.from_frames[first_pair_index];
508 const int to_frame = curve_pairs.to_frames[first_pair_index];
509 const Drawing *from_drawing = grease_pencil.get_drawing_at(layer, from_frame);
510 const Drawing *to_drawing = grease_pencil.get_drawing_at(layer, to_frame);
511 if (!from_drawing || !to_drawing) {
512 continue;
513 }
514 const OffsetIndices from_points_by_curve = from_drawing->strokes().points_by_curve();
515 const OffsetIndices to_points_by_curve = to_drawing->strokes().points_by_curve();
516 const Span<float3> from_positions = from_drawing->strokes().positions();
517 const Span<float3> to_positions = to_drawing->strokes().positions();
518
519 for (const int sorted_index : pair_range) {
520 const int pair_index = sorted_pairs[sorted_index];
521 const int from_curve = curve_pairs.from_curves[pair_index];
522 const int to_curve = curve_pairs.to_curves[pair_index];
523
524 int curve_size = 0;
525 bool curve_flip = false;
526 if (from_curve < 0 && to_curve < 0) {
527 /* No output curve. */
528 }
529 else if (from_curve < 0) {
530 const IndexRange to_points = to_points_by_curve[to_curve];
531 curve_size = to_points.size();
532 curve_flip = false;
533 }
534 else if (to_curve < 0) {
535 const IndexRange from_points = from_points_by_curve[from_curve];
536 curve_size = from_points.size();
537 curve_flip = false;
538 }
539 else {
540 const IndexRange from_points = from_points_by_curve[from_curve];
541 const IndexRange to_points = to_points_by_curve[to_curve];
542
543 curve_size = std::max(from_points.size(), to_points.size());
544 switch (flip_mode) {
545 case InterpolateFlipMode::None:
546 curve_flip = false;
547 break;
548 case InterpolateFlipMode::Flip:
549 curve_flip = true;
550 break;
551 case InterpolateFlipMode::FlipAuto: {
552 curve_flip = compute_auto_flip(from_positions.slice(from_points),
553 to_positions.slice(to_points));
554 break;
555 }
556 }
557 }
558
559 dst_curve_offsets[pair_index] = curve_size;
560 dst_curve_flip[pair_index] = curve_flip;
561 }
562 }
563 return offset_indices::accumulate_counts_to_offsets(dst_curve_offsets);
564 }();
565 const int dst_point_num = dst_points_by_curve.total_size();
566
567 bke::CurvesGeometry dst_curves(dst_point_num, dst_curve_num);
568 /* Offsets are empty when there are no curves. */
569 if (dst_curve_num > 0) {
570 dst_curves.offsets_for_write().copy_from(dst_curve_offsets);
571 }
572
573 /* Copy vertex group names since we still have other parts of the code depends on vertex group
574 * names to be available. */
576
577 /* Sorted map arrays that can be passed to the interpolation function directly.
578 * These index maps have the same order as the sorted indices, so slices of indices can be used
579 * for interpolating all curves of a frame pair at once. */
580 Array<int> from_curve_buffer(dst_curve_num);
581 Array<int> to_curve_buffer(dst_curve_num);
582 Array<int> from_sample_indices(dst_point_num);
583 Array<int> to_sample_indices(dst_point_num);
584 Array<float> from_sample_factors(dst_point_num);
585 Array<float> to_sample_factors(dst_point_num);
586 IndexMaskMemory memory;
587
588 for (const int pair_range_i : curves_by_pair.index_range()) {
589 const IndexRange pair_range = curves_by_pair[pair_range_i];
590 /* Subset of target curves that are filled by this frame pair. Selection is built from pair
591 * indices, which correspond to dst curve indices. */
592 const IndexMask dst_curve_mask = IndexMask::from_indices(
593 sorted_pairs.as_span().slice(pair_range), memory);
594 MutableSpan<int> from_indices = from_curve_buffer.as_mutable_span().slice(pair_range);
595 MutableSpan<int> to_indices = to_curve_buffer.as_mutable_span().slice(pair_range);
596
597 const int first_pair_index = sorted_pairs[pair_range.first()];
598 const int from_frame = curve_pairs.from_frames[first_pair_index];
599 const int to_frame = curve_pairs.to_frames[first_pair_index];
600 const Drawing *from_drawing = grease_pencil.get_drawing_at(layer, from_frame);
601 const Drawing *to_drawing = grease_pencil.get_drawing_at(layer, to_frame);
602 if (!from_drawing || !to_drawing) {
603 continue;
604 }
605 const OffsetIndices from_points_by_curve = from_drawing->strokes().points_by_curve();
606 const OffsetIndices to_points_by_curve = to_drawing->strokes().points_by_curve();
607 const VArray<bool> from_curves_cyclic = from_drawing->strokes().cyclic();
608 const VArray<bool> to_curves_cyclic = to_drawing->strokes().cyclic();
609
610 for (const int i : pair_range.index_range()) {
611 const int pair_index = sorted_pairs[pair_range[i]];
612 const IndexRange dst_points = dst_points_by_curve[pair_index];
613 from_indices[i] = curve_pairs.from_curves[pair_index];
614 to_indices[i] = curve_pairs.to_curves[pair_index];
615
616 const int from_curve = curve_pairs.from_curves[pair_index];
617 const int to_curve = curve_pairs.to_curves[pair_index];
618
619 BLI_assert(from_curve >= 0 || to_curve >= 0);
620 if (to_curve < 0) {
621 /* Copy "from" curve. */
622 array_utils::fill_index_range(from_sample_indices.as_mutable_span().slice(dst_points));
623 from_sample_factors.fill(0.0f);
624 continue;
625 }
626 if (from_curve < 0) {
627 /* Copy "to" curve. */
628 array_utils::fill_index_range(to_sample_indices.as_mutable_span().slice(dst_points));
629 to_sample_factors.fill(0.0f);
630 continue;
631 }
632
633 const IndexRange from_points = from_points_by_curve[from_curve];
634 const IndexRange to_points = to_points_by_curve[to_curve];
635 if (from_points.size() >= to_points.size()) {
636 /* Target curve samples match 'from' points. */
637 BLI_assert(from_points.size() == dst_points.size());
638 array_utils::fill_index_range(from_sample_indices.as_mutable_span().slice(dst_points));
639 from_sample_factors.as_mutable_span().slice(dst_points).fill(0.0f);
641 to_curve,
642 to_curves_cyclic[to_curve],
643 dst_curve_flip[pair_index],
644 to_sample_indices.as_mutable_span().slice(dst_points),
645 to_sample_factors.as_mutable_span().slice(dst_points));
646 }
647 else {
648 /* Target curve samples match 'to' points. */
649 BLI_assert(to_points.size() == dst_points.size());
651 from_curve,
652 from_curves_cyclic[from_curve],
653 dst_curve_flip[pair_index],
654 from_sample_indices.as_mutable_span().slice(dst_points),
655 from_sample_factors.as_mutable_span().slice(dst_points));
656 array_utils::fill_index_range(to_sample_indices.as_mutable_span().slice(dst_points));
657 to_sample_factors.fill(0.0f);
658 }
659 }
660
662 to_drawing->strokes(),
663 from_indices,
664 to_indices,
665 from_sample_indices,
666 to_sample_indices,
667 from_sample_factors,
668 to_sample_factors,
669 dst_curve_mask,
670 mix_factor,
671 dst_curves,
672 memory);
673 }
674
675 return dst_curves;
676}
677
679
680/* -------------------------------------------------------------------- */
683
685 const InterpolateOpData &opdata)
686{
687 Scene &scene = *CTX_data_scene(&C);
688 ScrArea &area = *CTX_wm_area(&C);
689
690 const StringRef msg = IFACE_("GPencil Interpolation: ");
691
692 std::string status;
693 if (hasNumInput(&opdata.numeric_input)) {
694 char str_ofs[NUM_STR_REP_LEN];
695 outputNumInput(&const_cast<NumInput &>(opdata.numeric_input), str_ofs, scene.unit);
696 status = msg + std::string(str_ofs);
697 }
698 else {
699 status = msg + std::to_string(int((opdata.init_factor + opdata.shift) * 100.0f)) + " %";
700 }
701
702 ED_area_status_text(&area, status.c_str());
704 &C, IFACE_("ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/MOVE to adjust factor"));
705}
706
707/* Utility function to get a drawing at the exact frame number. */
710 const int frame_number)
711{
713
714 const std::optional<int> start_frame = layer.start_frame_at(frame_number);
715 if (start_frame && *start_frame == frame_number) {
716 return grease_pencil.get_editable_drawing_at(layer, frame_number);
717 }
718 return nullptr;
719}
720
722 GreasePencil &grease_pencil,
725 const int frame_number)
726{
728
729 static constexpr eBezTriple_KeyframeType keyframe_type = BEZT_KEYTYPE_BREAKDOWN;
730
731 if (Drawing *drawing = get_drawing_at_exact_frame(grease_pencil, layer, frame_number)) {
732 layer_data.orig_curves = drawing->strokes();
733 return drawing;
734 }
735 return grease_pencil.insert_frame(layer, frame_number, 0, keyframe_type);
736}
737
739{
742
743 const auto &opdata = *static_cast<InterpolateOpData *>(op.customdata);
744 const Scene &scene = *CTX_data_scene(&C);
745 const int current_frame = scene.r.cfra;
746 Object &object = *CTX_data_active_object(&C);
747 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
748 const auto flip_mode = InterpolateFlipMode(RNA_enum_get(op.ptr, "flip"));
749
750 opdata.layer_mask.foreach_index([&](const int layer_index) {
751 Layer &layer = grease_pencil.layer(layer_index);
752 const InterpolateOpData::LayerData &layer_data = opdata.layer_data[layer_index];
753
754 /* Drawings must be created on operator invoke. */
755 Drawing *dst_drawing = get_drawing_at_exact_frame(grease_pencil, layer, current_frame);
756 if (dst_drawing == nullptr) {
757 return;
758 }
759
760 const float mix_factor = opdata.init_factor + opdata.shift;
762 grease_pencil, layer, layer_data.curve_pairs, mix_factor, flip_mode);
763
764 if (opdata.smooth_factor > 0.0f && opdata.smooth_steps > 0) {
765 MutableSpan<float3> positions = interpolated_curves.positions_for_write();
767 interpolated_curves.curves_range(),
768 interpolated_curves.points_by_curve(),
769 VArray<bool>::ForSingle(true, interpolated_curves.points_num()),
770 interpolated_curves.cyclic(),
771 opdata.smooth_steps,
772 opdata.smooth_factor,
773 false,
774 true,
775 positions);
776 interpolated_curves.tag_positions_changed();
777 }
778
779 dst_drawing->strokes_for_write() = std::move(interpolated_curves);
780 dst_drawing->tag_topology_changed();
781 });
782
784
787}
788
789/* Restore timeline changes when canceled. */
791{
794
795 if (op.customdata == nullptr) {
796 return;
797 }
798
799 const auto &opdata = *static_cast<InterpolateOpData *>(op.customdata);
800 const Scene &scene = *CTX_data_scene(&C);
801 const int current_frame = scene.r.cfra;
802 Object &object = *CTX_data_active_object(&C);
803 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
804
805 opdata.layer_mask.foreach_index([&](const int layer_index) {
806 Layer &layer = grease_pencil.layer(layer_index);
807 const InterpolateOpData::LayerData &layer_data = opdata.layer_data[layer_index];
808
809 if (layer_data.orig_curves) {
810 /* Keyframe existed before the operator, restore geometry. */
811 Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, current_frame);
812 if (drawing) {
813 drawing->strokes_for_write() = *layer_data.orig_curves;
814 drawing->tag_topology_changed();
817 }
818 }
819 else {
820 /* Frame was empty, remove the added drawing. */
821 grease_pencil.remove_frames(layer, {current_frame});
824 }
825 });
826}
827
829{
831
833 if (op.customdata == nullptr) {
834 return false;
835 }
836 InterpolateOpData &data = *static_cast<InterpolateOpData *>(op.customdata);
837
838 const Scene &scene = *CTX_data_scene(&C);
839 const int current_frame = scene.r.cfra;
840 Object &object = *CTX_data_active_object(&C);
841 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
842
843 /* Create target frames. */
844 data.layer_mask.foreach_index([&](const int layer_index) {
845 Layer &layer = grease_pencil.layer(layer_index);
846 InterpolateOpData::LayerData &layer_data = data.layer_data[layer_index];
847
848 ensure_drawing_at_exact_frame(grease_pencil, layer, layer_data, current_frame);
849 });
850
851 return true;
852}
853
854/* Exit and free memory. */
856{
857 ScrArea &area = *CTX_wm_area(&C);
858
859 if (op.customdata == nullptr) {
860 return;
861 }
862
863 ED_area_status_text(&area, nullptr);
864 ED_workspace_status_text(&C, nullptr);
865
866 MEM_delete(static_cast<InterpolateOpData *>(op.customdata));
867 op.customdata = nullptr;
868}
869
871{
873 return false;
874 }
876 if (!ts || !ts->gp_paint) {
877 return false;
878 }
879 /* Only 3D view */
880 ScrArea *area = CTX_wm_area(C);
881 if (area && area->spacetype != SPACE_VIEW3D) {
882 return false;
883 }
884
885 return true;
886}
887
888/* Invoke handler: Initialize the operator */
890 wmOperator *op,
891 const wmEvent * /*event*/)
892{
893 wmWindow &win = *CTX_wm_window(C);
894
895 if (!grease_pencil_interpolate_init(*C, *op)) {
897 return OPERATOR_CANCELLED;
898 }
899 InterpolateOpData &opdata = *static_cast<InterpolateOpData *>(op->customdata);
900
901 /* Set cursor to indicate modal operator. */
903
905
907
909
911}
912
919
920/* Modal handler: Events handling during interactive part */
922 wmOperator *op,
923 const wmEvent *event)
924{
925 wmWindow &win = *CTX_wm_window(C);
926 const ARegion &region = *CTX_wm_region(C);
927 ScrArea &area = *CTX_wm_area(C);
928 InterpolateOpData &opdata = *static_cast<InterpolateOpData *>(op->customdata);
929 const bool has_numinput = hasNumInput(&opdata.numeric_input);
930
931 switch (event->type) {
932 case EVT_MODAL_MAP: {
933 switch (InterpolateToolModalEvent(event->val)) {
935 ED_area_status_text(&area, nullptr);
936 ED_workspace_status_text(C, nullptr);
938
941 return OPERATOR_CANCELLED;
943 ED_area_status_text(&area, nullptr);
944 ED_workspace_status_text(C, nullptr);
946
947 /* Write current factor to properties for the next execution. */
948 RNA_float_set(op->ptr, "shift", opdata.shift);
949
951 return OPERATOR_FINISHED;
953 opdata.shift = std::clamp(opdata.init_factor + opdata.shift + 0.01f,
956 opdata.init_factor;
958 break;
960 opdata.shift = std::clamp(opdata.init_factor + opdata.shift - 0.01f,
963 opdata.init_factor;
965 break;
966 }
967 break;
968 }
969 case MOUSEMOVE:
970 /* Only handle mouse-move if not doing numeric-input. */
971 if (!has_numinput) {
972 const float mouse_pos = event->mval[0];
973 const float factor = std::clamp(
975 opdata.shift = factor - opdata.init_factor;
976
978 }
979 break;
980 default: {
981 if ((event->val == KM_PRESS) && handleNumInput(C, &opdata.numeric_input, event)) {
982 float value = (opdata.init_factor + opdata.shift) * 100.0f;
983 applyNumInput(&opdata.numeric_input, &value);
984 opdata.shift = std::clamp(value * 0.01f, interpolate_factor_min, interpolate_factor_max) -
985 opdata.init_factor;
986
988 break;
989 }
990 /* Unhandled event, allow to pass through. */
992 }
993 }
994
996}
997
1003
1005{
1006 ot->name = "Grease Pencil Interpolation";
1007 ot->idname = "GREASE_PENCIL_OT_interpolate";
1008 ot->description = "Interpolate Grease Pencil strokes between frames";
1009
1014
1015 ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
1016
1018 ot->srna,
1019 "shift",
1020 0.0f,
1021 -1.0f,
1022 1.0f,
1023 "Shift",
1024 "Bias factor for which frame has more influence on the interpolated strokes",
1025 -0.9f,
1026 0.9f);
1027
1028 RNA_def_enum(ot->srna,
1029 "layers",
1031 0,
1032 "Layer",
1033 "Layers included in the interpolation");
1034
1035 RNA_def_boolean(ot->srna,
1036 "exclude_breakdowns",
1037 false,
1038 "Exclude Breakdowns",
1039 "Exclude existing Breakdowns keyframes as interpolation extremes");
1040
1041 RNA_def_boolean(ot->srna,
1042 "use_selection",
1043 false,
1044 "Use Selection",
1045 "Use only selected strokes for interpolating");
1046
1047 RNA_def_enum(ot->srna,
1048 "flip",
1050 int(InterpolateFlipMode::FlipAuto),
1051 "Flip Mode",
1052 "Invert destination stroke to match start and end with source stroke");
1053
1054 RNA_def_int(ot->srna,
1055 "smooth_steps",
1056 1,
1057 1,
1058 3,
1059 "Iterations",
1060 "Number of times to smooth newly created strokes",
1061 1,
1062 3);
1063
1064 RNA_def_float(ot->srna,
1065 "smooth_factor",
1066 0.0f,
1067 0.0f,
1068 2.0f,
1069 "Smooth",
1070 "Amount of smoothing to apply to interpolated strokes, to reduce jitter/noise",
1071 0.0f,
1072 2.0f);
1073}
1074
1076
1077/* -------------------------------------------------------------------- */
1080
1081/* Helper: Perform easing equation calculations for GP interpolation operator. */
1083 const InterpolationType type,
1084 const float back_easing,
1085 const float amplitude,
1086 const float period,
1087 const CurveMapping &custom_ipo,
1088 const float time)
1089{
1090 constexpr float begin = 0.0f;
1091 constexpr float change = 1.0f;
1092 constexpr float duration = 1.0f;
1093
1094 switch (type) {
1096 return time;
1097
1099 return BKE_curvemapping_evaluateF(&custom_ipo, 0, time);
1100
1102 switch (easing) {
1103 case BEZT_IPO_EASE_IN:
1104 return BLI_easing_back_ease_in(time, begin, change, duration, back_easing);
1105 case BEZT_IPO_EASE_OUT:
1106 return BLI_easing_back_ease_out(time, begin, change, duration, back_easing);
1108 return BLI_easing_back_ease_in_out(time, begin, change, duration, back_easing);
1109
1110 default:
1111 return BLI_easing_back_ease_out(time, begin, change, duration, back_easing);
1112 }
1113 break;
1114
1116 switch (easing) {
1117 case BEZT_IPO_EASE_IN:
1118 return BLI_easing_bounce_ease_in(time, begin, change, duration);
1119 case BEZT_IPO_EASE_OUT:
1120 return BLI_easing_bounce_ease_out(time, begin, change, duration);
1122 return BLI_easing_bounce_ease_in_out(time, begin, change, duration);
1123
1124 default:
1125 return BLI_easing_bounce_ease_out(time, begin, change, duration);
1126 }
1127 break;
1128
1130 switch (easing) {
1131 case BEZT_IPO_EASE_IN:
1132 return BLI_easing_circ_ease_in(time, begin, change, duration);
1133 case BEZT_IPO_EASE_OUT:
1134 return BLI_easing_circ_ease_out(time, begin, change, duration);
1136 return BLI_easing_circ_ease_in_out(time, begin, change, duration);
1137
1138 default:
1139 return BLI_easing_circ_ease_in(time, begin, change, duration);
1140 }
1141 break;
1142
1144 switch (easing) {
1145 case BEZT_IPO_EASE_IN:
1146 return BLI_easing_cubic_ease_in(time, begin, change, duration);
1147 case BEZT_IPO_EASE_OUT:
1148 return BLI_easing_cubic_ease_out(time, begin, change, duration);
1150 return BLI_easing_cubic_ease_in_out(time, begin, change, duration);
1151
1152 default:
1153 return BLI_easing_cubic_ease_in(time, begin, change, duration);
1154 }
1155 break;
1156
1158 switch (easing) {
1159 case BEZT_IPO_EASE_IN:
1160 return BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period);
1161 case BEZT_IPO_EASE_OUT:
1162 return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period);
1164 return BLI_easing_elastic_ease_in_out(time, begin, change, duration, amplitude, period);
1165
1166 default:
1167 return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period);
1168 }
1169 break;
1170
1172 switch (easing) {
1173 case BEZT_IPO_EASE_IN:
1174 return BLI_easing_expo_ease_in(time, begin, change, duration);
1175 case BEZT_IPO_EASE_OUT:
1176 return BLI_easing_expo_ease_out(time, begin, change, duration);
1178 return BLI_easing_expo_ease_in_out(time, begin, change, duration);
1179
1180 default:
1181 return BLI_easing_expo_ease_in(time, begin, change, duration);
1182 }
1183 break;
1184
1186 switch (easing) {
1187 case BEZT_IPO_EASE_IN:
1188 return BLI_easing_quad_ease_in(time, begin, change, duration);
1189 case BEZT_IPO_EASE_OUT:
1190 return BLI_easing_quad_ease_out(time, begin, change, duration);
1192 return BLI_easing_quad_ease_in_out(time, begin, change, duration);
1193
1194 default:
1195 return BLI_easing_quad_ease_in(time, begin, change, duration);
1196 }
1197 break;
1198
1200 switch (easing) {
1201 case BEZT_IPO_EASE_IN:
1202 return BLI_easing_quart_ease_in(time, begin, change, duration);
1203 case BEZT_IPO_EASE_OUT:
1204 return BLI_easing_quart_ease_out(time, begin, change, duration);
1206 return BLI_easing_quart_ease_in_out(time, begin, change, duration);
1207
1208 default:
1209 return BLI_easing_quart_ease_in(time, begin, change, duration);
1210 }
1211 break;
1212
1214 switch (easing) {
1215 case BEZT_IPO_EASE_IN:
1216 return BLI_easing_quint_ease_in(time, begin, change, duration);
1217 case BEZT_IPO_EASE_OUT:
1218 return BLI_easing_quint_ease_out(time, begin, change, duration);
1220 return BLI_easing_quint_ease_in_out(time, begin, change, duration);
1221
1222 default:
1223 return BLI_easing_quint_ease_in(time, begin, change, duration);
1224 }
1225 break;
1226
1228 switch (easing) {
1229 case BEZT_IPO_EASE_IN:
1230 return BLI_easing_sine_ease_in(time, begin, change, duration);
1231 case BEZT_IPO_EASE_OUT:
1232 return BLI_easing_sine_ease_out(time, begin, change, duration);
1234 return BLI_easing_sine_ease_in_out(time, begin, change, duration);
1235
1236 default:
1237 return BLI_easing_sine_ease_in(time, begin, change, duration);
1238 }
1239 break;
1240
1241 default:
1243 break;
1244 }
1245
1246 return time;
1247}
1248
1250{
1253
1255 if (op->customdata == nullptr) {
1256 return OPERATOR_FINISHED;
1257 }
1258 InterpolateOpData &opdata = *static_cast<InterpolateOpData *>(op->customdata);
1259
1260 const Scene &scene = *CTX_data_scene(C);
1261 const int current_frame = scene.r.cfra;
1262 Object &object = *CTX_data_active_object(C);
1263 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
1265 const InterpolationType type = InterpolationType(RNA_enum_get(op->ptr, "type"));
1266 const eBezTriple_Easing easing = eBezTriple_Easing(RNA_enum_get(op->ptr, "easing"));
1267 const float back_easing = RNA_float_get(op->ptr, "back");
1268 const float amplitude = RNA_float_get(op->ptr, "amplitude");
1269 const float period = RNA_float_get(op->ptr, "period");
1270 const int step = RNA_int_get(op->ptr, "step");
1271
1272 GP_Interpolate_Settings &ipo_settings = ts.gp_interpolate;
1273 if (ipo_settings.custom_ipo == nullptr) {
1274 ipo_settings.custom_ipo = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
1275 }
1276 BKE_curvemapping_init(ipo_settings.custom_ipo);
1277
1278 opdata.layer_mask.foreach_index([&](const int layer_index) {
1279 Layer &layer = grease_pencil.layer(layer_index);
1280 InterpolateOpData::LayerData &layer_data = opdata.layer_data[layer_index];
1281
1282 std::optional<FramesMapKeyIntervalT> interval = find_frames_interval(
1283 layer, current_frame, opdata.exclude_breakdowns);
1284 if (!interval) {
1285 return;
1286 }
1287
1288 const int frame_range_size = interval->second - interval->first + 1;
1289
1290 /* First and last frame are ignored. */
1291 for (int cframe = interval->first + step; cframe < interval->second; cframe += step) {
1292 ensure_drawing_at_exact_frame(grease_pencil, layer, layer_data, cframe);
1293 Drawing *dst_drawing = get_drawing_at_exact_frame(grease_pencil, layer, cframe);
1294 if (dst_drawing == nullptr) {
1295 return;
1296 }
1297
1298 const float base_factor = float(cframe - interval->first) /
1299 std::max(frame_range_size - 1, 1);
1300 const float mix_factor = grease_pencil_interpolate_sequence_easing_calc(
1301 easing, type, back_easing, amplitude, period, *ipo_settings.custom_ipo, base_factor);
1302
1303 bke::CurvesGeometry interpolated_curves = interpolate_between_curves(
1304 grease_pencil, layer, layer_data.curve_pairs, mix_factor, opdata.flipmode);
1305
1306 if (opdata.smooth_factor > 0.0f && opdata.smooth_steps > 0) {
1307 MutableSpan<float3> positions = interpolated_curves.positions_for_write();
1309 interpolated_curves.curves_range(),
1310 interpolated_curves.points_by_curve(),
1311 VArray<bool>::ForSingle(true, interpolated_curves.points_num()),
1312 interpolated_curves.cyclic(),
1313 opdata.smooth_steps,
1314 opdata.smooth_factor,
1315 false,
1316 true,
1317 positions);
1318 interpolated_curves.tag_positions_changed();
1319 }
1320
1321 dst_drawing->strokes_for_write() = std::move(interpolated_curves);
1322 dst_drawing->tag_topology_changed();
1323 }
1324 });
1325
1326 /* Notifiers */
1329
1330 MEM_delete(static_cast<InterpolateOpData *>(op->customdata));
1331 op->customdata = nullptr;
1332
1333 return OPERATOR_FINISHED;
1334}
1335
1337{
1338 uiLayout *layout = op->layout;
1339 uiLayout *col, *row;
1340
1341 const InterpolationType type = InterpolationType(RNA_enum_get(op->ptr, "type"));
1342
1343 uiLayoutSetPropSep(layout, true);
1344 uiLayoutSetPropDecorate(layout, false);
1345 row = &layout->row(true);
1346 row->prop(op->ptr, "step", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1347
1348 row = &layout->row(true);
1349 row->prop(op->ptr, "layers", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1350
1352 row = &layout->row(true);
1353 row->prop(op->ptr, "interpolate_selected_only", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1354 }
1355
1356 row = &layout->row(true);
1357 row->prop(op->ptr, "exclude_breakdowns", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1358
1359 row = &layout->row(true);
1360 row->prop(op->ptr, "use_selection", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1361
1362 row = &layout->row(true);
1363 row->prop(op->ptr, "flip", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1364
1365 col = &layout->column(true);
1366 col->prop(op->ptr, "smooth_factor", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1367 col->prop(op->ptr, "smooth_steps", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1368
1369 row = &layout->row(true);
1370 row->prop(op->ptr, "type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1371
1372 if (type == InterpolationType::CurveMap) {
1373 /* Get an RNA pointer to ToolSettings to give to the custom curve. */
1374 Scene *scene = CTX_data_scene(C);
1375 ToolSettings *ts = scene->toolsettings;
1376 PointerRNA gpsettings_ptr = RNA_pointer_create_discrete(
1377 &scene->id, &RNA_GPencilInterpolateSettings, &ts->gp_interpolate);
1379 layout, &gpsettings_ptr, "interpolation_curve", 0, false, true, true, false);
1380 }
1381 else if (type != InterpolationType::Linear) {
1382 row = &layout->row(false);
1383 row->prop(op->ptr, "easing", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1384 if (type == InterpolationType::Back) {
1385 row = &layout->row(false);
1386 row->prop(op->ptr, "back", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1387 }
1388 else if (type == InterpolationType::Elastic) {
1389 row = &layout->row(false);
1390 row->prop(op->ptr, "amplitude", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1391 row = &layout->row(false);
1392 row->prop(op->ptr, "period", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1393 }
1394 }
1395}
1396
1398{
1399 PropertyRNA *prop;
1400
1401 ot->name = "Interpolate Sequence";
1402 ot->idname = "GREASE_PENCIL_OT_interpolate_sequence";
1403 ot->translation_context = BLT_I18NCONTEXT_ID_GPENCIL;
1404 ot->description = "Generate 'in-betweens' to smoothly interpolate between Grease Pencil frames";
1405
1409
1410 RNA_def_int(ot->srna,
1411 "step",
1412 1,
1413 1,
1414 MAXFRAME,
1415 "Step",
1416 "Number of frames between generated interpolated frames",
1417 1,
1418 MAXFRAME);
1419
1420 RNA_def_enum(ot->srna,
1421 "layers",
1423 0,
1424 "Layer",
1425 "Layers included in the interpolation");
1426
1427 RNA_def_boolean(ot->srna,
1428 "exclude_breakdowns",
1429 false,
1430 "Exclude Breakdowns",
1431 "Exclude existing Breakdowns keyframes as interpolation extremes");
1432
1433 RNA_def_boolean(ot->srna,
1434 "use_selection",
1435 false,
1436 "Use Selection",
1437 "Use only selected strokes for interpolating");
1438
1439 RNA_def_enum(ot->srna,
1440 "flip",
1442 int(InterpolateFlipMode::FlipAuto),
1443 "Flip Mode",
1444 "Invert destination stroke to match start and end with source stroke");
1445
1446 RNA_def_int(ot->srna,
1447 "smooth_steps",
1448 1,
1449 1,
1450 3,
1451 "Iterations",
1452 "Number of times to smooth newly created strokes",
1453 1,
1454 3);
1455
1456 RNA_def_float(ot->srna,
1457 "smooth_factor",
1458 0.0f,
1459 0.0f,
1460 2.0f,
1461 "Smooth",
1462 "Amount of smoothing to apply to interpolated strokes, to reduce jitter/noise",
1463 0.0f,
1464 2.0f);
1465
1466 prop = RNA_def_enum(ot->srna,
1467 "type",
1469 0,
1470 "Type",
1471 "Interpolation method to use the next time 'Interpolate Sequence' is run");
1473
1474 prop = RNA_def_enum(
1475 ot->srna,
1476 "easing",
1479 "Easing",
1480 "Which ends of the segment between the preceding and following Grease Pencil frames "
1481 "easing interpolation is applied to");
1483
1484 prop = RNA_def_float(ot->srna,
1485 "back",
1486 1.702f,
1487 0.0f,
1488 FLT_MAX,
1489 "Back",
1490 "Amount of overshoot for 'back' easing",
1491 0.0f,
1492 FLT_MAX);
1494
1495 RNA_def_float(ot->srna,
1496 "amplitude",
1497 0.15f,
1498 0.0f,
1499 FLT_MAX,
1500 "Amplitude",
1501 "Amount to boost elastic bounces for 'elastic' easing",
1502 0.0f,
1503 FLT_MAX);
1504
1505 RNA_def_float(ot->srna,
1506 "period",
1507 0.15f,
1508 -FLT_MAX,
1509 FLT_MAX,
1510 "Period",
1511 "Time between bounces for elastic easing",
1512 -FLT_MAX,
1513 FLT_MAX);
1514
1515 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1516}
1517
1519
1520} // namespace blender::ed::sculpt_paint::greasepencil
1521
1522/* -------------------------------------------------------------------- */
1525
1532
1534{
1536 static const EnumPropertyItem modal_items[] = {
1537 {int(InterpolateToolModalEvent::Cancel), "CANCEL", 0, "Cancel", ""},
1538 {int(InterpolateToolModalEvent::Confirm), "CONFIRM", 0, "Confirm", ""},
1539 {int(InterpolateToolModalEvent::Increase), "INCREASE", 0, "Increase", ""},
1540 {int(InterpolateToolModalEvent::Decrease), "DECREASE", 0, "Decrease", ""},
1541 {0, nullptr, 0, nullptr, nullptr},
1542 };
1543
1544 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Interpolate Tool Modal Map");
1545
1546 /* This function is called for each space-type, only needs to add map once. */
1547 if (keymap && keymap->modal_items) {
1548 return;
1549 }
1550
1551 keymap = WM_modalkeymap_ensure(keyconf, "Interpolate Tool Modal Map", modal_items);
1552
1553 WM_modalkeymap_assign(keymap, "GREASE_PENCIL_OT_interpolate");
1554}
1555
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:71
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:962
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ 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:311
bool applyNumInput(NumInput *n, float *vec)
Definition numinput.cc:189
void outputNumInput(NumInput *n, char *str, const UnitSettings &unit_settings)
Definition numinput.cc:87
bool hasNumInput(const NumInput *n)
Definition numinput.cc:170
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:872
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1040
Read Guarded memory(de)allocation.
#define RNA_ENUM_ITEM_HEADING(name, description)
Definition RNA_types.hh:639
#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)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
#define ND_DATA
Definition WM_types.hh:506
@ 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:581
#define NC_GPENCIL
Definition WM_types.hh:396
@ KM_PRESS
Definition WM_types.hh:308
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:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
const T * end() const
Definition BLI_array.hh:314
void fill(const T &value) const
Definition BLI_array.hh:261
const T * begin() const
Definition BLI_array.hh:310
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 ForSingle(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
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
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)
uiLayout & column(bool align)
uiLayout & row(bool align)
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:754
short val
Definition WM_types.hh:756
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:4225
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:929
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:956
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))