Blender V4.3
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
9#include "BKE_curves.hh"
10#include "BLI_map.hh"
12#include "BLI_utildefines.h"
13
14#include "BKE_context.hh"
15#include "BKE_grease_pencil.hh"
16#include "BKE_paint.hh"
17#include "BKE_report.hh"
18
19#include "DEG_depsgraph.hh"
20
21#include "DNA_layer_types.h"
22#include "DNA_scene_types.h"
23
24#include "ANIM_keyframing.hh"
25
26#include "ED_anim_api.hh"
27#include "ED_grease_pencil.hh"
28#include "ED_keyframes_edit.hh"
29#include "ED_markers.hh"
30
31#include "RNA_access.hh"
32#include "RNA_define.hh"
33
34#include "WM_api.hh"
35
37
39 const eBezTriple_KeyframeType key_type)
40{
41 for (GreasePencilFrame &frame : layer.frames_for_write().values()) {
42 if (frame.is_selected()) {
43 frame.type = key_type;
44 layer.tag_frames_map_changed();
45 }
46 }
47}
48
49static float get_snapped_frame_number(const float frame_number,
50 Scene &scene,
51 const eEditKeyframes_Snap mode)
52{
53 switch (mode) {
54 case SNAP_KEYS_CURFRAME: /* Snap to current frame. */
55 return scene.r.cfra;
56 case SNAP_KEYS_NEARSEC: /* Snap to nearest second. */
57 {
58 float secf = (scene.r.frs_sec / scene.r.frs_sec_base);
59 return floorf(frame_number / secf + 0.5f) * secf;
60 }
61 case SNAP_KEYS_NEARMARKER: /* Snap to nearest marker. */
62 return ED_markers_find_nearest_marker_time(&scene.markers, frame_number);
63 default:
64 break;
65 }
66 return frame_number;
67}
68
71 Scene &scene,
72 const eEditKeyframes_Snap mode)
73{
74 bool changed = false;
75 blender::Map<int, int> frame_number_destinations;
76 for (auto [frame_number, frame] : layer.frames().items()) {
77 if (!frame.is_selected()) {
78 continue;
79 }
80 const int snapped = round_fl_to_int(
81 get_snapped_frame_number(float(frame_number), scene, mode));
82 if (snapped != frame_number) {
83 frame_number_destinations.add(frame_number, snapped);
84 changed = true;
85 }
86 }
87
88 if (changed) {
89 grease_pencil.move_frames(layer, frame_number_destinations);
90 }
91
92 return changed;
93}
94
95static int get_mirrored_frame_number(const int frame_number,
96 const Scene &scene,
97 const eEditKeyframes_Mirror mode,
98 const TimeMarker *first_selected_marker)
99{
100 switch (mode) {
101 case MIRROR_KEYS_CURFRAME: /* Mirror over current frame. */
102 return 2 * scene.r.cfra - frame_number;
104 case MIRROR_KEYS_YAXIS: /* Mirror over frame 0. */
105 return -frame_number;
106 case MIRROR_KEYS_MARKER: /* Mirror over marker. */
107 if (first_selected_marker == nullptr) {
108 break;
109 }
110 return 2 * first_selected_marker->frame - frame_number;
111 default:
112 break;
113 }
114 return frame_number;
115}
116
119 Scene &scene,
120 const eEditKeyframes_Mirror mode)
121{
122 bool changed = false;
123 Map<int, int> frame_number_destinations;
124
125 /* Pre-compute the first selected marker, so that we don't compute it for each frame. */
126 const TimeMarker *first_selected_marker = (mode == MIRROR_KEYS_MARKER) ?
127 ED_markers_get_first_selected(&scene.markers) :
128 nullptr;
129
130 for (auto [frame_number, frame] : layer.frames().items()) {
131 if (!frame.is_selected()) {
132 continue;
133 }
134
135 const int mirrored_frame_number = get_mirrored_frame_number(
136 frame_number, scene, mode, first_selected_marker);
137
138 if (mirrored_frame_number != frame_number) {
139 frame_number_destinations.add(frame_number, mirrored_frame_number);
140 changed = true;
141 }
142 }
143
144 if (changed) {
145 grease_pencil.move_frames(layer, frame_number_destinations);
146 }
147
148 return changed;
149}
150
152{
153 using namespace bke::greasepencil;
154 bool changed = false;
155 LayerTransformData &trans_data = layer.runtime->trans_data_;
156
157 for (auto [frame_number, frame] : layer.frames_for_write().items()) {
158 if (!frame.is_selected()) {
159 continue;
160 }
161
162 /* Create the duplicate drawing. */
163 const Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number);
164 if (drawing == nullptr) {
165 continue;
166 }
167 const int duplicated_drawing_index = grease_pencil.drawings().size();
168 grease_pencil.add_duplicate_drawings(1, *drawing);
169
170 /* Make a copy of the frame in the duplicates. */
171 GreasePencilFrame frame_duplicate = frame;
172 frame_duplicate.drawing_index = duplicated_drawing_index;
173 trans_data.duplicated_frames_buffer.add_overwrite(frame_number, frame_duplicate);
174
175 /* Deselect the current frame, so that only the copy is selected. */
176 frame.flag ^= GP_FRAME_SELECTED;
177
178 changed = true;
179 }
180
181 if (changed) {
182 layer.tag_frames_map_changed();
183 }
184
185 return changed;
186}
187
189{
190 Vector<int> frames_to_remove;
191 for (auto [frame_number, frame] : layer.frames().items()) {
192 if (!frame.is_selected()) {
193 continue;
194 }
195 frames_to_remove.append(frame_number);
196 }
197 return grease_pencil.remove_frames(layer, frames_to_remove.as_span());
198}
199
200static void select_frame(GreasePencilFrame &frame, const short select_mode)
201{
202 switch (select_mode) {
203 case SELECT_ADD:
204 frame.flag |= GP_FRAME_SELECTED;
205 break;
206 case SELECT_SUBTRACT:
207 frame.flag &= ~GP_FRAME_SELECTED;
208 break;
209 case SELECT_INVERT:
210 frame.flag ^= GP_FRAME_SELECTED;
211 break;
212 }
213}
214
216 const int frame_number,
217 const short select_mode)
218{
219
220 GreasePencilFrame *frame = layer.frames_for_write().lookup_ptr(frame_number);
221 if (frame == nullptr) {
222 return false;
223 }
224 select_frame(*frame, select_mode);
225 layer.tag_frames_map_changed();
226 return true;
227}
228
230 const int frame_number,
231 const short select_mode)
232{
234 bke::greasepencil::TreeNode &node = node_->wrap();
235 if (node.is_group()) {
236 select_frames_at(node.as_group(), frame_number, select_mode);
237 }
238 else if (node.is_layer()) {
239 select_frame_at(node.as_layer(), frame_number, select_mode);
240 }
241 }
242}
243
244void select_all_frames(bke::greasepencil::Layer &layer, const short select_mode)
245{
246 for (auto item : layer.frames_for_write().items()) {
247 select_frame(item.value, select_mode);
248 layer.tag_frames_map_changed();
249 }
250}
251
253{
254 for (const auto &[frame_number, frame] : layer.frames().items()) {
255 if (frame.is_selected()) {
256 return true;
257 }
258 }
259 return false;
260}
261
264 const short tool,
265 const short select_mode)
266{
267 if (node.is_layer()) {
268 for (auto [frame_number, frame] : node.as_layer().frames_for_write().items()) {
269 /* Construct a dummy point coordinate to do this testing with. */
270 const float2 pt(float(frame_number), ked->channel_y);
271
272 /* Check the necessary regions. */
273 if (tool == BEZT_OK_CHANNEL_LASSO) {
274 if (keyframe_region_lasso_test(static_cast<const KeyframeEdit_LassoData *>(ked->data), pt))
275 {
276 select_frame(frame, select_mode);
277 }
278 }
279 else if (tool == BEZT_OK_CHANNEL_CIRCLE) {
280 if (keyframe_region_circle_test(static_cast<const KeyframeEdit_CircleData *>(ked->data),
281 pt))
282 {
283 select_frame(frame, select_mode);
284 }
285 }
286
287 node.as_layer().tag_frames_map_changed();
288 }
289 }
290 else if (node.is_group()) {
291 LISTBASE_FOREACH_BACKWARD (GreasePencilLayerTreeNode *, node_, &node.as_group().children) {
292 select_frames_region(ked, node_->wrap(), tool, select_mode);
293 }
294 }
295}
296
298 const float min,
299 const float max,
300 const short select_mode)
301{
302 /* Only select those frames which are in bounds. */
303 if (node.is_layer()) {
304 for (auto [frame_number, frame] : node.as_layer().frames_for_write().items()) {
305 if (IN_RANGE(float(frame_number), min, max)) {
306 select_frame(frame, select_mode);
307 node.as_layer().tag_frames_map_changed();
308 }
309 }
310 }
311 else if (node.is_group()) {
312 LISTBASE_FOREACH_BACKWARD (GreasePencilLayerTreeNode *, node_, &node.as_group().children) {
313 select_frames_range(node_->wrap(), min, max, select_mode);
314 }
315 }
316}
317
319 const int frame_number,
320 const GreasePencilFrame &frame)
321{
322 CfraElem *ce = MEM_cnew<CfraElem>(__func__);
323 ce->cfra = float(frame_number);
324 ce->sel = frame.is_selected();
325 BLI_addtail(&ked->list, ce);
326}
327
329 const bke::greasepencil::Layer &layer)
330{
331 BLI_assert(ked != nullptr);
332
333 for (const auto &[frame_number, frame] : layer.frames().items()) {
334 if (frame.is_selected()) {
335 append_frame_to_key_edit_data(ked, frame_number, frame);
336 }
337 }
338}
339
341 GreasePencil &grease_pencil,
343 const bool duplicate_previous_key,
344 bool &r_inserted_keyframe)
345{
346 const int current_frame = scene.r.cfra;
347 if (!layer.has_drawing_at(current_frame) && !blender::animrig::is_autokey_on(&scene)) {
348 return false;
349 }
350
351 /* If auto-key is on and the drawing at the current frame starts before the current frame a new
352 * keyframe needs to be inserted. */
353 const bool is_first = layer.is_empty() || (layer.sorted_keys().first() > current_frame);
354 const std::optional<int> previous_key_frame_start = layer.start_frame_at(current_frame);
355 const bool has_previous_key = previous_key_frame_start.has_value();
356 const bool needs_new_drawing = is_first || !has_previous_key ||
357 (previous_key_frame_start < current_frame);
358 if (blender::animrig::is_autokey_on(&scene) && needs_new_drawing) {
359 const bool use_additive_drawing = (scene.toolsettings->gpencil_flags &
361 if (has_previous_key && (use_additive_drawing || duplicate_previous_key)) {
362 /* We duplicate the frame that's currently visible and insert it at the current frame. */
363 grease_pencil.insert_duplicate_frame(layer, *previous_key_frame_start, current_frame, false);
364 }
365 else {
366 /* Otherwise we just insert a blank keyframe at the current frame. */
367 grease_pencil.insert_frame(layer, current_frame);
368 }
369 r_inserted_keyframe = true;
370 }
371 /* There should now always be a drawing at the current frame. */
372 BLI_assert(layer.has_drawing_at(current_frame));
373 return true;
374}
375
377{
378 using namespace blender::bke::greasepencil;
379 Scene *scene = CTX_data_scene(C);
380 Object *object = CTX_data_active_object(C);
381 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
382 const int current_frame = scene->r.cfra;
383 const bool all_layers = RNA_boolean_get(op->ptr, "all_layers");
384 const int duration = RNA_int_get(op->ptr, "duration");
385
386 bool changed = false;
387 if (all_layers) {
388 for (Layer *layer : grease_pencil.layers_for_write()) {
389 if (!layer->is_editable()) {
390 continue;
391 }
392 changed |= grease_pencil.insert_frame(*layer, current_frame, duration) != nullptr;
393 }
394 }
395 else {
396 if (!grease_pencil.has_active_layer()) {
397 return OPERATOR_CANCELLED;
398 }
399 changed |= grease_pencil.insert_frame(
400 *grease_pencil.get_active_layer(), current_frame, duration) != nullptr;
401 }
402
403 if (changed) {
404 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
405 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
407 }
408
409 return OPERATOR_FINISHED;
410}
411
413 const bke::GAttributeReader &attrs_b)
414{
415 return (attrs_a.varray.size() != attrs_b.varray.size() ||
416 attrs_a.varray.type() != attrs_b.varray.type());
417}
418
420 const bke::GAttributeReader &attrs_b)
421{
422 if (attrs_a.varray.is_span() && attrs_b.varray.is_span()) {
423 const GSpan attrs_span_a = attrs_a.varray.get_internal_span();
424 const GSpan attrs_span_b = attrs_b.varray.get_internal_span();
425
426 if (attrs_span_a.data() == attrs_span_b.data()) {
427 return true;
428 }
429 }
430
431 return false;
432}
433
434template<typename T>
435static bool attributes_elements_are_equal(const VArray<T> &attributes_a,
436 const VArray<T> &attributes_b)
437{
438 const std::optional<T> value_a = attributes_a.get_if_single();
439 const std::optional<T> value_b = attributes_b.get_if_single();
440 if (value_a.has_value() && value_b.has_value()) {
441 return value_a.value() == value_b.value();
442 }
443
444 const VArraySpan attrs_span_a = attributes_a;
445 const VArraySpan attrs_span_b = attributes_b;
446
447 return std::equal(
448 attrs_span_a.begin(), attrs_span_a.end(), attrs_span_b.begin(), attrs_span_b.end());
449}
450
452 const bke::CurvesGeometry &curves_b)
453{
454 using namespace blender::bke;
455
456 if (curves_a.points_num() == 0 && curves_b.points_num() == 0) {
457 return true;
458 }
459
460 if (curves_a.curves_num() != curves_b.curves_num() ||
461 curves_a.points_num() != curves_b.points_num() || curves_a.offsets() != curves_b.offsets())
462 {
463 return false;
464 }
465
466 const AttributeAccessor attributes_a = curves_a.attributes();
467 const AttributeAccessor attributes_b = curves_b.attributes();
468
469 const Set<StringRefNull> ids_a = attributes_a.all_ids();
470 const Set<StringRefNull> ids_b = attributes_b.all_ids();
471 if (ids_a != ids_b) {
472 return false;
473 }
474
475 for (const StringRef id : ids_a) {
476 GAttributeReader attrs_a = attributes_a.lookup(id);
477 GAttributeReader attrs_b = attributes_b.lookup(id);
478
479 if (attributes_varrays_not_equal(attrs_a, attrs_b)) {
480 return false;
481 }
482
483 if (attributes_varrays_span_data_equal(attrs_a, attrs_b)) {
484 return true;
485 }
486
487 bool attributes_are_equal = true;
488
489 attribute_math::convert_to_static_type(attrs_a.varray.type(), [&](auto dummy) {
490 using T = decltype(dummy);
491
492 const VArray attributes_a = attrs_a.varray.typed<T>();
493 const VArray attributes_b = attrs_b.varray.typed<T>();
494
495 attributes_are_equal = attributes_elements_are_equal(attributes_a, attributes_b);
496 });
497
498 if (!attributes_are_equal) {
499 return false;
500 }
501 }
502
503 return true;
504}
505
507{
508 using namespace blender::bke::greasepencil;
509 Object *object = CTX_data_active_object(C);
510 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
511 const bool selected = RNA_boolean_get(op->ptr, "selected");
512
513 bool changed = false;
514
515 for (Layer *layer : grease_pencil.layers_for_write()) {
516 if (!layer->is_editable()) {
517 continue;
518 }
519
520 Vector<int> start_frame_numbers;
521 for (const FramesMapKeyT key : layer->sorted_keys()) {
522 const GreasePencilFrame *frame = layer->frames().lookup_ptr(key);
523 if (selected && !frame->is_selected()) {
524 continue;
525 }
526 if (frame->is_end()) {
527 continue;
528 }
529 start_frame_numbers.append(int(key));
530 }
531
532 Vector<int> frame_numbers_to_delete;
533 for (const int i : start_frame_numbers.index_range().drop_back(1)) {
534 const int current = start_frame_numbers[i];
535 const int next = start_frame_numbers[i + 1];
536
537 Drawing *drawing = grease_pencil.get_drawing_at(*layer, current);
538 Drawing *drawing_next = grease_pencil.get_drawing_at(*layer, next);
539
540 if (!drawing || !drawing_next) {
541 continue;
542 }
543
544 bke::CurvesGeometry &curves = drawing->strokes_for_write();
545 bke::CurvesGeometry &curves_next = drawing_next->strokes_for_write();
546
547 if (!curves_geometry_is_equal(curves, curves_next)) {
548 continue;
549 }
550
551 frame_numbers_to_delete.append(next);
552 }
553
554 grease_pencil.remove_frames(*layer, frame_numbers_to_delete.as_span());
555
556 changed = true;
557 }
558
559 if (changed) {
560 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
561 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
563 }
564
565 return OPERATOR_FINISHED;
566}
567
569{
570 PropertyRNA *prop;
571
572 /* identifiers */
573 ot->name = "Insert Blank Frame";
574 ot->idname = "GREASE_PENCIL_OT_insert_blank_frame";
575 ot->description = "Insert a blank frame on the current scene frame";
576
577 /* callbacks */
580
582
583 /* properties */
584 prop = RNA_def_boolean(
585 ot->srna, "all_layers", false, "All Layers", "Insert a blank frame in all editable layers");
587 RNA_def_int(ot->srna, "duration", 0, 0, MAXFRAME, "Duration", "", 0, 100);
588}
589
591{
592 PropertyRNA *prop;
593
594 /* identifiers */
595 ot->name = "Delete Duplicate Frames";
596 ot->idname = "GREASE_PENCIL_OT_frame_clean_duplicate";
597 ot->description = "Remove any keyframe that is a duplicate of the previous one";
598
599 /* callbacks */
602
604
605 /* properties */
606 prop = RNA_def_boolean(
607 ot->srna, "selected", false, "Selected", "Only delete selected keyframes");
609}
610
612{
613 using namespace bke::greasepencil;
614
615 /* Clear buffer first. */
616 clipboard.clear();
617
618 /* Filter data. */
620 ListBase anim_data = {nullptr, nullptr};
621
623 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
624
625 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
626 /* This function only deals with grease pencil layer frames.
627 * This check is needed in the case of a call from the main dopesheet. */
628 if (ale->type != ANIMTYPE_GREASE_PENCIL_LAYER) {
629 continue;
630 }
631
632 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ale->id);
633 Layer *layer = reinterpret_cast<Layer *>(ale->data);
635 FramesMapKeyT layer_first_frame = std::numeric_limits<int>::max();
636 FramesMapKeyT layer_last_frame = std::numeric_limits<int>::min();
637 for (auto [frame_number, frame] : layer->frames().items()) {
638 if (frame.is_selected()) {
639 const Drawing *drawing = grease_pencil->get_drawing_at(*layer, frame_number);
640 const int duration = layer->get_frame_duration_at(frame_number);
641 buf.append(
642 {frame_number, Drawing(*drawing), duration, eBezTriple_KeyframeType(frame.type)});
643
644 /* Check the range of this layer only. */
645 if (frame_number < layer_first_frame) {
646 layer_first_frame = frame_number;
647 }
648 if (frame_number > layer_last_frame) {
649 layer_last_frame = frame_number;
650 }
651 }
652 }
653 if (!buf.is_empty()) {
654 BLI_assert(!clipboard.copy_buffer.contains(layer->name()));
655 clipboard.copy_buffer.add_new(layer->name(), {buf, layer_first_frame, layer_last_frame});
656 /* Update the range of entire copy buffer. */
657 if (layer_first_frame < clipboard.first_frame) {
658 clipboard.first_frame = layer_first_frame;
659 }
660 if (layer_last_frame > clipboard.last_frame) {
661 clipboard.last_frame = layer_last_frame;
662 }
663 }
664 }
665
666 /* In case 'relative' paste method is used. */
667 clipboard.cfra = ac->scene->r.cfra;
668
669 /* Clean up. */
670 ANIM_animdata_freelist(&anim_data);
671
672 /* If nothing ended up in the buffer, copy failed. */
673 return !clipboard.copy_buffer.is_empty();
674}
675
676static int calculate_offset(const eKeyPasteOffset offset_mode,
677 const int cfra,
678 const KeyframeClipboard &clipboard)
679{
680 int offset = 0;
681 switch (offset_mode) {
683 offset = (cfra - clipboard.first_frame);
684 break;
686 offset = (cfra - clipboard.last_frame);
687 break;
689 offset = (cfra - clipboard.cfra);
690 break;
692 offset = 0;
693 break;
694 }
695 return offset;
696}
697
699 const eKeyPasteOffset offset_mode,
700 const eKeyMergeMode merge_mode,
701 const KeyframeClipboard &clipboard)
702{
703 using namespace bke::greasepencil;
704
705 /* Check if buffer is empty. */
706 if (clipboard.copy_buffer.is_empty()) {
707 return false;
708 }
709
710 const int offset = calculate_offset(offset_mode, ac->scene->r.cfra, clipboard);
711
714 ListBase anim_data = {nullptr, nullptr};
715
717 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
718
719 /* Check if single channel in buffer (disregard names if so). */
720 const bool from_single_channel = clipboard.copy_buffer.size() == 1;
721
722 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
723 /* Only deal with GPlayers (case of calls from general dopesheet). */
724 if (ale->type != ANIMTYPE_GREASE_PENCIL_LAYER) {
725 continue;
726 }
727 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ale->id);
728 Layer &layer = *reinterpret_cast<Layer *>(ale->data);
729 const std::string layer_name = layer.name();
730 if (!from_single_channel && !clipboard.copy_buffer.contains(layer_name)) {
731 continue;
732 }
733 const KeyframeClipboard::LayerBufferItem layer_buffer =
734 from_single_channel ? *clipboard.copy_buffer.values().begin() :
735 clipboard.copy_buffer.lookup(layer_name);
736 bool changed = false;
737
738 /* Mix mode with existing data. */
739 switch (merge_mode) {
741 /* Do nothing. */
742 break;
743
745 /* Remove all keys. */
746 Vector<int> frames_to_remove;
747 for (auto frame_number : layer.frames().keys()) {
748 frames_to_remove.append(frame_number);
749 }
750 grease_pencil->remove_frames(layer, frames_to_remove);
751 changed = true;
752 break;
753 }
756 int frame_min, frame_max;
757
758 if (merge_mode == KEYFRAME_PASTE_MERGE_OVER_RANGE) {
759 /* Entire range of this layer. */
760 frame_min = layer_buffer.first_frame + offset;
761 frame_max = layer_buffer.last_frame + offset;
762 }
763 else {
764 /* Entire range of all copied keys. */
765 frame_min = clipboard.first_frame + offset;
766 frame_max = clipboard.last_frame + offset;
767 }
768
769 /* Remove keys in range. */
770 if (frame_min < frame_max) {
771 Vector<int> frames_to_remove;
772 for (auto frame_number : layer.frames().keys()) {
773 if (frame_min < frame_number && frame_number < frame_max) {
774 frames_to_remove.append(frame_number);
775 }
776 }
777 grease_pencil->remove_frames(layer, frames_to_remove);
778 changed = true;
779 }
780 break;
781 }
782 }
783 for (const KeyframeClipboard::DrawingBufferItem &item : layer_buffer.drawing_buffers) {
784 const int target_frame_number = item.frame_number + offset;
785 if (layer.frames().contains(target_frame_number)) {
786 grease_pencil->remove_frames(layer, {target_frame_number});
787 }
788 Drawing &dst_drawing = *grease_pencil->insert_frame(
789 layer, target_frame_number, item.duration, item.keytype);
790 dst_drawing = item.drawing;
791 changed = true;
792 }
793
794 if (changed) {
795 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
796 }
797 }
798
799 /* Clean up. */
800 ANIM_animdata_freelist(&anim_data);
801
802 return true;
803}
804
806{
807 using namespace blender::bke::greasepencil;
808 Scene *scene = CTX_data_scene(C);
809 Object *object = CTX_data_active_object(C);
810 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
811 const bool only_active = !RNA_boolean_get(op->ptr, "all");
812 const int current_frame = scene->r.cfra;
813 bool changed = false;
814
815 if (only_active) {
816 if (!grease_pencil.has_active_layer()) {
817 return OPERATOR_CANCELLED;
818 }
819 Layer &active_layer = *grease_pencil.get_active_layer();
820 const std::optional<int> active_frame_number = active_layer.start_frame_at(current_frame);
821 changed |= grease_pencil.insert_duplicate_frame(
822 active_layer, active_frame_number.value(), current_frame, false);
823 }
824 else {
825 for (Layer *layer : grease_pencil.layers_for_write()) {
826 const std::optional<int> active_frame_number = layer->start_frame_at(current_frame);
827 changed |= grease_pencil.insert_duplicate_frame(
828 *layer, active_frame_number.value(), current_frame, false);
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);
864 Object *object = CTX_data_active_object(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 if (!grease_pencil.has_active_layer()) {
872 return OPERATOR_CANCELLED;
873 }
874
875 Layer &active_layer = *grease_pencil.get_active_layer();
876 if (std::optional<int> active_frame_number = active_layer.start_frame_at(current_frame)) {
877 changed |= grease_pencil.remove_frames(active_layer, {active_frame_number.value()});
878 }
879 }
880 else {
881 for (Layer *layer : grease_pencil.layers_for_write()) {
882 if (std::optional<int> active_frame_number = layer->start_frame_at(current_frame)) {
883 changed |= grease_pencil.remove_frames(*layer, {active_frame_number.value()});
884 }
885 }
886 }
887
888 if (!changed) {
889 return OPERATOR_CANCELLED;
890 }
891
892 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
894
895 return OPERATOR_FINISHED;
896}
897
899{
900 /* identifiers */
901 ot->name = "Delete active Frame(s)";
902 ot->idname = "GREASE_PENCIL_OT_active_frame_delete";
903 ot->description = "Delete the active Grease Pencil frame(s)";
904
905 /* callback */
908
909 /* flags */
911
912 RNA_def_boolean(ot->srna, "all", false, "Delete all", "Delete active keyframes of all layer");
913}
914
915} // namespace blender::ed::greasepencil
916
918{
919 using namespace blender::ed::greasepencil;
920 WM_operatortype_append(GREASE_PENCIL_OT_insert_blank_frame);
921 WM_operatortype_append(GREASE_PENCIL_OT_frame_clean_duplicate);
922 WM_operatortype_append(GREASE_PENCIL_OT_frame_duplicate);
923 WM_operatortype_append(GREASE_PENCIL_OT_active_frame_delete);
924}
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:50
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
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:1041
eBezTriple_KeyframeType
@ GP_TOOL_FLAG_RETAIN_LAST
#define MAXFRAME
@ 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:245
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
#define NA_EDITED
Definition WM_types.hh:550
#define NC_GPENCIL
Definition WM_types.hh:366
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:457
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 void * data() const
constexpr IndexRange drop_back(int64_t n) const
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:271
constexpr const T * end() const
Definition BLI_span.hh:225
constexpr const T * begin() const
Definition BLI_span.hh:221
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
bke::CurvesGeometry & strokes_for_write()
std::optional< int > start_frame_at(int frame_number) const
#define floorf(x)
draw_view in_light_buf[] float
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])
static ulong * next
bool is_autokey_on(const Scene *scene)
void select_frames_at(bke::greasepencil::LayerGroup &layer_group, const int frame_number, const short select_mode)
static int insert_blank_frame_exec(bContext *C, wmOperator *op)
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 float get_snapped_frame_number(const float frame_number, Scene &scene, const eEditKeyframes_Snap mode)
void set_selected_frames_type(bke::greasepencil::Layer &layer, const eBezTriple_KeyframeType key_type)
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 int grease_pencil_active_frame_delete_exec(bContext *C, wmOperator *op)
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)
void select_all_frames(bke::greasepencil::Layer &layer, const short select_mode)
static int grease_pencil_frame_duplicate_exec(bContext *C, wmOperator *op)
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 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)
static int frame_clean_duplicate_exec(bContext *C, wmOperator *op)
bool snap_selected_frames(GreasePencil &grease_pencil, bke::greasepencil::Layer &layer, Scene &scene, const eEditKeyframes_Snap mode)
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.c:32
GreasePencilRuntimeHandle * runtime
struct RenderData r
eAnimCont_Types datatype
Map< std::string, LayerBufferItem > copy_buffer
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct PointerRNA * ptr
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))