Blender V5.0
grease_pencil_frames.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10
11#include "BKE_curves.hh"
12
13#include "BLI_listbase.h"
14#include "BLI_map.hh"
16#include "BLI_utildefines.h"
17
18#include "BKE_context.hh"
19#include "BKE_grease_pencil.hh"
20#include "BKE_paint.hh"
21
22#include "DEG_depsgraph.hh"
23
24#include "DNA_scene_types.h"
25
26#include "ANIM_keyframing.hh"
27
28#include "ED_anim_api.hh"
29#include "ED_grease_pencil.hh"
30#include "ED_keyframes_edit.hh"
31#include "ED_markers.hh"
32
33#include "RNA_access.hh"
34#include "RNA_define.hh"
35
36#include "WM_api.hh"
37
39
41 const eBezTriple_KeyframeType key_type)
42{
43 for (GreasePencilFrame &frame : layer.frames_for_write().values()) {
44 if (frame.is_selected()) {
45 frame.type = key_type;
47 }
48 }
49}
50
51static float get_snapped_frame_number(const float frame_number,
52 Scene &scene,
53 const eEditKeyframes_Snap mode)
54{
55 switch (mode) {
56 case SNAP_KEYS_CURFRAME: /* Snap to current frame. */
57 return scene.r.cfra;
58 case SNAP_KEYS_NEARSEC: /* Snap to nearest second. */
59 {
60 float secf = (scene.r.frs_sec / scene.r.frs_sec_base);
61 return floorf(frame_number / secf + 0.5f) * secf;
62 }
63 case SNAP_KEYS_NEARMARKER: /* Snap to nearest marker. */
64 return ED_markers_find_nearest_marker_time(&scene.markers, frame_number);
65 default:
66 break;
67 }
68 return frame_number;
69}
70
73 Scene &scene,
74 const eEditKeyframes_Snap mode)
75{
76 bool changed = false;
77 blender::Map<int, int> frame_number_destinations;
78 for (auto [frame_number, frame] : layer.frames().items()) {
79 if (!frame.is_selected()) {
80 continue;
81 }
82 const int snapped = round_fl_to_int(
83 get_snapped_frame_number(float(frame_number), scene, mode));
84 if (snapped != frame_number) {
85 frame_number_destinations.add(frame_number, snapped);
86 changed = true;
87 }
88 }
89
90 if (changed) {
91 grease_pencil.move_frames(layer, frame_number_destinations);
92 }
93
94 return changed;
95}
96
97static int get_mirrored_frame_number(const int frame_number,
98 const Scene &scene,
99 const eEditKeyframes_Mirror mode,
100 const TimeMarker *first_selected_marker)
101{
102 switch (mode) {
103 case MIRROR_KEYS_CURFRAME: /* Mirror over current frame. */
104 return 2 * scene.r.cfra - frame_number;
106 case MIRROR_KEYS_YAXIS: /* Mirror over frame 0. */
107 return -frame_number;
108 case MIRROR_KEYS_MARKER: /* Mirror over marker. */
109 if (first_selected_marker == nullptr) {
110 break;
111 }
112 return 2 * first_selected_marker->frame - frame_number;
113 default:
114 break;
115 }
116 return frame_number;
117}
118
121 Scene &scene,
122 const eEditKeyframes_Mirror mode)
123{
124 bool changed = false;
125 Map<int, int> frame_number_destinations;
126
127 /* Pre-compute the first selected marker, so that we don't compute it for each frame. */
128 const TimeMarker *first_selected_marker = (mode == MIRROR_KEYS_MARKER) ?
130 nullptr;
131
132 for (auto [frame_number, frame] : layer.frames().items()) {
133 if (!frame.is_selected()) {
134 continue;
135 }
136
137 const int mirrored_frame_number = get_mirrored_frame_number(
138 frame_number, scene, mode, first_selected_marker);
139
140 if (mirrored_frame_number != frame_number) {
141 frame_number_destinations.add(frame_number, mirrored_frame_number);
142 changed = true;
143 }
144 }
145
146 if (changed) {
147 grease_pencil.move_frames(layer, frame_number_destinations);
148 }
149
150 return changed;
151}
152
154{
155 using namespace bke::greasepencil;
156 bool changed = false;
157 LayerTransformData &trans_data = layer.runtime->trans_data_;
158
159 for (auto [frame_number, frame] : layer.frames_for_write().items()) {
160 if (!frame.is_selected()) {
161 continue;
162 }
163
164 /* Create the duplicate drawing. */
165 const Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number);
166 if (drawing == nullptr) {
167 continue;
168 }
169 const int duplicated_drawing_index = grease_pencil.drawings().size();
170 grease_pencil.add_duplicate_drawings(1, *drawing);
171
172 /* Make a copy of the frame in the duplicates. */
173 GreasePencilFrame frame_duplicate = frame;
174 frame_duplicate.drawing_index = duplicated_drawing_index;
175 trans_data.duplicated_frames_buffer.add_overwrite(frame_number, frame_duplicate);
176
177 /* Deselect the current frame, so that only the copy is selected. */
178 frame.flag ^= GP_FRAME_SELECTED;
179
180 changed = true;
181 }
182
183 if (changed) {
185 }
186
187 return changed;
188}
189
191{
192 Vector<int> frames_to_remove;
193 for (auto [frame_number, frame] : layer.frames().items()) {
194 if (!frame.is_selected()) {
195 continue;
196 }
197 frames_to_remove.append(frame_number);
198 }
199 return grease_pencil.remove_frames(layer, frames_to_remove.as_span());
200}
201
202static void select_frame(GreasePencilFrame &frame, const short select_mode)
203{
204 switch (select_mode) {
205 case SELECT_ADD:
206 frame.flag |= GP_FRAME_SELECTED;
207 break;
208 case SELECT_SUBTRACT:
209 frame.flag &= ~GP_FRAME_SELECTED;
210 break;
211 case SELECT_INVERT:
212 frame.flag ^= GP_FRAME_SELECTED;
213 break;
214 }
215}
216
218 const int frame_number,
219 const short select_mode)
220{
221
222 GreasePencilFrame *frame = layer.frames_for_write().lookup_ptr(frame_number);
223 if (frame == nullptr) {
224 return false;
225 }
226 select_frame(*frame, select_mode);
228 return true;
229}
230
232 const int frame_number,
233 const short select_mode)
234{
236 bke::greasepencil::TreeNode &node = node_->wrap();
237 if (node.is_group()) {
238 select_frames_at(node.as_group(), frame_number, select_mode);
239 }
240 else if (node.is_layer()) {
241 select_frame_at(node.as_layer(), frame_number, select_mode);
242 }
243 }
244}
245
246void select_all_frames(bke::greasepencil::Layer &layer, const short select_mode)
247{
248 for (auto item : layer.frames_for_write().items()) {
249 select_frame(item.value, select_mode);
251 }
252}
253
255{
256 for (const auto &[frame_number, frame] : layer.frames().items()) {
257 if (frame.is_selected()) {
258 return true;
259 }
260 }
261 return false;
262}
263
266 const short tool,
267 const short select_mode)
268{
269 if (node.is_layer()) {
270 for (auto [frame_number, frame] : node.as_layer().frames_for_write().items()) {
271 /* Construct a dummy point coordinate to do this testing with. */
272 const float2 pt(float(frame_number), ked->channel_y);
273
274 /* Check the necessary regions. */
275 if (tool == BEZT_OK_CHANNEL_LASSO) {
276 if (keyframe_region_lasso_test(static_cast<const KeyframeEdit_LassoData *>(ked->data), pt))
277 {
278 select_frame(frame, select_mode);
279 }
280 }
281 else if (tool == BEZT_OK_CHANNEL_CIRCLE) {
282 if (keyframe_region_circle_test(static_cast<const KeyframeEdit_CircleData *>(ked->data),
283 pt))
284 {
285 select_frame(frame, select_mode);
286 }
287 }
288
290 }
291 }
292 else if (node.is_group()) {
294 select_frames_region(ked, node_->wrap(), tool, select_mode);
295 }
296 }
297}
298
300 const float min,
301 const float max,
302 const short select_mode)
303{
304 /* Only select those frames which are in bounds. */
305 if (node.is_layer()) {
306 for (auto [frame_number, frame] : node.as_layer().frames_for_write().items()) {
307 if (IN_RANGE(float(frame_number), min, max)) {
308 select_frame(frame, select_mode);
310 }
311 }
312 }
313 else if (node.is_group()) {
315 select_frames_range(node_->wrap(), min, max, select_mode);
316 }
317 }
318}
319
321 const int frame_number,
322 const GreasePencilFrame &frame)
323{
324 CfraElem *ce = MEM_callocN<CfraElem>(__func__);
325 ce->cfra = float(frame_number);
326 ce->sel = frame.is_selected();
327 BLI_addtail(&ked->list, ce);
328}
329
331 const bke::greasepencil::Layer &layer)
332{
333 BLI_assert(ked != nullptr);
334
335 for (const auto &[frame_number, frame] : layer.frames().items()) {
336 if (frame.is_selected()) {
337 append_frame_to_key_edit_data(ked, frame_number, frame);
338 }
339 }
340}
341
343 GreasePencil &grease_pencil,
345 const bool duplicate_previous_key,
346 bool &r_inserted_keyframe)
347{
348 const int current_frame = scene.r.cfra;
349 if (!layer.has_drawing_at(current_frame) && !blender::animrig::is_autokey_on(&scene)) {
350 return false;
351 }
352
353 /* If auto-key is on and the drawing at the current frame starts before the current frame a new
354 * keyframe needs to be inserted. */
355 const bool is_first = layer.is_empty() || (layer.sorted_keys().first() > current_frame);
356 const std::optional<int> previous_key_frame_start = layer.start_frame_at(current_frame);
357 const bool has_previous_key = previous_key_frame_start.has_value();
358 const bool needs_new_drawing = is_first || !has_previous_key ||
359 (previous_key_frame_start < current_frame);
360 if (blender::animrig::is_autokey_on(&scene) && needs_new_drawing) {
361 const bool use_additive_drawing = (scene.toolsettings->gpencil_flags &
363 if (has_previous_key && (use_additive_drawing || duplicate_previous_key)) {
364 /* We duplicate the frame that's currently visible and insert it at the current frame. */
365 grease_pencil.insert_duplicate_frame(layer, *previous_key_frame_start, current_frame, false);
366 }
367 else {
368 /* Otherwise we just insert a blank keyframe at the current frame. */
369 grease_pencil.insert_frame(layer, current_frame);
370 }
371 r_inserted_keyframe = true;
372 }
373 /* There should now always be a drawing at the current frame. */
374 BLI_assert(layer.has_drawing_at(current_frame));
375 return true;
376}
377
379{
380 using namespace blender::bke::greasepencil;
381 Scene *scene = CTX_data_scene(C);
383 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
384 const int current_frame = scene->r.cfra;
385 const bool all_layers = RNA_boolean_get(op->ptr, "all_layers");
386 const int duration = RNA_int_get(op->ptr, "duration");
387
388 bool changed = false;
389 if (all_layers) {
390 for (Layer *layer : grease_pencil.layers_for_write()) {
391 if (!layer->is_editable()) {
392 continue;
393 }
394 changed |= grease_pencil.insert_frame(*layer, current_frame, duration) != nullptr;
395 }
396 }
397 else {
398 if (!grease_pencil.has_active_layer()) {
399 return OPERATOR_CANCELLED;
400 }
401 changed |= grease_pencil.insert_frame(
402 *grease_pencil.get_active_layer(), current_frame, duration) != nullptr;
403 }
404
405 if (changed) {
406 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
407 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
409 }
410
411 return OPERATOR_FINISHED;
412}
413
415 const bke::GAttributeReader &attrs_b)
416{
417 return (attrs_a.varray.size() != attrs_b.varray.size() ||
418 attrs_a.varray.type() != attrs_b.varray.type());
419}
420
422 const bke::GAttributeReader &attrs_b)
423{
424 if (attrs_a.varray.is_span() && attrs_b.varray.is_span()) {
425 const GSpan attrs_span_a = attrs_a.varray.get_internal_span();
426 const GSpan attrs_span_b = attrs_b.varray.get_internal_span();
427
428 if (attrs_span_a.data() == attrs_span_b.data()) {
429 return true;
430 }
431 }
432
433 return false;
434}
435
436template<typename T>
437static bool attributes_elements_are_equal(const VArray<T> &attributes_a,
438 const VArray<T> &attributes_b)
439{
440 const std::optional<T> value_a = attributes_a.get_if_single();
441 const std::optional<T> value_b = attributes_b.get_if_single();
442 if (value_a.has_value() && value_b.has_value()) {
443 return value_a.value() == value_b.value();
444 }
445
446 const VArraySpan attrs_span_a = attributes_a;
447 const VArraySpan attrs_span_b = attributes_b;
448
449 return std::equal(
450 attrs_span_a.begin(), attrs_span_a.end(), attrs_span_b.begin(), attrs_span_b.end());
451}
452
454 const bke::CurvesGeometry &curves_b)
455{
456 using namespace blender::bke;
457
458 if (curves_a.is_empty() && curves_b.is_empty()) {
459 return true;
460 }
461
462 if (curves_a.curves_num() != curves_b.curves_num() ||
463 curves_a.points_num() != curves_b.points_num() || curves_a.offsets() != curves_b.offsets())
464 {
465 return false;
466 }
467
468 const AttributeAccessor attributes_a = curves_a.attributes();
469 const AttributeAccessor attributes_b = curves_b.attributes();
470
471 const Set<StringRefNull> ids_a = attributes_a.all_ids();
472 const Set<StringRefNull> ids_b = attributes_b.all_ids();
473 if (ids_a != ids_b) {
474 return false;
475 }
476
477 for (const StringRef id : ids_a) {
478 GAttributeReader attrs_a = attributes_a.lookup(id);
479 GAttributeReader attrs_b = attributes_b.lookup(id);
480
481 if (attributes_varrays_not_equal(attrs_a, attrs_b)) {
482 return false;
483 }
484
485 if (attributes_varrays_span_data_equal(attrs_a, attrs_b)) {
486 return true;
487 }
488
489 bool attributes_are_equal = true;
490
491 attribute_math::convert_to_static_type(attrs_a.varray.type(), [&](auto dummy) {
492 using T = decltype(dummy);
493
494 const VArray attributes_a = attrs_a.varray.typed<T>();
495 const VArray attributes_b = attrs_b.varray.typed<T>();
496
497 attributes_are_equal = attributes_elements_are_equal(attributes_a, attributes_b);
498 });
499
500 if (!attributes_are_equal) {
501 return false;
502 }
503 }
504
505 return true;
506}
507
509{
510 using namespace blender::bke::greasepencil;
512 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
513 const bool selected = RNA_boolean_get(op->ptr, "selected");
514
515 bool changed = false;
516
517 for (Layer *layer : grease_pencil.layers_for_write()) {
518 if (!layer->is_editable()) {
519 continue;
520 }
521
522 Vector<int> start_frame_numbers;
523 for (const FramesMapKeyT key : layer->sorted_keys()) {
524 const GreasePencilFrame *frame = layer->frames().lookup_ptr(key);
525 if (selected && !frame->is_selected()) {
526 continue;
527 }
528 if (frame->is_end()) {
529 continue;
530 }
531 start_frame_numbers.append(int(key));
532 }
533
534 Vector<int> frame_numbers_to_delete;
535 for (const int i : start_frame_numbers.index_range().drop_back(1)) {
536 const int current = start_frame_numbers[i];
537 const int next = start_frame_numbers[i + 1];
538
539 Drawing *drawing = grease_pencil.get_drawing_at(*layer, current);
540 Drawing *drawing_next = grease_pencil.get_drawing_at(*layer, next);
541
542 if (!drawing || !drawing_next) {
543 continue;
544 }
545
547 bke::CurvesGeometry &curves_next = drawing_next->strokes_for_write();
548
549 if (!curves_geometry_is_equal(curves, curves_next)) {
550 continue;
551 }
552
553 frame_numbers_to_delete.append(next);
554 }
555
556 grease_pencil.remove_frames(*layer, frame_numbers_to_delete.as_span());
557
558 changed = true;
559 }
560
561 if (changed) {
562 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
563 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
565 }
566
567 return OPERATOR_FINISHED;
568}
569
571{
572 PropertyRNA *prop;
573
574 /* identifiers */
575 ot->name = "Insert Blank Frame";
576 ot->idname = "GREASE_PENCIL_OT_insert_blank_frame";
577 ot->description = "Insert a blank frame on the current scene frame";
578
579 /* callbacks */
582
584
585 /* properties */
586 prop = RNA_def_boolean(
587 ot->srna, "all_layers", false, "All Layers", "Insert a blank frame in all editable layers");
589 RNA_def_int(ot->srna, "duration", 0, 0, MAXFRAME, "Duration", "", 0, 100);
590}
591
593{
594 PropertyRNA *prop;
595
596 /* identifiers */
597 ot->name = "Delete Duplicate Frames";
598 ot->idname = "GREASE_PENCIL_OT_frame_clean_duplicate";
599 ot->description = "Remove any keyframe that is a duplicate of the previous one";
600
601 /* callbacks */
604
606
607 /* properties */
608 prop = RNA_def_boolean(
609 ot->srna, "selected", false, "Selected", "Only delete selected keyframes");
611}
612
614{
615 using namespace bke::greasepencil;
616
617 /* Clear buffer first. */
618 clipboard.clear();
619
620 /* Filter data. */
622 ListBase anim_data = {nullptr, nullptr};
623
625 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
626
627 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
628 /* This function only deals with grease pencil layer frames.
629 * This check is needed in the case of a call from the main dope-sheet. */
630 if (ale->type != ANIMTYPE_GREASE_PENCIL_LAYER) {
631 continue;
632 }
633
634 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ale->id);
635 Layer *layer = reinterpret_cast<Layer *>(ale->data);
637 FramesMapKeyT layer_first_frame = std::numeric_limits<int>::max();
638 FramesMapKeyT layer_last_frame = std::numeric_limits<int>::min();
639 for (auto [frame_number, frame] : layer->frames().items()) {
640 if (frame.is_selected()) {
641 const Drawing *drawing = grease_pencil->get_drawing_at(*layer, frame_number);
642 const int duration = layer->get_frame_duration_at(frame_number);
643 buf.append(
644 {frame_number, Drawing(*drawing), duration, eBezTriple_KeyframeType(frame.type)});
645
646 /* Check the range of this layer only. */
647 layer_first_frame = std::min(frame_number, layer_first_frame);
648 layer_last_frame = std::max(frame_number, layer_last_frame);
649 }
650 }
651 if (!buf.is_empty()) {
652 BLI_assert(!clipboard.copy_buffer.contains(layer->name()));
653 clipboard.copy_buffer.add_new(layer->name(), {buf, layer_first_frame, layer_last_frame});
654 /* Update the range of entire copy buffer. */
655 clipboard.first_frame = std::min(layer_first_frame, clipboard.first_frame);
656 clipboard.last_frame = std::max(layer_last_frame, clipboard.last_frame);
657 }
658 }
659
660 /* In case 'relative' paste method is used. */
661 clipboard.cfra = ac->scene->r.cfra;
662
663 /* Clean up. */
664 ANIM_animdata_freelist(&anim_data);
665
666 /* If nothing ended up in the buffer, copy failed. */
667 return !clipboard.copy_buffer.is_empty();
668}
669
670static int calculate_offset(const eKeyPasteOffset offset_mode,
671 const int cfra,
672 const KeyframeClipboard &clipboard)
673{
674 int offset = 0;
675 switch (offset_mode) {
677 offset = (cfra - clipboard.first_frame);
678 break;
680 offset = (cfra - clipboard.last_frame);
681 break;
683 offset = (cfra - clipboard.cfra);
684 break;
686 offset = 0;
687 break;
688 }
689 return offset;
690}
691
693 const eKeyPasteOffset offset_mode,
694 const eKeyMergeMode merge_mode,
695 const KeyframeClipboard &clipboard)
696{
697 using namespace bke::greasepencil;
698
699 /* Check if buffer is empty. */
700 if (clipboard.copy_buffer.is_empty()) {
701 return false;
702 }
703
704 const int offset = calculate_offset(offset_mode, ac->scene->r.cfra, clipboard);
705
708 ListBase anim_data = {nullptr, nullptr};
709
711 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
712
713 /* Check if single channel in buffer (disregard names if so). */
714 const bool from_single_channel = clipboard.copy_buffer.size() == 1;
715
716 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
717 /* Only deal with GPlayers (case of calls from general dope-sheet). */
718 if (ale->type != ANIMTYPE_GREASE_PENCIL_LAYER) {
719 continue;
720 }
721 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ale->id);
722 Layer &layer = *reinterpret_cast<Layer *>(ale->data);
723 const std::string layer_name = layer.name();
724 if (!from_single_channel && !clipboard.copy_buffer.contains(layer_name)) {
725 continue;
726 }
727 const KeyframeClipboard::LayerBufferItem layer_buffer =
728 from_single_channel ? *clipboard.copy_buffer.values().begin() :
729 clipboard.copy_buffer.lookup(layer_name);
730 bool changed = false;
731
732 /* Mix mode with existing data. */
733 switch (merge_mode) {
735 /* Do nothing. */
736 break;
737
739 /* Remove all keys. */
740 Vector<int> frames_to_remove;
741 for (auto frame_number : layer.frames().keys()) {
742 frames_to_remove.append(frame_number);
743 }
744 grease_pencil->remove_frames(layer, frames_to_remove);
745 changed = true;
746 break;
747 }
750 int frame_min, frame_max;
751
752 if (merge_mode == KEYFRAME_PASTE_MERGE_OVER_RANGE) {
753 /* Entire range of this layer. */
754 frame_min = layer_buffer.first_frame + offset;
755 frame_max = layer_buffer.last_frame + offset;
756 }
757 else {
758 /* Entire range of all copied keys. */
759 frame_min = clipboard.first_frame + offset;
760 frame_max = clipboard.last_frame + offset;
761 }
762
763 /* Remove keys in range. */
764 if (frame_min < frame_max) {
765 Vector<int> frames_to_remove;
766 for (auto frame_number : layer.frames().keys()) {
767 if (frame_min < frame_number && frame_number < frame_max) {
768 frames_to_remove.append(frame_number);
769 }
770 }
771 grease_pencil->remove_frames(layer, frames_to_remove);
772 changed = true;
773 }
774 break;
775 }
776 }
777 for (const KeyframeClipboard::DrawingBufferItem &item : layer_buffer.drawing_buffers) {
778 const int target_frame_number = item.frame_number + offset;
779 if (layer.frames().contains(target_frame_number)) {
780 grease_pencil->remove_frames(layer, {target_frame_number});
781 }
782 Drawing &dst_drawing = *grease_pencil->insert_frame(
783 layer, target_frame_number, item.duration, item.keytype);
784 dst_drawing = item.drawing;
785 changed = true;
786 }
787
788 if (changed) {
789 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
790 }
791 }
792
793 /* Clean up. */
794 ANIM_animdata_freelist(&anim_data);
795
796 return true;
797}
798
800{
801 using namespace blender::bke::greasepencil;
802 Scene *scene = CTX_data_scene(C);
804 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
805 const bool only_active = !RNA_boolean_get(op->ptr, "all");
806 const int current_frame = scene->r.cfra;
807 bool changed = false;
808
809 auto insert_duplicate_frame = [&](Layer &layer, std::optional<int> active_frame_number) {
810 if (!active_frame_number.has_value()) {
811 return false;
812 }
813 return grease_pencil.insert_duplicate_frame(
814 layer, active_frame_number.value(), current_frame, false);
815 };
816
817 if (only_active) {
818 if (!grease_pencil.has_active_layer()) {
819 return OPERATOR_CANCELLED;
820 }
821 Layer &active_layer = *grease_pencil.get_active_layer();
822 const std::optional<int> active_frame_number = active_layer.start_frame_at(current_frame);
823 changed |= insert_duplicate_frame(active_layer, active_frame_number);
824 }
825 else {
826 for (Layer *layer : grease_pencil.layers_for_write()) {
827 const std::optional<int> active_frame_number = layer->start_frame_at(current_frame);
828 changed |= insert_duplicate_frame(*layer, active_frame_number);
829 }
830 }
831
832 if (!changed) {
833 return OPERATOR_CANCELLED;
834 }
835
836 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
838
839 return OPERATOR_FINISHED;
840}
841
843{
844 /* identifiers */
845 ot->name = "Duplicate Active Frame(s)";
846 ot->idname = "GREASE_PENCIL_OT_frame_duplicate";
847 ot->description = "Make a copy of the active Grease Pencil frame(s)";
848
849 /* callback */
852
853 /* flags */
855
857 ot->srna, "all", false, "Duplicate all", "Duplicate active keyframes of all layer");
858}
859
861{
862 using namespace blender::bke::greasepencil;
863 Scene *scene = CTX_data_scene(C);
865 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
866 const bool only_active = !RNA_boolean_get(op->ptr, "all");
867 const int current_frame = scene->r.cfra;
868 bool changed = false;
869
870 if (only_active) {
871 Layer *active_layer = grease_pencil.get_active_layer();
872 if ((active_layer == nullptr) || active_layer->is_locked()) {
873 return OPERATOR_CANCELLED;
874 }
875 if (std::optional<int> active_frame_number = active_layer->start_frame_at(current_frame)) {
876 changed |= grease_pencil.remove_frames(*active_layer, {active_frame_number.value()});
877 }
878 }
879 else {
880 for (Layer *layer : grease_pencil.layers_for_write()) {
881 if (layer->is_locked()) {
882 continue;
883 }
884 if (std::optional<int> active_frame_number = layer->start_frame_at(current_frame)) {
885 changed |= grease_pencil.remove_frames(*layer, {active_frame_number.value()});
886 }
887 }
888 }
889
890 if (!changed) {
891 return OPERATOR_CANCELLED;
892 }
893
894 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
896
897 return OPERATOR_FINISHED;
898}
899
901{
902 /* identifiers */
903 ot->name = "Delete Active Frame(s)";
904 ot->idname = "GREASE_PENCIL_OT_active_frame_delete";
905 ot->description = "Delete the active Grease Pencil frame(s)";
906
907 /* callback */
910
911 /* flags */
913
914 RNA_def_boolean(ot->srna, "all", false, "Delete all", "Delete active keyframes of all layers");
915}
916
918{
920 return false;
921 }
922 const Object &ob = *CTX_data_active_object(C);
923 const Scene &scene = *CTX_data_scene(C);
924
925 /* Ensure that there is a breakdown keyframe visible at the current frame. */
926 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob.data);
927 if (const bke::greasepencil::Layer *active_layer = grease_pencil.get_active_layer()) {
928 const GreasePencilFrame *frame = active_layer->frame_at(scene.r.cfra);
929 if (frame && frame->type == BEZT_KEYTYPE_BREAKDOWN) {
930 return true;
931 }
932 }
933 return false;
934}
935
937 wmOperator * /*op*/)
938{
939 const Object &ob = *CTX_data_active_object(C);
940 const Scene &scene = *CTX_data_scene(C);
941 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob.data);
942 bke::greasepencil::Layer *active_layer = grease_pencil.get_active_layer();
943 const int current_frame = active_layer->start_frame_at(scene.r.cfra).value();
944
945 const Span<int> sorted_keys = active_layer->sorted_keys();
946 const int curr_frame_index = sorted_keys.first_index(current_frame);
947 Vector<int> frame_numbers_to_remove;
948
949 for (int i = curr_frame_index; i <= sorted_keys.size(); i++) {
950 int frame_number = sorted_keys[i];
951 GreasePencilFrame *frame = active_layer->frame_at(frame_number);
952 if (frame && frame->type == BEZT_KEYTYPE_BREAKDOWN) {
953 frame_numbers_to_remove.append(frame_number);
954 continue;
955 }
956 break;
957 }
958 for (int i = curr_frame_index - 1; i >= 0; i--) {
959 int frame_number = sorted_keys[i];
960 GreasePencilFrame *frame = active_layer->frame_at(frame_number);
961 if (frame && frame->type == BEZT_KEYTYPE_BREAKDOWN) {
962 frame_numbers_to_remove.append(frame_number);
963 continue;
964 }
965 break;
966 }
967
968 if (frame_numbers_to_remove.is_empty()) {
969 return OPERATOR_CANCELLED;
970 }
971
972 grease_pencil.remove_frames(*active_layer, frame_numbers_to_remove);
973
974 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
976
977 return OPERATOR_FINISHED;
978}
979
981{
982 /* identifiers */
983 ot->name = "Delete Breakdown Frames";
984 ot->idname = "GREASE_PENCIL_OT_delete_breakdown";
985 ot->description =
986 "Remove breakdown frames generated by interpolating between two Grease Pencil frames";
987
988 /* callback */
991
992 /* flags */
994}
995
996} // namespace blender::ed::greasepencil
997
Functions to insert, delete or modify keyframes.
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Low-level operations for curves.
Low-level operations for grease pencil.
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
MINLINE int round_fl_to_int(float a)
#define IN_RANGE(a, b, c)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
eBezTriple_KeyframeType
@ BEZT_KEYTYPE_BREAKDOWN
@ GP_TOOL_FLAG_RETAIN_LAST
#define MAXFRAME
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ ANIMTYPE_GREASE_PENCIL_LAYER
eAnimCont_Types
eAnimFilter_Flags
@ ANIMFILTER_FOREDIT
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_LIST_VISIBLE
@ ANIMFILTER_NODUPLIS
@ ANIMFILTER_SEL
eEditKeyframes_Mirror
@ MIRROR_KEYS_YAXIS
@ MIRROR_KEYS_MARKER
@ MIRROR_KEYS_CURFRAME
@ MIRROR_KEYS_XAXIS
@ KEYFRAME_PASTE_MERGE_OVER_RANGE_ALL
@ KEYFRAME_PASTE_MERGE_OVER_RANGE
@ KEYFRAME_PASTE_MERGE_OVER
@ KEYFRAME_PASTE_MERGE_MIX
eKeyPasteOffset
@ KEYFRAME_PASTE_OFFSET_NONE
@ KEYFRAME_PASTE_OFFSET_CFRA_END
@ KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE
@ KEYFRAME_PASTE_OFFSET_CFRA_START
@ BEZT_OK_CHANNEL_CIRCLE
@ BEZT_OK_CHANNEL_LASSO
eEditKeyframes_Snap
@ SNAP_KEYS_CURFRAME
@ SNAP_KEYS_NEARMARKER
@ SNAP_KEYS_NEARSEC
@ SELECT_INVERT
@ SELECT_SUBTRACT
@ SELECT_ADD
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
@ 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
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:463
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
int ED_markers_find_nearest_marker_time(ListBase *markers, float x)
TimeMarker * ED_markers_get_first_selected(ListBase *markers)
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
ValueIterator values() const &
Definition BLI_map.hh:884
KeyIterator keys() const &
Definition BLI_map.hh:875
bool contains(const Key &key) const
Definition BLI_map.hh:353
ItemIterator items() const &
Definition BLI_map.hh:902
const void * data() const
constexpr IndexRange drop_back(int64_t n) const
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
constexpr int64_t first_index(const T &search_value) const
Definition BLI_span.hh:377
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr const T * begin() const
Definition BLI_span.hh:220
std::optional< T > get_if_single() const
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
Span< T > as_span() const
GAttributeReader lookup(const StringRef attribute_id) const
Set< StringRefNull > all_ids() const
AttributeAccessor attributes() const
bke::CurvesGeometry & strokes_for_write()
const Map< FramesMapKeyT, GreasePencilFrame > & frames() const
const GreasePencilFrame * frame_at(const int frame_number) const
bool has_drawing_at(const int frame_number) const
int get_frame_duration_at(const int frame_number) const
std::optional< int > start_frame_at(int frame_number) const
Span< FramesMapKeyT > sorted_keys() const
Map< FramesMapKeyT, GreasePencilFrame > & frames_for_write()
const LayerGroup & as_group() const
nullptr float
#define filter
void ED_operatortypes_grease_pencil_frames()
bool keyframe_region_lasso_test(const KeyframeEdit_LassoData *data_lasso, const float xy[2])
bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, const float xy[2])
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
static ulong * next
bool is_autokey_on(const Scene *scene)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
void select_frames_at(bke::greasepencil::LayerGroup &layer_group, const int frame_number, const short select_mode)
bool active_grease_pencil_poll(bContext *C)
static void GREASE_PENCIL_OT_active_frame_delete(wmOperatorType *ot)
bool remove_all_selected_frames(GreasePencil &grease_pencil, bke::greasepencil::Layer &layer)
bool grease_pencil_copy_keyframes(bAnimContext *ac, KeyframeClipboard &clipboard)
bool grease_pencil_paste_keyframes(bAnimContext *ac, const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode, const KeyframeClipboard &clipboard)
bool ensure_active_keyframe(const Scene &scene, GreasePencil &grease_pencil, bke::greasepencil::Layer &layer, const bool duplicate_previous_key, bool &r_inserted_keyframe)
static wmOperatorStatus grease_pencil_delete_breakdown_frames_exec(bContext *C, wmOperator *)
static wmOperatorStatus frame_clean_duplicate_exec(bContext *C, wmOperator *op)
static float get_snapped_frame_number(const float frame_number, Scene &scene, const eEditKeyframes_Snap mode)
static wmOperatorStatus insert_blank_frame_exec(bContext *C, wmOperator *op)
void set_selected_frames_type(bke::greasepencil::Layer &layer, const eBezTriple_KeyframeType key_type)
static wmOperatorStatus grease_pencil_frame_duplicate_exec(bContext *C, wmOperator *op)
bool mirror_selected_frames(GreasePencil &grease_pencil, bke::greasepencil::Layer &layer, Scene &scene, const eEditKeyframes_Mirror mode)
void select_frames_range(bke::greasepencil::TreeNode &node, const float min, const float max, const short select_mode)
static bool curves_geometry_is_equal(const bke::CurvesGeometry &curves_a, const bke::CurvesGeometry &curves_b)
static int get_mirrored_frame_number(const int frame_number, const Scene &scene, const eEditKeyframes_Mirror mode, const TimeMarker *first_selected_marker)
static void select_frame(GreasePencilFrame &frame, const short select_mode)
void create_keyframe_edit_data_selected_frames_list(KeyframeEditData *ked, const bke::greasepencil::Layer &layer)
void select_frames_region(KeyframeEditData *ked, bke::greasepencil::TreeNode &node, const short tool, const short select_mode)
bool has_any_frame_selected(const bke::greasepencil::Layer &layer)
static bool attributes_varrays_not_equal(const bke::GAttributeReader &attrs_a, const bke::GAttributeReader &attrs_b)
static void GREASE_PENCIL_OT_frame_clean_duplicate(wmOperatorType *ot)
static bool attributes_elements_are_equal(const VArray< T > &attributes_a, const VArray< T > &attributes_b)
bool editable_grease_pencil_poll(bContext *C)
void select_all_frames(bke::greasepencil::Layer &layer, const short select_mode)
static void append_frame_to_key_edit_data(KeyframeEditData *ked, const int frame_number, const GreasePencilFrame &frame)
static void GREASE_PENCIL_OT_insert_blank_frame(wmOperatorType *ot)
static void GREASE_PENCIL_OT_frame_duplicate(wmOperatorType *ot)
bool duplicate_selected_frames(GreasePencil &grease_pencil, bke::greasepencil::Layer &layer)
static int calculate_offset(const eKeyPasteOffset offset_mode, const int cfra, const KeyframeClipboard &clipboard)
static wmOperatorStatus grease_pencil_active_frame_delete_exec(bContext *C, wmOperator *op)
static bool grease_pencil_active_breakdown_frame_poll(bContext *C)
static void GREASE_PENCIL_OT_delete_breakdown(wmOperatorType *ot)
static bool attributes_varrays_span_data_equal(const bke::GAttributeReader &attrs_a, const bke::GAttributeReader &attrs_b)
bool select_frame_at(bke::greasepencil::Layer &layer, const int frame_number, const short select_mode)
bool snap_selected_frames(GreasePencil &grease_pencil, bke::greasepencil::Layer &layer, Scene &scene, const eEditKeyframes_Snap mode)
VecBase< float, 2 > float2
#define floorf
int RNA_int_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
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_flag(PropertyRNA *prop, PropertyFlag flag)
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)
#define min(a, b)
Definition sort.cc:36
GreasePencilLayerRuntimeHandle * runtime
struct ToolSettings * toolsettings
struct RenderData r
ListBase markers
eAnimCont_Types datatype
Map< std::string, LayerBufferItem > copy_buffer
struct PointerRNA * ptr
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))