Blender V4.3
grease_pencil_edit.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 "BLI_array_utils.hh"
10#include "BLI_assert.h"
11#include "BLI_index_mask.hh"
12#include "BLI_index_range.hh"
13#include "BLI_math_base.hh"
14#include "BLI_math_geom.h"
15#include "BLI_math_matrix.h"
16#include "BLI_math_matrix.hh"
17#include "BLI_math_vector.hh"
19#include "BLI_offset_indices.hh"
20#include "BLI_span.hh"
21#include "BLI_string.h"
22#include "BLI_utildefines.h"
23#include "BLT_translation.hh"
24
25#include "DNA_anim_types.h"
26#include "DNA_array_utils.hh"
27#include "DNA_material_types.h"
28#include "DNA_object_types.h"
29#include "DNA_scene_types.h"
30#include "DNA_space_types.h"
31#include "DNA_view3d_types.h"
33
34#include "BKE_anim_data.hh"
35#include "BKE_animsys.h"
36#include "BKE_attribute.hh"
37#include "BKE_context.hh"
38#include "BKE_curves_utils.hh"
39#include "BKE_customdata.hh"
40#include "BKE_deform.hh"
41#include "BKE_fcurve_driver.h"
42#include "BKE_grease_pencil.hh"
43#include "BKE_instances.hh"
44#include "BKE_lib_id.hh"
45#include "BKE_main.hh"
46#include "BKE_material.h"
47#include "BKE_preview_image.hh"
48#include "BKE_report.hh"
49#include "BKE_scene.hh"
50
51#include "RNA_access.hh"
52#include "RNA_define.hh"
53#include "RNA_enum_types.hh"
54
55#include "DEG_depsgraph.hh"
57
58#include "ED_curves.hh"
59#include "ED_grease_pencil.hh"
60#include "ED_object.hh"
62#include "ED_view3d.hh"
63
66#include "GEO_reorder.hh"
67#include "GEO_set_curve_type.hh"
68#include "GEO_smooth_curves.hh"
70
71#include "UI_interface_c.hh"
72
73#include "UI_resources.hh"
74#include <limits>
75
77
78/* -------------------------------------------------------------------- */
81
83{
84 const Scene *scene = CTX_data_scene(C);
86 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
87
88 const int iterations = RNA_int_get(op->ptr, "iterations");
89 const float influence = RNA_float_get(op->ptr, "factor");
90 const bool keep_shape = RNA_boolean_get(op->ptr, "keep_shape");
91 const bool smooth_ends = RNA_boolean_get(op->ptr, "smooth_ends");
92
93 const bool smooth_position = RNA_boolean_get(op->ptr, "smooth_position");
94 const bool smooth_radius = RNA_boolean_get(op->ptr, "smooth_radius");
95 const bool smooth_opacity = RNA_boolean_get(op->ptr, "smooth_opacity");
96
97 if (!(smooth_position || smooth_radius || smooth_opacity)) {
98 /* There's nothing to be smoothed, return. */
99 return OPERATOR_FINISHED;
100 }
101
102 bool changed = false;
103 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
104 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
106 if (curves.points_num() == 0) {
107 return;
108 }
109
110 IndexMaskMemory memory;
112 *object, info.drawing, info.layer_index, memory);
113 if (strokes.is_empty()) {
114 return;
115 }
116
117 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
118 const OffsetIndices points_by_curve = curves.points_by_curve();
119 const VArray<bool> cyclic = curves.cyclic();
120 const VArray<bool> point_selection = *curves.attributes().lookup_or_default<bool>(
121 ".selection", bke::AttrDomain::Point, true);
122
123 if (smooth_position) {
124 bke::GSpanAttributeWriter positions = attributes.lookup_for_write_span("position");
126 points_by_curve,
127 point_selection,
128 cyclic,
129 iterations,
130 influence,
131 smooth_ends,
132 keep_shape,
133 positions.span);
134 positions.finish();
135 changed = true;
136 }
137 if (smooth_opacity && info.drawing.opacities().is_span()) {
138 bke::GSpanAttributeWriter opacities = attributes.lookup_for_write_span("opacity");
140 points_by_curve,
141 point_selection,
142 cyclic,
143 iterations,
144 influence,
145 smooth_ends,
146 false,
147 opacities.span);
148 opacities.finish();
149 changed = true;
150 }
151 if (smooth_radius && info.drawing.radii().is_span()) {
152 bke::GSpanAttributeWriter radii = attributes.lookup_for_write_span("radius");
154 points_by_curve,
155 point_selection,
156 cyclic,
157 iterations,
158 influence,
159 smooth_ends,
160 false,
161 radii.span);
162 radii.finish();
163 changed = true;
164 }
165 });
166
167 if (changed) {
168 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
169 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
170 }
171
172 return OPERATOR_FINISHED;
173}
174
176{
177 PropertyRNA *prop;
178
179 ot->name = "Smooth Stroke";
180 ot->idname = "GREASE_PENCIL_OT_stroke_smooth";
181 ot->description = "Smooth selected strokes";
182
185
187
188 prop = RNA_def_int(ot->srna, "iterations", 10, 1, 100, "Iterations", "", 1, 30);
190 RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 1.0f, "Factor", "", 0.0f, 1.0f);
191 RNA_def_boolean(ot->srna, "smooth_ends", false, "Smooth Endpoints", "");
192 RNA_def_boolean(ot->srna, "keep_shape", false, "Keep Shape", "");
193
194 RNA_def_boolean(ot->srna, "smooth_position", true, "Position", "");
195 RNA_def_boolean(ot->srna, "smooth_radius", true, "Radius", "");
196 RNA_def_boolean(ot->srna, "smooth_opacity", false, "Opacity", "");
197}
198
200
201/* -------------------------------------------------------------------- */
204
206 float3 pos, float3 posA, float3 posB, float val, float valA, float valB)
207{
208 float dist1 = math::distance_squared(posA, pos);
209 float dist2 = math::distance_squared(posB, pos);
210
211 if (dist1 + dist2 > 0) {
212 float interpolated_val = interpf(valB, valA, dist1 / (dist1 + dist2));
213 return math::distance(interpolated_val, val);
214 }
215 return 0.0f;
216}
217
219 const bool cyclic,
220 const float epsilon,
221 const FunctionRef<float(int64_t, int64_t, int64_t)> dist_function,
222 MutableSpan<bool> points_to_delete)
223{
224 int64_t total_points_to_delete = 0;
225 const Span<bool> curve_selection = points_to_delete.slice(points);
226 if (!curve_selection.contains(true)) {
227 return total_points_to_delete;
228 }
229
230 const bool is_last_segment_selected = (curve_selection.first() && curve_selection.last());
231
232 const Vector<IndexRange> selection_ranges = array_utils::find_all_ranges(curve_selection, true);
234 selection_ranges.index_range(), 1024, [&](const IndexRange range_of_ranges) {
235 for (const IndexRange range : selection_ranges.as_span().slice(range_of_ranges)) {
236 total_points_to_delete += ramer_douglas_peucker_simplify(
237 range.shift(points.start()), epsilon, dist_function, points_to_delete);
238 }
239 });
240
241 /* For cyclic curves, simplify the last segment. */
242 if (cyclic && points.size() > 2 && is_last_segment_selected) {
243 const float dist = dist_function(points.last(1), points.first(), points.last());
244 if (dist <= epsilon) {
245 points_to_delete[points.last()] = true;
246 total_points_to_delete++;
247 }
248 }
249
250 return total_points_to_delete;
251}
252
254{
255 const Scene *scene = CTX_data_scene(C);
257 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
258
259 const float epsilon = RNA_float_get(op->ptr, "factor");
260
261 bool changed = false;
262 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
263 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
265 if (curves.points_num() == 0) {
266 return;
267 }
268
269 IndexMaskMemory memory;
271 *object, info.drawing, info.layer_index, memory);
272 if (strokes.is_empty()) {
273 return;
274 }
275
276 const Span<float3> positions = curves.positions();
277 const VArray<float> radii = info.drawing.radii();
278
279 /* Distance functions for `ramer_douglas_peucker_simplify`. */
280 const auto dist_function_positions =
281 [positions](int64_t first_index, int64_t last_index, int64_t index) {
282 const float dist_position = dist_to_line_v3(
283 positions[index], positions[first_index], positions[last_index]);
284 return dist_position;
285 };
286 const auto dist_function_positions_and_radii =
287 [positions, radii](int64_t first_index, int64_t last_index, int64_t index) {
288 const float dist_position = dist_to_line_v3(
289 positions[index], positions[first_index], positions[last_index]);
290 const float dist_radii = dist_to_interpolated(positions[index],
291 positions[first_index],
292 positions[last_index],
293 radii[index],
294 radii[first_index],
295 radii[last_index]);
296 return math::max(dist_position, dist_radii);
297 };
298
299 const VArray<bool> cyclic = curves.cyclic();
300 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
301 const VArray<bool> selection = *curves.attributes().lookup_or_default<bool>(
302 ".selection", bke::AttrDomain::Point, true);
303
304 /* Mark all points in the editable curves to be deleted. */
305 Array<bool> points_to_delete(curves.points_num(), false);
306 bke::curves::fill_points(points_by_curve, strokes, true, points_to_delete.as_mutable_span());
307
308 std::atomic<int64_t> total_points_to_delete = 0;
309 if (radii.is_single()) {
310 strokes.foreach_index([&](const int64_t curve_i) {
311 const IndexRange points = points_by_curve[curve_i];
312 total_points_to_delete += stroke_simplify(points,
313 cyclic[curve_i],
314 epsilon,
315 dist_function_positions,
316 points_to_delete.as_mutable_span());
317 });
318 }
319 else if (radii.is_span()) {
320 strokes.foreach_index([&](const int64_t curve_i) {
321 const IndexRange points = points_by_curve[curve_i];
322 total_points_to_delete += stroke_simplify(points,
323 cyclic[curve_i],
324 epsilon,
325 dist_function_positions_and_radii,
326 points_to_delete.as_mutable_span());
327 });
328 }
329
330 if (total_points_to_delete > 0) {
331 IndexMaskMemory memory;
332 curves.remove_points(IndexMask::from_bools(points_to_delete, memory), {});
334 changed = true;
335 }
336 });
337
338 if (changed) {
339 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
340 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
341 }
342 return OPERATOR_FINISHED;
343}
344
346{
347 PropertyRNA *prop;
348
349 ot->name = "Simplify Stroke";
350 ot->idname = "GREASE_PENCIL_OT_stroke_simplify";
351 ot->description = "Simplify selected strokes";
352
355
357
358 prop = RNA_def_float(ot->srna, "factor", 0.01f, 0.0f, 100.0f, "Factor", "", 0.0f, 100.0f);
360}
361
363
364/* -------------------------------------------------------------------- */
367
369 const IndexMask &mask)
370{
371 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
372 const VArray<bool> src_cyclic = curves.cyclic();
373
374 Array<bool> points_to_delete(curves.points_num());
375 mask.to_bools(points_to_delete.as_mutable_span());
376 const int total_points = points_to_delete.as_span().count(false);
377
378 /* Return if deleting everything. */
379 if (total_points == 0) {
380 return {};
381 }
382
383 int curr_dst_point_id = 0;
384 Array<int> dst_to_src_point(total_points);
385 Vector<int> dst_curve_counts;
386 Vector<int> dst_to_src_curve;
387 Vector<bool> dst_cyclic;
388
389 for (const int curve_i : curves.curves_range()) {
390 const IndexRange points = points_by_curve[curve_i];
391 const Span<bool> curve_points_to_delete = points_to_delete.as_span().slice(points);
392 const bool curve_cyclic = src_cyclic[curve_i];
393
394 /* Note, these ranges start at zero and needed to be shifted by `points.first()` */
395 const Vector<IndexRange> ranges_to_keep = array_utils::find_all_ranges(curve_points_to_delete,
396 false);
397
398 if (ranges_to_keep.is_empty()) {
399 continue;
400 }
401
402 const bool is_last_segment_selected = curve_cyclic && ranges_to_keep.first().first() == 0 &&
403 ranges_to_keep.last().last() == points.size() - 1;
404 const bool is_curve_self_joined = is_last_segment_selected && ranges_to_keep.size() != 1;
405 const bool is_cyclic = ranges_to_keep.size() == 1 && is_last_segment_selected;
406
407 IndexRange range_ids = ranges_to_keep.index_range();
408 /* Skip the first range because it is joined to the end of the last range. */
409 for (const int range_i : ranges_to_keep.index_range().drop_front(is_curve_self_joined)) {
410 const IndexRange range = ranges_to_keep[range_i];
411
412 int count = range.size();
413 for (const int src_point : range.shift(points.first())) {
414 dst_to_src_point[curr_dst_point_id++] = src_point;
415 }
416
417 /* Join the first range to the end of the last range. */
418 if (is_curve_self_joined && range_i == range_ids.last()) {
419 const IndexRange first_range = ranges_to_keep[range_ids.first()];
420 for (const int src_point : first_range.shift(points.first())) {
421 dst_to_src_point[curr_dst_point_id++] = src_point;
422 }
423 count += first_range.size();
424 }
425
426 dst_curve_counts.append(count);
427 dst_to_src_curve.append(curve_i);
428 dst_cyclic.append(is_cyclic);
429 }
430 }
431
432 const int total_curves = dst_to_src_curve.size();
433
434 bke::CurvesGeometry dst_curves(total_points, total_curves);
435
436 BKE_defgroup_copy_list(&dst_curves.vertex_group_names, &curves.vertex_group_names);
437
438 MutableSpan<int> new_curve_offsets = dst_curves.offsets_for_write();
439 array_utils::copy(dst_curve_counts.as_span(), new_curve_offsets.drop_back(1));
441
442 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
443 const bke::AttributeAccessor src_attributes = curves.attributes();
444
445 /* Transfer curve attributes. */
446 gather_attributes(src_attributes,
450 dst_to_src_curve,
451 dst_attributes);
452 array_utils::copy(dst_cyclic.as_span(), dst_curves.cyclic_for_write());
453
454 /* Transfer point attributes. */
455 gather_attributes(src_attributes,
458 {},
459 dst_to_src_point,
460 dst_attributes);
461
462 dst_curves.update_curve_types();
464
465 return dst_curves;
466}
467
469{
470 const Scene *scene = CTX_data_scene(C);
472 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
473
475 scene->toolsettings);
476
477 bool changed = false;
478 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
479 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
480 IndexMaskMemory memory;
482 *object, info.drawing, info.layer_index, selection_domain, memory);
483 if (elements.is_empty()) {
484 return;
485 }
486
488 if (selection_domain == bke::AttrDomain::Curve) {
489 curves.remove_curves(elements, {});
490 }
491 else if (selection_domain == bke::AttrDomain::Point) {
493 }
495 changed = true;
496 });
497
498 if (changed) {
499 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
500 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
501 }
502 return OPERATOR_FINISHED;
503}
504
506{
507 ot->name = "Delete";
508 ot->idname = "GREASE_PENCIL_OT_delete";
509 ot->description = "Delete selected strokes or points";
510
513
515}
516
518
519/* -------------------------------------------------------------------- */
522
523enum class DissolveMode : int8_t {
530};
531
533 {int(DissolveMode::POINTS), "POINTS", 0, "Dissolve", "Dissolve selected points"},
535 "BETWEEN",
536 0,
537 "Dissolve Between",
538 "Dissolve points between selected points"},
540 "UNSELECT",
541 0,
542 "Dissolve Unselect",
543 "Dissolve all unselected points"},
544 {0, nullptr, 0, nullptr, nullptr},
545};
546
548 const IndexMask &mask,
549 const DissolveMode mode)
550{
551 const VArray<bool> selection = *curves.attributes().lookup_or_default<bool>(
552 ".selection", bke::AttrDomain::Point, true);
553
554 Array<bool> points_to_dissolve(curves.points_num(), false);
555 selection.materialize(mask, points_to_dissolve);
556
557 if (mode == DissolveMode::POINTS) {
558 return points_to_dissolve;
559 }
560
561 /* Both `between` and `unselect` have the unselected point being the ones dissolved so we need
562 * to invert. */
564
565 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
566 /* Because we are going to invert, these become the points to keep. */
567 MutableSpan<bool> points_to_keep = points_to_dissolve.as_mutable_span();
568
569 threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) {
570 for (const int64_t curve_i : range) {
571 const IndexRange points = points_by_curve[curve_i];
572 const Span<bool> curve_selection = points_to_dissolve.as_span().slice(points);
573 /* The unselected curves should not be dissolved. */
574 if (!curve_selection.contains(true)) {
575 points_to_keep.slice(points).fill(true);
576 continue;
577 }
578
579 /* `between` is just `unselect` but with the first and last segments not getting
580 * dissolved. */
581 if (mode != DissolveMode::BETWEEN) {
582 continue;
583 }
584
585 const Vector<IndexRange> deselection_ranges = array_utils::find_all_ranges(curve_selection,
586 false);
587
588 if (deselection_ranges.size() != 0) {
589 const IndexRange first_range = deselection_ranges.first().shift(points.first());
590 const IndexRange last_range = deselection_ranges.last().shift(points.first());
591
592 /* Ranges should only be fill if the first/last point matches the start/end point
593 * of the segment. */
594 if (first_range.first() == points.first()) {
595 points_to_keep.slice(first_range).fill(true);
596 }
597 if (last_range.last() == points.last()) {
598 points_to_keep.slice(last_range).fill(true);
599 }
600 }
601 }
602 });
603
604 array_utils::invert_booleans(points_to_dissolve);
605
606 return points_to_dissolve;
607}
608
610{
611 const Scene *scene = CTX_data_scene(C);
613 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
614
615 const DissolveMode mode = DissolveMode(RNA_enum_get(op->ptr, "type"));
616
617 bool changed = false;
618 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
619 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
621 if (curves.points_num() == 0) {
622 return;
623 }
624
625 IndexMaskMemory memory;
627 *object, info.drawing, info.layer_index, memory);
628 if (points.is_empty()) {
629 return;
630 }
631
632 const Array<bool> points_to_dissolve = get_points_to_dissolve(curves, points, mode);
633 if (points_to_dissolve.as_span().contains(true)) {
634 curves.remove_points(IndexMask::from_bools(points_to_dissolve, memory), {});
636 changed = true;
637 }
638 });
639
640 if (changed) {
641 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
642 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
643 }
644 return OPERATOR_FINISHED;
645}
646
648{
649 PropertyRNA *prop;
650
651 ot->name = "Dissolve";
652 ot->idname = "GREASE_PENCIL_OT_dissolve";
653 ot->description = "Delete selected points without splitting strokes";
654
655 ot->invoke = WM_menu_invoke;
658
660
661 ot->prop = prop = RNA_def_enum(ot->srna,
662 "type",
664 0,
665 "Type",
666 "Method used for dissolving stroke points");
669}
670
672
673/* -------------------------------------------------------------------- */
676
683
686 "ACTIVE_FRAME",
687 0,
688 "Active Frame",
689 "Deletes current frame in the active layer"},
691 "ALL_FRAMES",
692 0,
693 "All Active Frames",
694 "Delete active frames for all layers"},
695 {0, nullptr, 0, nullptr, nullptr},
696};
697
699{
700 const Scene *scene = CTX_data_scene(C);
702 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
703 const int current_frame = scene->r.cfra;
704
705 const DeleteFrameMode mode = DeleteFrameMode(RNA_enum_get(op->ptr, "type"));
706
707 bool changed = false;
708 if (mode == DeleteFrameMode::ACTIVE_FRAME && grease_pencil.has_active_layer()) {
709 bke::greasepencil::Layer &layer = *grease_pencil.get_active_layer();
710 if (layer.is_editable() && layer.start_frame_at(current_frame)) {
711 changed |= grease_pencil.remove_frames(layer, {*layer.start_frame_at(current_frame)});
712 }
713 }
714 else if (mode == DeleteFrameMode::ALL_FRAMES) {
715 for (bke::greasepencil::Layer *layer : grease_pencil.layers_for_write()) {
716 if (layer->is_editable() && layer->start_frame_at(current_frame)) {
717 changed |= grease_pencil.remove_frames(*layer, {*layer->start_frame_at(current_frame)});
718 }
719 }
720 }
721
722 if (changed) {
723 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
724 WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_EDITED, &grease_pencil);
726 }
727
728 return OPERATOR_FINISHED;
729}
730
732{
733 PropertyRNA *prop;
734
735 ot->name = "Delete Frame";
736 ot->idname = "GREASE_PENCIL_OT_delete_frame";
737 ot->description = "Delete Grease Pencil Frame(s)";
738
739 ot->invoke = WM_menu_invoke;
742
744
745 ot->prop = prop = RNA_def_enum(ot->srna,
746 "type",
748 0,
749 "Type",
750 "Method used for deleting Grease Pencil frames");
752}
753
754
755/* -------------------------------------------------------------------- */
758
760{
761 using namespace blender;
762 Main *bmain = CTX_data_main(C);
763 const Scene *scene = CTX_data_scene(C);
765 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
766 Material *ma = nullptr;
767 char name[MAX_ID_NAME - 2];
768 RNA_string_get(op->ptr, "material", name);
769
770 int material_index = object->actcol - 1;
771
772 if (name[0] != '\0') {
773 ma = reinterpret_cast<Material *>(BKE_libblock_find_name(bmain, ID_MA, name));
774 if (ma == nullptr) {
775 BKE_reportf(op->reports, RPT_WARNING, TIP_("Material '%s' could not be found"), name);
776 return OPERATOR_CANCELLED;
777 }
778
779 /* Find slot index. */
780 material_index = BKE_object_material_index_get(object, ma);
781 }
782
783 if (material_index == -1) {
784 return OPERATOR_CANCELLED;
785 }
786
787 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
788 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
789 IndexMaskMemory memory;
791 *object, info.drawing, info.layer_index, memory);
792 if (strokes.is_empty()) {
793 return;
794 }
795
798 curves.attributes_for_write().lookup_or_add_for_write_span<int>("material_index",
800 index_mask::masked_fill(materials.span, material_index, strokes);
801 materials.finish();
802 });
803
804 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
805 WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_EDITED, &grease_pencil);
806
807 return OPERATOR_FINISHED;
808}
809
811{
812 ot->name = "Assign Material";
813 ot->idname = "GREASE_PENCIL_OT_stroke_material_set";
814 ot->description = "Assign the active material slot to the selected strokes";
815
818
820
821 ot->prop = RNA_def_string(
822 ot->srna, "material", nullptr, MAX_ID_NAME - 2, "Material", "Name of the material");
824}
825
826
827/* -------------------------------------------------------------------- */
830
831enum class CyclicalMode : int8_t {
833 CLOSE = 0,
835 OPEN = 1,
838};
839
841 {int(CyclicalMode::CLOSE), "CLOSE", 0, "Close All", ""},
842 {int(CyclicalMode::OPEN), "OPEN", 0, "Open All", ""},
843 {int(CyclicalMode::TOGGLE), "TOGGLE", 0, "Toggle", ""},
844 {0, nullptr, 0, nullptr, nullptr},
845};
846
848{
849 const Scene *scene = CTX_data_scene(C);
851 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
852
853 const CyclicalMode mode = CyclicalMode(RNA_enum_get(op->ptr, "type"));
854
855 bool changed = false;
856 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
857 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
859 if (mode == CyclicalMode::OPEN && !curves.attributes().contains("cyclic")) {
860 /* Avoid creating unneeded attribute. */
861 return;
862 }
863
864 IndexMaskMemory memory;
866 *object, info.drawing, info.layer_index, memory);
867 if (strokes.is_empty()) {
868 return;
869 }
870
871 MutableSpan<bool> cyclic = curves.cyclic_for_write();
872 switch (mode) {
874 index_mask::masked_fill(cyclic, true, strokes);
875 break;
877 index_mask::masked_fill(cyclic, false, strokes);
878 break;
880 array_utils::invert_booleans(cyclic, strokes);
881 break;
882 }
883
884 /* Remove the attribute if it is empty. */
885 if (mode != CyclicalMode::CLOSE) {
887 curves.attributes_for_write().remove("cyclic");
888 }
889 }
890
892 changed = true;
893 });
894
895 if (changed) {
896 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
897 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
898 }
899
900 return OPERATOR_FINISHED;
901}
902
904{
905 ot->name = "Set Cyclical State";
906 ot->idname = "GREASE_PENCIL_OT_cyclical_set";
907 ot->description = "Close or open the selected stroke adding a segment from last to first point";
908
909 ot->invoke = WM_menu_invoke;
912
914
915 ot->prop = RNA_def_enum(
916 ot->srna, "type", prop_cyclical_types, int(CyclicalMode::TOGGLE), "Type", "");
917}
918
920
921/* -------------------------------------------------------------------- */
924
926{
927 const Scene *scene = CTX_data_scene(C);
929 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
930
931 if (object->totcol == 0) {
932 return OPERATOR_CANCELLED;
933 }
934
935 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
936 for (const MutableDrawingInfo &info : drawings) {
937 IndexMaskMemory memory;
939 *object, info.drawing, info.layer_index, memory);
940 if (strokes.is_empty()) {
941 continue;
942 }
943 bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
944
945 const VArray<int> materials = *curves.attributes().lookup_or_default<int>(
946 "material_index", bke::AttrDomain::Curve, 0);
947 object->actcol = materials[strokes.first()] + 1;
948 break;
949 };
950
951 WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_EDITED, &grease_pencil);
952
953 return OPERATOR_FINISHED;
954}
955
957{
958 ot->name = "Set Active Material";
959 ot->idname = "GREASE_PENCIL_OT_set_active_material";
960 ot->description = "Set the selected stroke material as the active material";
961
964
966}
967
968
969/* -------------------------------------------------------------------- */
972
974{
975 const Scene *scene = CTX_data_scene(C);
977 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
978
979 /* Radius is half of the thickness. */
980 const float radius = RNA_float_get(op->ptr, "thickness") * 0.5f;
981
982 bool changed = false;
983 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
984 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
985 IndexMaskMemory memory;
987 *object, info.drawing, info.layer_index, memory);
988 if (strokes.is_empty()) {
989 return;
990 }
992
993 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
995 bke::curves::fill_points<float>(points_by_curve, strokes, radius, radii);
996 changed = true;
997 });
998
999 if (changed) {
1000 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1001 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
1002 }
1003
1004 return OPERATOR_FINISHED;
1005}
1006
1008{
1009 ot->name = "Set Uniform Thickness";
1010 ot->idname = "GREASE_PENCIL_OT_set_uniform_thickness";
1011 ot->description = "Set all stroke points to same thickness";
1012
1015
1016 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1017
1018 ot->prop = RNA_def_float(
1019 ot->srna, "thickness", 0.1f, 0.0f, 1000.0f, "Thickness", "Thickness", 0.0f, 1000.0f);
1020}
1021
1023/* -------------------------------------------------------------------- */
1026
1028{
1029 const Scene *scene = CTX_data_scene(C);
1030 Object *object = CTX_data_active_object(C);
1031 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1032
1033 const float opacity = RNA_float_get(op->ptr, "opacity");
1034
1035 bool changed = false;
1036 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
1037 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
1038 IndexMaskMemory memory;
1040 *object, info.drawing, info.layer_index, memory);
1041 if (strokes.is_empty()) {
1042 return;
1043 }
1045
1046 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
1048 bke::curves::fill_points<float>(points_by_curve, strokes, opacity, opacities);
1049 changed = true;
1050 });
1051
1052 if (changed) {
1053 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1054 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
1055 }
1056
1057 return OPERATOR_FINISHED;
1058}
1059
1061{
1062 ot->name = "Set Uniform Opacity";
1063 ot->idname = "GREASE_PENCIL_OT_set_uniform_opacity";
1064 ot->description = "Set all stroke points to same opacity";
1065
1068
1069 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1070
1071 ot->prop = RNA_def_float(ot->srna, "opacity", 1.0f, 0.0f, 1.0f, "Opacity", "", 0.0f, 1.0f);
1072}
1073
1075
1076/* -------------------------------------------------------------------- */
1079
1081{
1082 const Scene *scene = CTX_data_scene(C);
1083 Object *object = CTX_data_active_object(C);
1084 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1085
1086 bool changed = false;
1087 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
1088 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
1089 IndexMaskMemory memory;
1091 *object, info.drawing, info.layer_index, memory);
1092 if (strokes.is_empty()) {
1093 return;
1094 }
1096
1097 /* Switch stroke direction. */
1098 curves.reverse_curves(strokes);
1099
1100 changed = true;
1101 });
1102
1103 if (changed) {
1104 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1105 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
1106 }
1107
1108 return OPERATOR_FINISHED;
1109}
1110
1112{
1113 /* identifiers */
1114 ot->name = "Switch Direction";
1115 ot->idname = "GREASE_PENCIL_OT_stroke_switch_direction";
1116 ot->description = "Change direction of the points of the selected strokes";
1117
1118 /* Callbacks. */
1121
1122 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1123}
1124
1126
1127/* -------------------------------------------------------------------- */
1130
1131enum class CapsMode : int8_t {
1133 FLAT = 0,
1137 END = 2,
1140};
1141
1142static void toggle_caps(MutableSpan<int8_t> caps, const IndexMask &strokes)
1143{
1144 strokes.foreach_index([&](const int stroke_i) {
1145 if (caps[stroke_i] == GP_STROKE_CAP_FLAT) {
1146 caps[stroke_i] = GP_STROKE_CAP_ROUND;
1147 }
1148 else {
1149 caps[stroke_i] = GP_STROKE_CAP_FLAT;
1150 }
1151 });
1152}
1153
1155{
1156 const Scene *scene = CTX_data_scene(C);
1157 Object *object = CTX_data_active_object(C);
1158 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1159
1160 const CapsMode mode = CapsMode(RNA_enum_get(op->ptr, "type"));
1161
1162 bool changed = false;
1163 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
1164 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
1166 IndexMaskMemory memory;
1168 *object, info.drawing, info.layer_index, memory);
1169 if (strokes.is_empty()) {
1170 return;
1171 }
1172
1173 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1174
1175 if (ELEM(mode, CapsMode::ROUND, CapsMode::FLAT)) {
1179 "end_cap", bke::AttrDomain::Curve);
1180
1181 const int8_t flag_set = (mode == CapsMode::ROUND) ? int8_t(GP_STROKE_CAP_TYPE_ROUND) :
1183
1184 index_mask::masked_fill(start_caps.span, flag_set, strokes);
1185 index_mask::masked_fill(end_caps.span, flag_set, strokes);
1186 start_caps.finish();
1187 end_caps.finish();
1188 }
1189 else {
1190 switch (mode) {
1191 case CapsMode::START: {
1193 "start_cap", bke::AttrDomain::Curve);
1194 toggle_caps(caps.span, strokes);
1195 caps.finish();
1196 break;
1197 }
1198 case CapsMode::END: {
1200 "end_cap", bke::AttrDomain::Curve);
1201 toggle_caps(caps.span, strokes);
1202 caps.finish();
1203 break;
1204 }
1205 case CapsMode::ROUND:
1206 case CapsMode::FLAT:
1207 break;
1208 }
1209 }
1210
1211 changed = true;
1212 });
1213
1214 if (changed) {
1215 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1216 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
1217 }
1218
1219 return OPERATOR_FINISHED;
1220}
1221
1223{
1224 static const EnumPropertyItem prop_caps_types[] = {
1225 {int(CapsMode::ROUND), "ROUND", 0, "Rounded", "Set as default rounded"},
1226 {int(CapsMode::FLAT), "FLAT", 0, "Flat", ""},
1228 {int(CapsMode::START), "START", 0, "Toggle Start", ""},
1229 {int(CapsMode::END), "END", 0, "Toggle End", ""},
1230 {0, nullptr, 0, nullptr, nullptr},
1231 };
1232
1233 ot->name = "Set Curve Caps";
1234 ot->idname = "GREASE_PENCIL_OT_caps_set";
1235 ot->description = "Change curve caps mode (rounded or flat)";
1236
1237 ot->invoke = WM_menu_invoke;
1240
1241 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1242
1243 ot->prop = RNA_def_enum(ot->srna, "type", prop_caps_types, int(CapsMode::ROUND), "Type", "");
1244}
1245
1247
1248/* -------------------------------------------------------------------- */
1251
1252/* Retry enum items with object materials. */
1254 PointerRNA * /*ptr*/,
1255 PropertyRNA * /*prop*/,
1256 bool *r_free)
1257{
1259 EnumPropertyItem *item = nullptr, item_tmp = {0};
1260 int totitem = 0;
1261
1262 if (ob == nullptr) {
1264 }
1265
1266 /* Existing materials */
1267 for (const int i : IndexRange(ob->totcol)) {
1268 if (Material *ma = BKE_object_material_get(ob, i + 1)) {
1269 item_tmp.identifier = ma->id.name + 2;
1270 item_tmp.name = ma->id.name + 2;
1271 item_tmp.value = i + 1;
1272 item_tmp.icon = ma->preview ? ma->preview->runtime->icon_id : ICON_NONE;
1273
1274 RNA_enum_item_add(&item, &totitem, &item_tmp);
1275 }
1276 }
1277 RNA_enum_item_end(&item, &totitem);
1278 *r_free = true;
1279
1280 return item;
1281}
1282
1284{
1285 Object *object = CTX_data_active_object(C);
1286 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1287 const int slot = RNA_enum_get(op->ptr, "slot");
1288
1289 /* Try to get material slot. */
1290 if ((slot < 1) || (slot > object->totcol)) {
1291 return OPERATOR_CANCELLED;
1292 }
1293
1294 /* Set active material. */
1295 object->actcol = slot;
1296
1297 WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_EDITED, &grease_pencil);
1298
1299 return OPERATOR_FINISHED;
1300}
1301
1303{
1304 ot->name = "Set Active Material";
1305 ot->idname = "GREASE_PENCIL_OT_set_material";
1306 ot->description = "Set active material";
1307
1310
1311 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1312
1313 /* Material to use (dynamic enum) */
1314 ot->prop = RNA_def_enum(ot->srna, "slot", rna_enum_dummy_DEFAULT_items, 0, "Material Slot", "");
1316}
1317
1318
1319/* -------------------------------------------------------------------- */
1322
1324{
1325 const Scene *scene = CTX_data_scene(C);
1326 Object *object = CTX_data_active_object(C);
1327 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1328
1330 scene->toolsettings);
1331
1332 std::atomic<bool> changed = false;
1333 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
1334 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
1335 IndexMaskMemory memory;
1337 *object, info.drawing, info.layer_index, selection_domain, memory);
1338 if (elements.is_empty()) {
1339 return;
1340 }
1341
1343 if (selection_domain == bke::AttrDomain::Curve) {
1345 }
1346 else if (selection_domain == bke::AttrDomain::Point) {
1348 }
1350 changed.store(true, std::memory_order_relaxed);
1351 });
1352
1353 if (changed) {
1354 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1355 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
1356 }
1357 return OPERATOR_FINISHED;
1358}
1359
1361{
1362 ot->name = "Duplicate";
1363 ot->idname = "GREASE_PENCIL_OT_duplicate";
1364 ot->description = "Duplicate the selected points";
1365
1368
1369 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1370}
1371
1373{
1374 Object *object = CTX_data_active_object(C);
1375 Scene &scene = *CTX_data_scene(C);
1376 const int limit = RNA_int_get(op->ptr, "limit");
1377
1378 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1379 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
1380
1381 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
1383 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
1384
1385 IndexMaskMemory memory;
1387 *object, info.drawing, info.layer_index, memory);
1388
1389 const IndexMask curves_to_delete = IndexMask::from_predicate(
1390 editable_strokes, GrainSize(4096), memory, [&](const int i) {
1391 return points_by_curve[i].size() <= limit;
1392 });
1393
1394 curves.remove_curves(curves_to_delete, {});
1395 });
1396
1397 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1398 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
1399
1400 return OPERATOR_FINISHED;
1401}
1402
1404{
1406 C, op, event, IFACE_("Remove Loose Points"), IFACE_("Delete"));
1407}
1408
1410{
1411 ot->name = "Clean Loose Points";
1412 ot->idname = "GREASE_PENCIL_OT_clean_loose";
1413 ot->description = "Remove loose points";
1414
1418
1419 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1420
1421 RNA_def_int(ot->srna,
1422 "limit",
1423 1,
1424 1,
1425 INT_MAX,
1426 "Limit",
1427 "Number of points to consider stroke as loose",
1428 1,
1429 INT_MAX);
1430}
1431
1433
1434/* -------------------------------------------------------------------- */
1437
1439{
1440 const int cuts = RNA_int_get(op->ptr, "number_cuts");
1441 const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
1442
1443 std::atomic<bool> changed = false;
1444
1445 const Scene *scene = CTX_data_scene(C);
1446 Object *object = CTX_data_active_object(C);
1447 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1449 scene->toolsettings);
1450
1451 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
1452
1453 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
1454 IndexMaskMemory memory;
1456 *object, info.drawing, info.layer_index, memory);
1457 if (strokes.is_empty()) {
1458 return;
1459 }
1461
1462 VArray<int> vcuts = {};
1463
1464 if (selection_domain == bke::AttrDomain::Curve || !only_selected) {
1465 /* Subdivide entire selected curve, every stroke subdivides to the same cut. */
1466 vcuts = VArray<int>::ForSingle(cuts, curves.points_num());
1467 }
1468 else if (selection_domain == bke::AttrDomain::Point) {
1469 /* Subdivide between selected points. Only cut between selected points.
1470 * Make the cut array the same length as point count for specifying
1471 * cut/uncut for each segment. */
1472 const VArray<bool> selection = *curves.attributes().lookup_or_default<bool>(
1473 ".selection", bke::AttrDomain::Point, true);
1474
1475 const OffsetIndices points_by_curve = curves.points_by_curve();
1476 const VArray<bool> cyclic = curves.cyclic();
1477
1478 Array<int> use_cuts(curves.points_num(), 0);
1479
1480 /* The cut is after each point, so the last point selected wouldn't need to be registered. */
1481 for (const int curve : curves.curves_range()) {
1482 /* No need to loop to the last point since the cut is registered on the point before the
1483 * segment. */
1484 for (const int point : points_by_curve[curve].drop_back(1)) {
1485 /* The point itself should be selected. */
1486 if (!selection[point]) {
1487 continue;
1488 }
1489 /* If the next point in the curve is selected, then cut this segment. */
1490 if (selection[point + 1]) {
1491 use_cuts[point] = cuts;
1492 }
1493 }
1494 /* Check for cyclic and selection. */
1495 if (cyclic[curve]) {
1496 const int first_point = points_by_curve[curve].first();
1497 const int last_point = points_by_curve[curve].last();
1498 if (selection[first_point] && selection[last_point]) {
1499 use_cuts[last_point] = cuts;
1500 }
1501 }
1502 }
1503 vcuts = VArray<int>::ForContainer(std::move(use_cuts));
1504 }
1505
1506 curves = geometry::subdivide_curves(curves, strokes, vcuts);
1508 changed.store(true, std::memory_order_relaxed);
1509 });
1510
1511 if (changed) {
1512 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1514 }
1515
1516 return OPERATOR_FINISHED;
1517}
1518
1520{
1521 PropertyRNA *prop;
1522
1523 ot->name = "Subdivide Stroke";
1524 ot->idname = "GREASE_PENCIL_OT_stroke_subdivide";
1525 ot->description =
1526 "Subdivide between continuous selected points of the stroke adding a point half way "
1527 "between "
1528 "them";
1529
1532
1533 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1534
1535 prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 32, "Number of Cuts", "", 1, 5);
1536 /* Avoid re-using last var because it can cause _very_ high value and annoy users. */
1538
1539 RNA_def_boolean(ot->srna,
1540 "only_selected",
1541 true,
1542 "Selected Points",
1543 "Smooth only selected points in the stroke");
1544}
1545
1547
1548/* -------------------------------------------------------------------- */
1551
1554 TOP = 0,
1556 UP = 1,
1558 DOWN = 2,
1561};
1562
1564 const IndexMask &selected,
1565 const ReorderDirection direction)
1566{
1567 Array<int> indices(universe.size());
1568
1570 /* Initialize the indices. */
1572 }
1573
1575 /*
1576 * Take the selected indices and move them to the start for `Bottom` or the end for `Top`
1577 * And fill the reset with the unselected indices.
1578 *
1579 * Here's a diagram:
1580 *
1581 * Input
1582 * 0 1 2 3 4 5 6 7 8 9
1583 * ^ ^ ^
1584 *
1585 * Top
1586 * |-----A-----| |-B-|
1587 * 0 1 3 6 7 8 9 2 4 5
1588 * ^ ^ ^
1589 *
1590 * Bottom
1591 * |-A-| |-----B-----|
1592 * 2 4 5 0 1 3 6 7 8 9
1593 * ^ ^ ^
1594 */
1595
1596 IndexMaskMemory memory;
1597 const IndexMask unselected = selected.complement(universe, memory);
1598
1599 const IndexMask &A = (direction == ReorderDirection::BOTTOM) ? selected : unselected;
1600 const IndexMask &B = (direction == ReorderDirection::BOTTOM) ? unselected : selected;
1601
1602 A.to_indices(indices.as_mutable_span().take_front(A.size()));
1603 B.to_indices(indices.as_mutable_span().take_back(B.size()));
1604 }
1605 else if (direction == ReorderDirection::DOWN) {
1606 selected.foreach_index_optimized<int>([&](const int curve_i, const int pos) {
1607 /* Check if the curve index is touching the beginning without any gaps. */
1608 if (curve_i != pos) {
1609 /* Move a index down by flipping it with the one below it. */
1610 std::swap(indices[curve_i], indices[curve_i - 1]);
1611 }
1612 });
1613 }
1614 else if (direction == ReorderDirection::UP) {
1615 Array<int> selected_indices(selected.size());
1616 selected.to_indices(selected_indices.as_mutable_span());
1617
1618 /* Because each index is moving up we need to loop through the indices backwards,
1619 * starting at the largest. */
1620 for (const int i : selected_indices.index_range()) {
1621 const int pos = selected_indices.index_range().last(i);
1622 const int curve_i = selected_indices[pos];
1623
1624 /* Check if the curve index is touching the end without any gaps. */
1625 if (curve_i != universe.last(i)) {
1626 /* Move a index up by flipping it with the one above it. */
1627 std::swap(indices[curve_i], indices[curve_i + 1]);
1628 }
1629 }
1630 }
1631
1632 return indices;
1633}
1634
1636{
1637 const Scene *scene = CTX_data_scene(C);
1638 Object *object = CTX_data_active_object(C);
1639 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1640
1641 const ReorderDirection direction = ReorderDirection(RNA_enum_get(op->ptr, "direction"));
1642
1643 std::atomic<bool> changed = false;
1644 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
1645 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
1646 IndexMaskMemory memory;
1648 *object, info.drawing, info.layer_index, memory);
1649 if (strokes.is_empty()) {
1650 return;
1651 }
1653
1654 /* Return if everything is selected. */
1655 if (strokes.size() == curves.curves_num()) {
1656 return;
1657 }
1658
1659 const Array<int> indices = get_reordered_indices(curves.curves_range(), strokes, direction);
1660
1663 changed.store(true, std::memory_order_relaxed);
1664 });
1665
1666 if (changed) {
1667 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1668 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
1669 }
1670
1671 return OPERATOR_FINISHED;
1672}
1673
1675{
1676 static const EnumPropertyItem prop_reorder_direction[] = {
1677 {int(ReorderDirection::TOP), "TOP", 0, "Bring to Front", ""},
1678 {int(ReorderDirection::UP), "UP", 0, "Bring Forward", ""},
1680 {int(ReorderDirection::DOWN), "DOWN", 0, "Send Backward", ""},
1681 {int(ReorderDirection::BOTTOM), "BOTTOM", 0, "Send to Back", ""},
1682 {0, nullptr, 0, nullptr, nullptr},
1683 };
1684
1685 ot->name = "Reorder";
1686 ot->idname = "GREASE_PENCIL_OT_reorder";
1687 ot->description = "Change the display order of the selected strokes";
1688
1691
1692 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1693
1694 ot->prop = RNA_def_enum(
1695 ot->srna, "direction", prop_reorder_direction, int(ReorderDirection::TOP), "Direction", "");
1696}
1697
1699
1700/* -------------------------------------------------------------------- */
1703
1705{
1706 using namespace bke::greasepencil;
1707 const Scene *scene = CTX_data_scene(C);
1708 bool changed = false;
1709
1710 Object *object = CTX_data_active_object(C);
1711 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1712
1713 int target_layer_name_length;
1714 char *target_layer_name = RNA_string_get_alloc(
1715 op->ptr, "target_layer_name", nullptr, 0, &target_layer_name_length);
1716 BLI_SCOPED_DEFER([&] { MEM_SAFE_FREE(target_layer_name); });
1717 const bool add_new_layer = RNA_boolean_get(op->ptr, "add_new_layer");
1718 if (add_new_layer) {
1719 grease_pencil.add_layer(target_layer_name);
1720 }
1721
1722 TreeNode *target_node = grease_pencil.find_node_by_name(target_layer_name);
1723 if (target_node == nullptr || !target_node->is_layer()) {
1724 BKE_reportf(op->reports, RPT_ERROR, "There is no layer '%s'", target_layer_name);
1725 return OPERATOR_CANCELLED;
1726 }
1727
1728 Layer &layer_dst = target_node->as_layer();
1729 if (layer_dst.is_locked()) {
1730 BKE_reportf(op->reports, RPT_ERROR, "'%s' Layer is locked", target_layer_name);
1731 return OPERATOR_CANCELLED;
1732 }
1733
1734 /* Iterate through all the drawings at current scene frame. */
1735 const Vector<MutableDrawingInfo> drawings_src = retrieve_editable_drawings(*scene,
1736 grease_pencil);
1737 for (const MutableDrawingInfo &info : drawings_src) {
1738 bke::CurvesGeometry &curves_src = info.drawing.strokes_for_write();
1739 IndexMaskMemory memory;
1740 const IndexMask selected_strokes = ed::curves::retrieve_selected_curves(curves_src, memory);
1741 if (selected_strokes.is_empty()) {
1742 continue;
1743 }
1744
1745 if (!layer_dst.has_drawing_at(info.frame_number)) {
1746 /* Move geometry to a new drawing in target layer. */
1747 Drawing &drawing_dst = *grease_pencil.insert_frame(layer_dst, info.frame_number);
1749 curves_src, selected_strokes, {});
1750
1751 curves_src.remove_curves(selected_strokes, {});
1752 drawing_dst.tag_topology_changed();
1753 }
1754 else if (Drawing *drawing_dst = grease_pencil.get_editable_drawing_at(layer_dst,
1755 info.frame_number))
1756 {
1757 /* Append geometry to drawing in target layer. */
1758 bke::CurvesGeometry selected_elems = curves_copy_curve_selection(
1759 curves_src, selected_strokes, {});
1760 Curves *selected_curves = bke::curves_new_nomain(std::move(selected_elems));
1761 Curves *layer_curves = bke::curves_new_nomain(std::move(drawing_dst->strokes_for_write()));
1762 std::array<bke::GeometrySet, 2> geometry_sets{bke::GeometrySet::from_curves(selected_curves),
1763 bke::GeometrySet::from_curves(layer_curves)};
1764 bke::GeometrySet joined = geometry::join_geometries(geometry_sets, {});
1765 drawing_dst->strokes_for_write() = std::move(joined.get_curves_for_write()->geometry.wrap());
1766
1767 curves_src.remove_curves(selected_strokes, {});
1768
1769 drawing_dst->tag_topology_changed();
1770 }
1771
1772 info.drawing.tag_topology_changed();
1773 changed = true;
1774 }
1775
1776 if (changed) {
1777 /* updates */
1780 }
1781
1782 return OPERATOR_FINISHED;
1783}
1784
1786{
1787 const bool add_new_layer = RNA_boolean_get(op->ptr, "add_new_layer");
1788 if (add_new_layer) {
1789 Object *object = CTX_data_active_object(C);
1790 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1791
1792 const std::string unique_name = grease_pencil.unique_layer_name("Layer");
1793 RNA_string_set(op->ptr, "target_layer_name", unique_name.c_str());
1794
1796 C, op, event, IFACE_("Move to New Layer"), IFACE_("Create"));
1797 }
1799}
1800
1802{
1803 PropertyRNA *prop;
1804
1805 ot->name = "Move to Layer";
1806 ot->idname = "GREASE_PENCIL_OT_move_to_layer";
1807 ot->description = "Move selected strokes to another layer";
1808
1812
1813 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1814
1815 prop = RNA_def_string(
1816 ot->srna, "target_layer_name", nullptr, INT16_MAX, "Name", "Target Grease Pencil Layer");
1818 prop = RNA_def_boolean(
1819 ot->srna, "add_new_layer", false, "New Layer", "Move selection to a new layer");
1821}
1822
1824
1825/* -------------------------------------------------------------------- */
1828
1829enum class SeparateMode : int8_t {
1830 /* Selected Points/Strokes. */
1832 /* By Material. */
1834 /* By Active Layer. */
1836};
1837
1839 {int(SeparateMode::SELECTED), "SELECTED", 0, "Selection", "Separate selected geometry"},
1840 {int(SeparateMode::MATERIAL), "MATERIAL", 0, "By Material", "Separate by material"},
1841 {int(SeparateMode::LAYER), "LAYER", 0, "By Layer", "Separate by layer"},
1842 {0, nullptr, 0, nullptr, nullptr},
1843};
1844
1845static void remove_unused_materials(Main *bmain, Object *object)
1846{
1847 int actcol = object->actcol;
1848 for (int slot = 1; slot <= object->totcol; slot++) {
1849 while (slot <= object->totcol && !BKE_object_material_slot_used(object, slot)) {
1850 object->actcol = slot;
1851 if (!BKE_object_material_slot_remove(bmain, object)) {
1852 break;
1853 }
1854
1855 if (actcol >= slot) {
1856 actcol--;
1857 }
1858 }
1859 }
1860 object->actcol = actcol;
1861}
1862
1864 Scene *scene,
1865 ViewLayer *view_layer,
1866 Base *base_prev,
1867 const GreasePencil &grease_pencil_src)
1868{
1869 const eDupli_ID_Flags dupflag = eDupli_ID_Flags(U.dupflag & USER_DUP_ACT);
1870 Base *base_new = object::add_duplicate(bmain, scene, view_layer, base_prev, dupflag);
1871 Object *object_dst = base_new->object;
1872 object_dst->mode = OB_MODE_OBJECT;
1873 object_dst->data = BKE_grease_pencil_add(bmain, grease_pencil_src.id.name + 2);
1874
1875 return object_dst;
1876}
1877
1879 const int layer_index, const GreasePencil &grease_pencil_src, GreasePencil &grease_pencil_dst)
1880{
1881 using namespace bke::greasepencil;
1882
1883 /* This assumes that the index is valid. Will cause an assert if it is not. */
1884 const Layer &layer_src = grease_pencil_src.layer(layer_index);
1885 if (TreeNode *node = grease_pencil_dst.find_node_by_name(layer_src.name())) {
1886 return node->as_layer();
1887 }
1888
1889 /* If the layer can't be found in `grease_pencil_dst` by name add a new layer. */
1890 Layer &new_layer = grease_pencil_dst.add_layer(layer_src.name());
1891
1892 /* Transfer Layer attributes. */
1893 bke::gather_attributes(grease_pencil_src.attributes(),
1896 {},
1897 Span({layer_index}),
1898 grease_pencil_dst.attributes_for_write());
1899
1900 return new_layer;
1901}
1902
1904 Main &bmain,
1905 Scene &scene,
1906 ViewLayer &view_layer,
1907 Base &base_prev,
1908 Object &object_src)
1909{
1910 using namespace bke::greasepencil;
1911 bool changed = false;
1912
1913 GreasePencil &grease_pencil_src = *static_cast<GreasePencil *>(object_src.data);
1915 &bmain, &scene, &view_layer, &base_prev, grease_pencil_src);
1916 GreasePencil &grease_pencil_dst = *static_cast<GreasePencil *>(object_dst->data);
1917
1918 /* Iterate through all the drawings at current scene frame. */
1919 const Vector<MutableDrawingInfo> drawings_src = retrieve_editable_drawings(scene,
1920 grease_pencil_src);
1921 for (const MutableDrawingInfo &info : drawings_src) {
1922 bke::CurvesGeometry &curves_src = info.drawing.strokes_for_write();
1923 IndexMaskMemory memory;
1924 const IndexMask selected_points = ed::curves::retrieve_selected_points(curves_src, memory);
1925 if (selected_points.is_empty()) {
1926 continue;
1927 }
1928
1929 /* Insert Keyframe at current frame/layer. */
1931 info.layer_index, grease_pencil_src, grease_pencil_dst);
1932
1933 Drawing *drawing_dst = grease_pencil_dst.insert_frame(layer_dst, info.frame_number);
1934 /* TODO: Can we assume the insert never fails? */
1935 BLI_assert(drawing_dst != nullptr);
1936
1937 /* Copy strokes to new CurvesGeometry. */
1939 curves_src, selected_points, {});
1940 curves_src = remove_points_and_split(curves_src, selected_points);
1941
1942 info.drawing.tag_topology_changed();
1943 drawing_dst->tag_topology_changed();
1944
1945 changed = true;
1946 }
1947
1948 if (changed) {
1949 grease_pencil_dst.set_active_layer(nullptr);
1950
1951 /* Add object materials to target object. */
1953 object_dst,
1954 BKE_object_material_array_p(&object_src),
1955 *BKE_object_material_len_p(&object_src),
1956 false);
1957
1958 remove_unused_materials(&bmain, object_dst);
1959 DEG_id_tag_update(&grease_pencil_dst.id, ID_RECALC_GEOMETRY);
1960 WM_event_add_notifier(&C, NC_OBJECT | ND_DRAW, &grease_pencil_dst);
1961 }
1962 return changed;
1963}
1964
1966 Main &bmain,
1967 Scene &scene,
1968 ViewLayer &view_layer,
1969 Base &base_prev,
1970 Object &object_src)
1971{
1972 using namespace bke::greasepencil;
1973 bool changed = false;
1974
1975 GreasePencil &grease_pencil_src = *static_cast<GreasePencil *>(object_src.data);
1976
1977 /* Create a new object for each layer. */
1978 for (const int layer_i : grease_pencil_src.layers().index_range()) {
1979 Layer &layer_src = grease_pencil_src.layer(layer_i);
1980 if (layer_src.is_selected() || layer_src.is_locked()) {
1981 continue;
1982 }
1983
1985 &bmain, &scene, &view_layer, &base_prev, grease_pencil_src);
1986 GreasePencil &grease_pencil_dst = *static_cast<GreasePencil *>(object_dst->data);
1988 layer_i, grease_pencil_src, grease_pencil_dst);
1989
1990 /* Iterate through all the drawings at current frame. */
1992 scene, grease_pencil_src, layer_src);
1993 for (const MutableDrawingInfo &info : drawings_src) {
1994 bke::CurvesGeometry &curves_src = info.drawing.strokes_for_write();
1995 IndexMaskMemory memory;
1996 const IndexMask strokes = retrieve_editable_strokes(
1997 object_src, info.drawing, info.layer_index, memory);
1998 if (strokes.is_empty()) {
1999 continue;
2000 }
2001
2002 /* Add object materials. */
2004 object_dst,
2005 BKE_object_material_array_p(&object_src),
2006 *BKE_object_material_len_p(&object_src),
2007 false);
2008
2009 /* Insert Keyframe at current frame/layer. */
2010 Drawing *drawing_dst = grease_pencil_dst.insert_frame(layer_dst, info.frame_number);
2011 /* TODO: Can we assume the insert never fails? */
2012 BLI_assert(drawing_dst != nullptr);
2013
2014 /* Copy strokes to new CurvesGeometry. */
2016 info.drawing.strokes(), strokes, {});
2017 curves_src.remove_curves(strokes, {});
2018
2019 info.drawing.tag_topology_changed();
2020 drawing_dst->tag_topology_changed();
2021
2022 changed = true;
2023 }
2024
2025 remove_unused_materials(&bmain, object_dst);
2026
2027 DEG_id_tag_update(&grease_pencil_dst.id, ID_RECALC_GEOMETRY);
2028 WM_event_add_notifier(&C, NC_OBJECT | ND_DRAW, &grease_pencil_dst);
2029 }
2030
2031 return changed;
2032}
2033
2035 Main &bmain,
2036 Scene &scene,
2037 ViewLayer &view_layer,
2038 Base &base_prev,
2039 Object &object_src)
2040{
2041 using namespace blender::bke;
2042 using namespace bke::greasepencil;
2043 bool changed = false;
2044
2045 GreasePencil &grease_pencil_src = *static_cast<GreasePencil *>(object_src.data);
2046
2047 /* Create a new object for each material. */
2048 for (const int mat_i : IndexRange(object_src.totcol).drop_front(1)) {
2049 if (!BKE_object_material_slot_used(&object_src, mat_i + 1)) {
2050 continue;
2051 }
2052
2054 &bmain, &scene, &view_layer, &base_prev, grease_pencil_src);
2055
2056 /* Add object materials. */
2058 object_dst,
2059 BKE_object_material_array_p(&object_src),
2060 *BKE_object_material_len_p(&object_src),
2061 false);
2062
2063 /* Iterate through all the drawings at current scene frame. */
2064 const Vector<MutableDrawingInfo> drawings_src = retrieve_editable_drawings(scene,
2065 grease_pencil_src);
2066 for (const MutableDrawingInfo &info : drawings_src) {
2067 bke::CurvesGeometry &curves_src = info.drawing.strokes_for_write();
2068 IndexMaskMemory memory;
2070 object_src, info.drawing, mat_i, memory);
2071 if (strokes.is_empty()) {
2072 continue;
2073 }
2074
2075 GreasePencil &grease_pencil_dst = *static_cast<GreasePencil *>(object_dst->data);
2076
2077 /* Insert Keyframe at current frame/layer. */
2079 info.layer_index, grease_pencil_src, grease_pencil_dst);
2080
2081 Drawing *drawing_dst = grease_pencil_dst.insert_frame(layer_dst, info.frame_number);
2082 /* TODO: Can we assume the insert never fails? */
2083 BLI_assert(drawing_dst != nullptr);
2084
2085 /* Copy strokes to new CurvesGeometry. */
2086 drawing_dst->strokes_for_write() = bke::curves_copy_curve_selection(curves_src, strokes, {});
2087 curves_src.remove_curves(strokes, {});
2088
2089 info.drawing.tag_topology_changed();
2090 drawing_dst->tag_topology_changed();
2091 DEG_id_tag_update(&grease_pencil_dst.id, ID_RECALC_GEOMETRY);
2092 WM_event_add_notifier(&C, NC_OBJECT | ND_DRAW, &grease_pencil_dst);
2093
2094 changed = true;
2095 }
2096
2097 remove_unused_materials(&bmain, object_dst);
2098 }
2099
2100 if (changed) {
2101 remove_unused_materials(&bmain, &object_src);
2102 }
2103
2104 return changed;
2105}
2106
2108{
2109 using namespace bke::greasepencil;
2110 Main *bmain = CTX_data_main(C);
2111 Scene *scene = CTX_data_scene(C);
2112 ViewLayer *view_layer = CTX_data_view_layer(C);
2113 Base *base_prev = CTX_data_active_base(C);
2114 Object *object_src = CTX_data_active_object(C);
2115 GreasePencil &grease_pencil_src = *static_cast<GreasePencil *>(object_src->data);
2116
2117 const SeparateMode mode = SeparateMode(RNA_enum_get(op->ptr, "mode"));
2118 bool changed = false;
2119
2120 WM_cursor_wait(true);
2121
2122 switch (mode) {
2124 /* Cancel if nothing selected. */
2126 grease_pencil_src);
2127 const bool has_selection = std::any_of(
2128 drawings.begin(), drawings.end(), [&](const MutableDrawingInfo &info) {
2129 return ed::curves::has_anything_selected(info.drawing.strokes());
2130 });
2131 if (!has_selection) {
2132 BKE_report(op->reports, RPT_ERROR, "Nothing selected");
2133 WM_cursor_wait(false);
2134 return OPERATOR_CANCELLED;
2135 }
2136
2138 *C, *bmain, *scene, *view_layer, *base_prev, *object_src);
2139 break;
2140 }
2142 /* Cancel if the object only has one material. */
2143 if (object_src->totcol == 1) {
2144 BKE_report(op->reports, RPT_ERROR, "The object has only one material");
2145 WM_cursor_wait(false);
2146 return OPERATOR_CANCELLED;
2147 }
2148
2150 *C, *bmain, *scene, *view_layer, *base_prev, *object_src);
2151 break;
2152 }
2153 case SeparateMode::LAYER: {
2154 /* Cancel if the object only has one layer. */
2155 if (grease_pencil_src.layers().size() == 1) {
2156 BKE_report(op->reports, RPT_ERROR, "The object has only one layer");
2157 WM_cursor_wait(false);
2158 return OPERATOR_CANCELLED;
2159 }
2161 *C, *bmain, *scene, *view_layer, *base_prev, *object_src);
2162 break;
2163 }
2164 }
2165
2166 WM_cursor_wait(false);
2167
2168 if (changed) {
2169 DEG_id_tag_update(&grease_pencil_src.id, ID_RECALC_GEOMETRY);
2170 WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_EDITED, &grease_pencil_src);
2171 }
2172
2173 return OPERATOR_FINISHED;
2174}
2175
2177{
2178 ot->name = "Separate";
2179 ot->idname = "GREASE_PENCIL_OT_separate";
2180 ot->description = "Separate the selected geometry into a new grease pencil object";
2181
2182 ot->invoke = WM_menu_invoke;
2185
2186 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2187
2188 ot->prop = RNA_def_enum(
2189 ot->srna, "mode", prop_separate_modes, int(SeparateMode::SELECTED), "Mode", "");
2190}
2191
2193
2194/* -------------------------------------------------------------------- */
2197
2198/* Global clipboard for Grease Pencil curves. */
2199static struct Clipboard {
2201 /* Object transform of stored curves. */
2203 /* We store the material uid's of the copied curves, so we can match those when pasting the
2204 * clipboard into another object. */
2207} *grease_pencil_clipboard = nullptr;
2208
2211
2213{
2214 std::scoped_lock lock(grease_pencil_clipboard_lock);
2215
2216 if (grease_pencil_clipboard == nullptr) {
2217 grease_pencil_clipboard = MEM_new<Clipboard>(__func__);
2218 }
2220}
2221
2223{
2224 Main *bmain = CTX_data_main(C);
2225 const Scene &scene = *CTX_data_scene(C);
2226 Object *object = CTX_data_active_object(C);
2228 scene.toolsettings);
2229 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
2230 const bool keep_world_transform = RNA_boolean_get(op->ptr, "keep_world_transform");
2231 const bool paste_on_back = RNA_boolean_get(op->ptr, "paste_back");
2232
2233 /* Get active layer in the target object. */
2234 if (!grease_pencil.has_active_layer()) {
2235 BKE_report(op->reports, RPT_ERROR, "No active Grease Pencil layer");
2236 return OPERATOR_CANCELLED;
2237 }
2238 bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
2239 if (!active_layer.is_editable()) {
2240 BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hidden");
2241 return OPERATOR_CANCELLED;
2242 }
2243
2244 /* Ensure active keyframe. */
2245 bool inserted_keyframe = false;
2246 if (!ensure_active_keyframe(scene, grease_pencil, active_layer, false, inserted_keyframe)) {
2247 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
2248 return OPERATOR_CANCELLED;
2249 }
2250 bke::greasepencil::Drawing *target_drawing = grease_pencil.get_editable_drawing_at(active_layer,
2251 scene.r.cfra);
2252 if (target_drawing == nullptr) {
2253 return OPERATOR_CANCELLED;
2254 }
2255
2256 /* Deselect everything from editable drawings. The pasted strokes are the only ones then after
2257 * the paste. That's convenient for the user. */
2258 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
2259 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
2261 info.drawing.strokes_for_write(), selection_domain, CD_PROP_BOOL);
2262 ed::curves::fill_selection_false(selection_in_target.span);
2263 selection_in_target.finish();
2264 });
2265
2266 const float4x4 object_to_layer = math::invert(active_layer.to_object_space(*object));
2268 *bmain, *object, *target_drawing, object_to_layer, keep_world_transform, paste_on_back);
2269
2270 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
2271 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
2272
2273 if (inserted_keyframe) {
2275 }
2276
2277 return OPERATOR_FINISHED;
2278}
2279
2281 Span<float4x4> transforms)
2282{
2283 BLI_assert(geometries.size() == transforms.size());
2284
2285 std::unique_ptr<bke::Instances> instances = std::make_unique<bke::Instances>();
2286 instances->resize(geometries.size());
2287 instances->transforms_for_write().copy_from(transforms);
2288 MutableSpan<int> handles = instances->reference_handles_for_write();
2289 for (const int i : geometries.index_range()) {
2290 handles[i] = instances->add_new_reference(bke::InstanceReference{geometries[i]});
2291 }
2292
2294 options.keep_original_ids = true;
2295 options.realize_instance_attributes = false;
2296 return realize_instances(bke::GeometrySet::from_instances(instances.release()), options);
2297}
2298
2300{
2302
2303 const Scene *scene = CTX_data_scene(C);
2304 const Object *object = CTX_data_active_object(C);
2305 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
2307 scene->toolsettings);
2308
2310
2311 bool anything_copied = false;
2312 int num_copied = 0;
2313 Vector<bke::GeometrySet> set_of_copied_curves;
2314 Vector<float4x4> set_of_transforms;
2315
2316 /* Collect all selected strokes/points on all editable layers. */
2317 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
2318 for (const MutableDrawingInfo &drawing_info : drawings) {
2319 const bke::CurvesGeometry &curves = drawing_info.drawing.strokes();
2320 const Layer &layer = grease_pencil.layer(drawing_info.layer_index);
2321 const float4x4 layer_to_object = layer.to_object_space(*object);
2322
2323 if (curves.curves_num() == 0) {
2324 continue;
2325 }
2327 continue;
2328 }
2329
2330 /* Get a copy of the selected geometry on this layer. */
2331 IndexMaskMemory memory;
2332 bke::CurvesGeometry copied_curves;
2333
2334 if (selection_domain == bke::AttrDomain::Curve) {
2335 const IndexMask selected_curves = ed::curves::retrieve_selected_curves(curves, memory);
2336 copied_curves = curves_copy_curve_selection(curves, selected_curves, {});
2337 num_copied += copied_curves.curves_num();
2338 }
2339 else if (selection_domain == bke::AttrDomain::Point) {
2340 const IndexMask selected_points = ed::curves::retrieve_selected_points(curves, memory);
2341 copied_curves = curves_copy_point_selection(curves, selected_points, {});
2342 num_copied += copied_curves.points_num();
2343 }
2344
2345 /* Add the layer selection to the set of copied curves. */
2346 Curves *layer_curves = curves_new_nomain(std::move(copied_curves));
2347 set_of_copied_curves.append(bke::GeometrySet::from_curves(layer_curves));
2348 set_of_transforms.append(layer_to_object);
2349 anything_copied = true;
2350 }
2351
2352 if (!anything_copied) {
2353 clipboard.curves.resize(0, 0);
2354 return OPERATOR_CANCELLED;
2355 }
2356
2357 /* Merge all copied curves into one CurvesGeometry object and assign it to the clipboard. */
2358 bke::GeometrySet joined_copied_curves = join_geometries_with_transform(set_of_copied_curves,
2359 set_of_transforms);
2360 clipboard.curves = std::move(joined_copied_curves.get_curves_for_write()->geometry.wrap());
2361 clipboard.transform = object->object_to_world();
2362
2363 /* Store the session uid of the materials used by the curves in the clipboard. We use the uid to
2364 * remap the material indices when pasting. */
2365 clipboard.materials.clear();
2366 clipboard.materials_in_source_num = grease_pencil.material_array_num;
2367 const bke::AttributeAccessor attributes = clipboard.curves.attributes();
2368 const VArraySpan<int> material_indices = *attributes.lookup_or_default<int>(
2369 "material_index", bke::AttrDomain::Curve, 0);
2370 for (const int material_index : IndexRange(grease_pencil.material_array_num)) {
2371 if (!material_indices.contains(material_index)) {
2372 continue;
2373 }
2374 const Material *material = grease_pencil.material_array[material_index];
2375 clipboard.materials.append({material->id.session_uid, material_index});
2376 }
2377
2378 /* Report the numbers. */
2379 if (selection_domain == bke::AttrDomain::Curve) {
2380 BKE_reportf(op->reports, RPT_INFO, "Copied %d selected curve(s)", num_copied);
2381 }
2382 else if (selection_domain == bke::AttrDomain::Point) {
2383 BKE_reportf(op->reports, RPT_INFO, "Copied %d selected point(s)", num_copied);
2384 }
2385
2386 return OPERATOR_FINISHED;
2387}
2388
2390{
2392 return false;
2393 }
2394
2395 std::scoped_lock lock(grease_pencil_clipboard_lock);
2396 /* Check for curves in the Grease Pencil clipboard. */
2397 return (grease_pencil_clipboard && grease_pencil_clipboard->curves.curves_num() > 0);
2398}
2399
2401{
2402 ot->name = "Paste Strokes";
2403 ot->idname = "GREASE_PENCIL_OT_paste";
2404 ot->description =
2405 "Paste Grease Pencil points or strokes from the internal clipboard to the active layer";
2406
2409
2410 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2411
2412 ot->prop = RNA_def_boolean(
2413 ot->srna, "paste_back", false, "Paste on Back", "Add pasted strokes behind all strokes");
2415 ot->prop = RNA_def_boolean(ot->srna,
2416 "keep_world_transform",
2417 false,
2418 "Keep World Transform",
2419 "Keep the world transform of strokes from the clipboard unchanged");
2420}
2421
2423{
2424 ot->name = "Copy Strokes";
2425 ot->idname = "GREASE_PENCIL_OT_copy";
2426 ot->description = "Copy the selected Grease Pencil points or strokes to the internal clipboard";
2427
2430
2431 ot->flag = OPTYPE_REGISTER;
2432}
2433
2435
2437{
2438 std::scoped_lock lock(grease_pencil_clipboard_lock);
2439
2441 MEM_delete(grease_pencil_clipboard);
2442 grease_pencil_clipboard = nullptr;
2443 }
2444}
2445
2447{
2448 using namespace blender::ed::greasepencil;
2449 return ensure_grease_pencil_clipboard().curves;
2450}
2451
2453{
2454 using namespace blender::ed::greasepencil;
2455
2456 /* Get a list of all materials in the scene. */
2457 Map<uint, Material *> scene_materials;
2458 LISTBASE_FOREACH (Material *, material, &bmain.materials) {
2459 scene_materials.add(material->id.session_uid, material);
2460 }
2461
2463 Array<int> clipboard_material_remap(clipboard.materials_in_source_num, 0);
2464 for (const int i : clipboard.materials.index_range()) {
2465 /* Check if the material name exists in the scene. */
2466 int target_index;
2467 uint material_id = clipboard.materials[i].first;
2468 Material *material = scene_materials.lookup_default(material_id, nullptr);
2469 if (!material) {
2470 /* Material is removed, so create a new material. */
2471 BKE_grease_pencil_object_material_new(&bmain, &object, nullptr, &target_index);
2472 clipboard_material_remap[clipboard.materials[i].second] = target_index;
2473 continue;
2474 }
2475
2476 /* Find or add the material to the target object. */
2477 target_index = BKE_object_material_ensure(&bmain, &object, material);
2478 clipboard_material_remap[clipboard.materials[i].second] = target_index;
2479 }
2480
2481 return clipboard_material_remap;
2482}
2483
2485 Object &object,
2487 const float4x4 &transform,
2488 const bool keep_world_transform,
2489 const bool paste_back)
2490{
2491 const Clipboard &clipboard = ensure_grease_pencil_clipboard();
2492 const bke::CurvesGeometry &clipboard_curves = clipboard.curves;
2493 const float4x4 clipboard_to_world = clipboard.transform;
2494 if (clipboard_curves.curves_num() <= 0) {
2495 return {};
2496 }
2497
2498 /* Get a list of all materials in the scene. */
2499 const Array<int> clipboard_material_remap = ed::greasepencil::clipboard_materials_remap(bmain,
2500 object);
2501
2502 /* Get the index range of the pasted curves in the target layer. */
2503 const IndexRange pasted_curves_range = paste_back ?
2504 IndexRange(0, clipboard_curves.curves_num()) :
2505 IndexRange(drawing.strokes().curves_num(),
2506 clipboard_curves.curves_num());
2507
2508 /* Append the geometry from the clipboard to the target layer. */
2510 Curves *target_id = curves_new_nomain(std::move(drawing.strokes_for_write()));
2511
2512 const Array<bke::GeometrySet> geometry_sets = {
2513 bke::GeometrySet::from_curves(paste_back ? clipboard_id : target_id),
2514 bke::GeometrySet::from_curves(paste_back ? target_id : clipboard_id)};
2515
2516 const float4x4 clipboard_transform = transform *
2517 (keep_world_transform ?
2518 object.world_to_object() * clipboard_to_world :
2520 const Array<float4x4> transforms = paste_back ?
2521 Span<float4x4>{clipboard_transform,
2523 Span<float4x4>{float4x4::identity(), clipboard_transform};
2524 bke::GeometrySet joined_curves = join_geometries_with_transform(geometry_sets, transforms);
2525
2526 drawing.strokes_for_write() = std::move(joined_curves.get_curves_for_write()->geometry.wrap());
2527
2528 /* Remap the material indices of the pasted curves to the target object material indices. */
2530 bke::SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
2531 "material_index", bke::AttrDomain::Curve);
2532 if (material_indices) {
2533 for (const int i : pasted_curves_range) {
2534 material_indices.span[i] = clipboard_material_remap[material_indices.span[i]];
2535 }
2536 material_indices.finish();
2537 }
2538
2539 drawing.tag_topology_changed();
2540
2541 return pasted_curves_range;
2542}
2543
2544/* -------------------------------------------------------------------- */
2548{
2549 const Scene *scene = CTX_data_scene(C);
2550 Object *object = CTX_data_active_object(C);
2551 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
2552
2553 const float threshold = RNA_float_get(op->ptr, "threshold");
2554 const bool use_unselected = RNA_boolean_get(op->ptr, "use_unselected");
2555
2556 std::atomic<bool> changed = false;
2557
2558 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
2559 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
2560 bke::greasepencil::Drawing &drawing = info.drawing;
2561 IndexMaskMemory memory;
2562 const IndexMask points = use_unselected ?
2564 *object, drawing, info.layer_index, memory) :
2566 *object, info.drawing, info.layer_index, memory);
2567 if (points.is_empty()) {
2568 return;
2569 }
2571 drawing.strokes(), threshold, points, {});
2572 drawing.tag_topology_changed();
2573 changed.store(true, std::memory_order_relaxed);
2574 });
2575 if (changed) {
2576 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
2577 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
2578 }
2579 return OPERATOR_FINISHED;
2580}
2581
2583{
2584 PropertyRNA *prop;
2585
2586 ot->name = "Merge by Distance";
2587 ot->idname = "GREASE_PENCIL_OT_stroke_merge_by_distance";
2588 ot->description = "Merge points by distance";
2589
2592
2593 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2594
2595 prop = RNA_def_float(ot->srna, "threshold", 0.001f, 0.0f, 100.0f, "Threshold", "", 0.0f, 100.0f);
2596 /* Avoid re-using last var. */
2598
2599 prop = RNA_def_boolean(ot->srna,
2600 "use_unselected",
2601 false,
2602 "Unselected",
2603 "Use whole stroke, not only selected points");
2605}
2606
2608
2609/* -------------------------------------------------------------------- */
2612
2614 const IndexMask &points_to_extrude)
2615{
2616 const OffsetIndices<int> points_by_curve = src.points_by_curve();
2617
2618 const int old_curves_num = src.curves_num();
2619 const int old_points_num = src.points_num();
2620
2621 Vector<int> dst_to_src_points(old_points_num);
2623
2624 Vector<int> dst_to_src_curves(old_curves_num);
2626
2627 Vector<bool> dst_selected(old_points_num, false);
2628
2629 Vector<int> dst_curve_counts(old_curves_num);
2631 points_by_curve, src.curves_range(), dst_curve_counts.as_mutable_span());
2632
2633 const VArray<bool> &src_cyclic = src.cyclic();
2634
2635 /* Point offset keeps track of the points inserted. */
2636 int point_offset = 0;
2637 for (const int curve_index : src.curves_range()) {
2638 const IndexRange curve_points = points_by_curve[curve_index];
2639 const IndexMask curve_points_to_extrude = points_to_extrude.slice_content(curve_points);
2640 const bool curve_cyclic = src_cyclic[curve_index];
2641
2642 curve_points_to_extrude.foreach_index([&](const int src_point_index) {
2643 if (!curve_cyclic && (src_point_index == curve_points.first())) {
2644 /* Start-point extruded, we insert a new point at the beginning of the curve.
2645 * NOTE: all points of a cyclic curve behave like an inner-point. */
2646 dst_to_src_points.insert(src_point_index + point_offset, src_point_index);
2647 dst_selected.insert(src_point_index + point_offset, true);
2648 ++dst_curve_counts[curve_index];
2649 ++point_offset;
2650 return;
2651 }
2652 if (!curve_cyclic && (src_point_index == curve_points.last())) {
2653 /* End-point extruded, we insert a new point at the end of the curve.
2654 * NOTE: all points of a cyclic curve behave like an inner-point. */
2655 dst_to_src_points.insert(src_point_index + point_offset + 1, src_point_index);
2656 dst_selected.insert(src_point_index + point_offset + 1, true);
2657 ++dst_curve_counts[curve_index];
2658 ++point_offset;
2659 return;
2660 }
2661
2662 /* Inner-point extruded: we create a new curve made of two points located at the same
2663 * position. Only one of them is selected so that the other one remains stuck to the curve.
2664 */
2665 dst_to_src_points.append(src_point_index);
2666 dst_selected.append(false);
2667 dst_to_src_points.append(src_point_index);
2668 dst_selected.append(true);
2669 dst_to_src_curves.append(curve_index);
2670 dst_curve_counts.append(2);
2671 });
2672 }
2673
2674 const int new_points_num = dst_to_src_points.size();
2675 const int new_curves_num = dst_to_src_curves.size();
2676
2677 bke::CurvesGeometry dst(new_points_num, new_curves_num);
2678
2679 /* Setup curve offsets, based on the number of points in each curve. */
2680 MutableSpan<int> new_curve_offsets = dst.offsets_for_write();
2681 array_utils::copy(dst_curve_counts.as_span(), new_curve_offsets.drop_back(1));
2683
2684 /* Attributes. */
2685 const bke::AttributeAccessor src_attributes = src.attributes();
2687
2688 /* Selection attribute. */
2691 selection.span.copy_from(dst_selected.as_span());
2692 selection.finish();
2693
2694 /* Cyclic attribute : newly created curves cannot be cyclic.
2695 * NOTE: if the cyclic attribute is single and false, it can be kept this way.
2696 */
2697 if (src_cyclic.get_if_single().value_or(true)) {
2698 dst.cyclic_for_write().drop_front(old_curves_num).fill(false);
2699 }
2700
2701 bke::gather_attributes(src_attributes,
2705 dst_to_src_curves,
2706 dst_attributes);
2707
2708 bke::gather_attributes(src_attributes,
2712 dst_to_src_points,
2713 dst_attributes);
2714
2715 dst.update_curve_types();
2716 return dst;
2717}
2718
2720{
2721 const Scene *scene = CTX_data_scene(C);
2722 Object *object = CTX_data_active_object(C);
2723 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
2724
2725 std::atomic<bool> changed = false;
2726 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
2727 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
2728 IndexMaskMemory memory;
2729 const IndexMask points_to_extrude = retrieve_editable_and_selected_points(
2730 *object, info.drawing, info.layer_index, memory);
2731 if (points_to_extrude.is_empty()) {
2732 return;
2733 }
2734
2735 const bke::CurvesGeometry &curves = info.drawing.strokes();
2736 info.drawing.strokes_for_write() = extrude_grease_pencil_curves(curves, points_to_extrude);
2737
2739 changed.store(true, std::memory_order_relaxed);
2740 });
2741
2742 if (changed) {
2743 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
2744 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
2745 }
2746
2747 return OPERATOR_FINISHED;
2748}
2749
2751{
2752 ot->name = "Extrude Stroke Points";
2753 ot->idname = "GREASE_PENCIL_OT_extrude";
2754 ot->description = "Extrude the selected points";
2755
2758
2759 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2760}
2761
2763
2764/* -------------------------------------------------------------------- */
2767
2769{
2770 Scene &scene = *CTX_data_scene(C);
2772
2773 View3D *v3d = CTX_wm_view3d(C);
2774 ARegion *region = CTX_wm_region(C);
2775
2776 const ReprojectMode mode = ReprojectMode(RNA_enum_get(op->ptr, "type"));
2777 const bool keep_original = RNA_boolean_get(op->ptr, "keep_original");
2778
2779 Object *object = CTX_data_active_object(C);
2780 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
2781 const float offset = RNA_float_get(op->ptr, "offset");
2782
2783 ViewDepths *view_depths = nullptr;
2784 if (mode == ReprojectMode::Surface) {
2786 depsgraph, region, v3d, nullptr, V3D_DEPTH_NO_GPENCIL, false, &view_depths);
2787 }
2788
2790 scene.toolsettings);
2791
2792 const int oldframe = int(DEG_get_ctime(depsgraph));
2793 if (keep_original) {
2794 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
2795 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
2796 IndexMaskMemory memory;
2798 *object, info.drawing, info.layer_index, selection_domain, memory);
2799 if (elements.is_empty()) {
2800 return;
2801 }
2802
2804 if (selection_domain == bke::AttrDomain::Curve) {
2806 }
2807 else if (selection_domain == bke::AttrDomain::Point) {
2809 }
2811 });
2812 }
2813
2814 /* TODO: This can probably be optimized further for the non-Surface projection use case by
2815 * considering all drawings for the parallel loop instead of having to partition by frame number.
2816 */
2817 std::atomic<bool> changed = false;
2818 Array<Vector<MutableDrawingInfo>> drawings_per_frame =
2820 for (const Span<MutableDrawingInfo> drawings : drawings_per_frame) {
2821 if (drawings.is_empty()) {
2822 continue;
2823 }
2824 const int current_frame_number = drawings.first().frame_number;
2825
2826 if (mode == ReprojectMode::Surface) {
2827 scene.r.cfra = current_frame_number;
2829 }
2830
2831 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
2832 IndexMaskMemory memory;
2833 const IndexMask points_to_reproject = retrieve_editable_and_selected_points(
2834 *object, info.drawing, info.layer_index, memory);
2835 if (points_to_reproject.is_empty()) {
2836 return;
2837 }
2838
2839 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
2841 /* Pass in a copy of view_depths, DrawingPlacement fully owns (and frees) them. */
2842 ViewDepths *view_depths_copy = nullptr;
2843 if (view_depths != nullptr) {
2844 view_depths_copy = static_cast<ViewDepths *>(MEM_dupallocN(view_depths));
2845 view_depths_copy->depths = static_cast<float *>(MEM_dupallocN(view_depths->depths));
2846 }
2847 const DrawingPlacement drawing_placement(
2848 scene, *region, *v3d, *object, &layer, mode, offset, view_depths_copy);
2849
2850 MutableSpan<float3> positions = curves.positions_for_write();
2851 points_to_reproject.foreach_index(GrainSize(4096), [&](const int point_i) {
2852 positions[point_i] = drawing_placement.reproject(positions[point_i]);
2853 });
2855
2856 changed.store(true, std::memory_order_relaxed);
2857 });
2858 }
2859
2860 if (view_depths != nullptr) {
2861 ED_view3d_depths_free(view_depths);
2862 }
2863
2864 if (mode == ReprojectMode::Surface) {
2865 scene.r.cfra = oldframe;
2867 }
2868
2869 if (changed) {
2870 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
2871 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
2872 }
2873
2874 return OPERATOR_FINISHED;
2875}
2876
2878{
2879 uiLayout *layout = op->layout;
2880 uiLayout *row;
2881
2882 const ReprojectMode type = ReprojectMode(RNA_enum_get(op->ptr, "type"));
2883
2884 uiLayoutSetPropSep(layout, true);
2885 uiLayoutSetPropDecorate(layout, false);
2886 row = uiLayoutRow(layout, true);
2887 uiItemR(row, op->ptr, "type", UI_ITEM_NONE, nullptr, ICON_NONE);
2888
2889 if (type == ReprojectMode::Surface) {
2890 row = uiLayoutRow(layout, true);
2891 uiItemR(row, op->ptr, "offset", UI_ITEM_NONE, nullptr, ICON_NONE);
2892 }
2893 row = uiLayoutRow(layout, true);
2894 uiItemR(row, op->ptr, "keep_original", UI_ITEM_NONE, nullptr, ICON_NONE);
2895}
2896
2898{
2899 static const EnumPropertyItem reproject_type[] = {
2901 "FRONT",
2902 0,
2903 "Front",
2904 "Reproject the strokes using the X-Z plane"},
2905 {int(ReprojectMode::Side), "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
2906 {int(ReprojectMode::Top), "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
2908 "VIEW",
2909 0,
2910 "View",
2911 "Reproject the strokes to end up on the same plane, as if drawn from the current "
2912 "viewpoint "
2913 "using 'Cursor' Stroke Placement"},
2915 "SURFACE",
2916 0,
2917 "Surface",
2918 "Reproject the strokes on to the scene geometry, as if drawn using 'Surface' placement"},
2920 "CURSOR",
2921 0,
2922 "Cursor",
2923 "Reproject the strokes using the orientation of 3D cursor"},
2924 {0, nullptr, 0, nullptr, nullptr},
2925 };
2926
2927 /* identifiers */
2928 ot->name = "Reproject Strokes";
2929 ot->idname = "GREASE_PENCIL_OT_reproject";
2930 ot->description =
2931 "Reproject the selected strokes from the current viewpoint as if they had been newly "
2932 "drawn "
2933 "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, "
2934 "or for matching deforming geometry)";
2935
2936 /* callbacks */
2937 ot->invoke = WM_menu_invoke;
2941
2942 /* flags */
2943 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2944
2945 /* properties */
2946 ot->prop = RNA_def_enum(
2947 ot->srna, "type", reproject_type, int(ReprojectMode::View), "Projection Type", "");
2948
2950 ot->srna,
2951 "keep_original",
2952 false,
2953 "Keep Original",
2954 "Keep original strokes and create a copy before reprojecting");
2956
2957 RNA_def_float(ot->srna, "offset", 0.0f, 0.0f, 10.0f, "Surface Offset", "", 0.0f, 10.0f);
2958}
2959
2961/* -------------------------------------------------------------------- */
2964
2965/* Poll callback for snap operators */
2966/* NOTE: For now, we only allow these in the 3D view, as other editors do not
2967 * define a cursor or grid-step which can be used.
2968 */
2970{
2972 return false;
2973 }
2974
2975 ScrArea *area = CTX_wm_area(C);
2976 return (area != nullptr) && (area->spacetype == SPACE_VIEW3D);
2977}
2978
2980{
2982
2983 const Scene &scene = *CTX_data_scene(C);
2984 Object &object = *CTX_data_active_object(C);
2985 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
2986 const View3D &v3d = *CTX_wm_view3d(C);
2987 const ARegion &region = *CTX_wm_region(C);
2988 const float grid_size = ED_view3d_grid_view_scale(&scene, &v3d, &region, nullptr);
2989
2990 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
2991 for (const MutableDrawingInfo &drawing_info : drawings) {
2992 bke::CurvesGeometry &curves = drawing_info.drawing.strokes_for_write();
2993 if (curves.curves_num() == 0) {
2994 continue;
2995 }
2997 continue;
2998 }
2999
3000 IndexMaskMemory memory;
3001 const IndexMask selected_points = ed::curves::retrieve_selected_points(curves, memory);
3002
3003 const Layer &layer = grease_pencil.layer(drawing_info.layer_index);
3004 const float4x4 layer_to_world = layer.to_world_space(object);
3005 const float4x4 world_to_layer = math::invert(layer_to_world);
3006
3007 MutableSpan<float3> positions = curves.positions_for_write();
3008 selected_points.foreach_index(GrainSize(4096), [&](const int point_i) {
3009 const float3 pos_world = math::transform_point(layer_to_world, positions[point_i]);
3010 const float3 pos_snapped = grid_size * math::floor(pos_world / grid_size + 0.5f);
3011 positions[point_i] = math::transform_point(world_to_layer, pos_snapped);
3012 });
3013
3014 drawing_info.drawing.tag_positions_changed();
3018 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, &grease_pencil);
3019 }
3020
3021 return OPERATOR_FINISHED;
3022}
3023
3025{
3026 ot->name = "Snap Selection to Grid";
3027 ot->idname = "GREASE_PENCIL_OT_snap_to_grid";
3028 ot->description = "Snap selected points to the nearest grid points";
3029
3032
3033 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3034}
3035
3037
3038/* -------------------------------------------------------------------- */
3041
3043{
3045
3046 const Scene &scene = *CTX_data_scene(C);
3047 Object &object = *CTX_data_active_object(C);
3048 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
3049 const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
3050 const float3 cursor_world = scene.cursor.location;
3051
3052 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
3053 for (const MutableDrawingInfo &drawing_info : drawings) {
3054 bke::CurvesGeometry &curves = drawing_info.drawing.strokes_for_write();
3055 if (curves.curves_num() == 0) {
3056 continue;
3057 }
3059 continue;
3060 }
3061
3062 IndexMaskMemory selected_points_memory;
3064 selected_points_memory);
3065
3066 const Layer &layer = grease_pencil.layer(drawing_info.layer_index);
3067 const float4x4 layer_to_world = layer.to_world_space(object);
3068 const float4x4 world_to_layer = math::invert(layer_to_world);
3069 const float3 cursor_layer = math::transform_point(world_to_layer, cursor_world);
3070
3071 MutableSpan<float3> positions = curves.positions_for_write();
3072 if (use_offset) {
3073 const OffsetIndices points_by_curve = curves.points_by_curve();
3074 IndexMaskMemory selected_curves_memory;
3075 const IndexMask selected_curves = ed::curves::retrieve_selected_curves(
3076 curves, selected_curves_memory);
3077
3078 selected_curves.foreach_index(GrainSize(512), [&](const int curve_i) {
3079 const IndexRange points = points_by_curve[curve_i];
3080
3081 /* Offset from first point of the curve. */
3082 const float3 offset = cursor_layer - positions[points.first()];
3083 selected_points.slice_content(points).foreach_index(
3084 GrainSize(4096), [&](const int point_i) { positions[point_i] += offset; });
3085 });
3086 }
3087 else {
3088 /* Set all selected positions to the cursor location. */
3089 index_mask::masked_fill(positions, cursor_layer, selected_points);
3090 }
3091
3092 drawing_info.drawing.tag_positions_changed();
3096 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, &grease_pencil);
3097 }
3098
3099 return OPERATOR_FINISHED;
3100}
3101
3103{
3104 /* identifiers */
3105 ot->name = "Snap Selection to Cursor";
3106 ot->idname = "GREASE_PENCIL_OT_snap_to_cursor";
3107 ot->description = "Snap selected points/strokes to the cursor";
3108
3109 /* callbacks */
3112
3113 /* flags */
3114 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3115
3116 /* props */
3117 ot->prop = RNA_def_boolean(ot->srna,
3118 "use_offset",
3119 true,
3120 "With Offset",
3121 "Offset the entire stroke instead of selected points only");
3122}
3123
3125
3126/* -------------------------------------------------------------------- */
3129
3131 const Object &object,
3132 const GreasePencil &grease_pencil,
3133 float3 &r_centroid,
3134 float3 &r_min,
3135 float3 &r_max)
3136{
3138
3139 int num_selected = 0;
3140 r_centroid = float3(0.0f);
3141 r_min = float3(std::numeric_limits<float>::max());
3142 r_max = float3(std::numeric_limits<float>::lowest());
3143
3144 const Vector<DrawingInfo> drawings = retrieve_visible_drawings(scene, grease_pencil, false);
3145 for (const DrawingInfo &drawing_info : drawings) {
3146 const Layer &layer = grease_pencil.layer(drawing_info.layer_index);
3147 if (layer.is_locked()) {
3148 continue;
3149 }
3150 const bke::CurvesGeometry &curves = drawing_info.drawing.strokes();
3151 if (curves.curves_num() == 0) {
3152 continue;
3153 }
3155 continue;
3156 }
3157
3158 IndexMaskMemory selected_points_memory;
3160 selected_points_memory);
3161 const float4x4 layer_to_world = layer.to_world_space(object);
3162
3163 Span<float3> positions = curves.positions();
3164 selected_points.foreach_index(GrainSize(4096), [&](const int point_i) {
3165 const float3 pos_world = math::transform_point(layer_to_world, positions[point_i]);
3166 r_centroid += pos_world;
3167 math::min_max(pos_world, r_min, r_max);
3168 });
3169 num_selected += selected_points.size();
3170 }
3171 if (num_selected == 0) {
3172 r_min = r_max = float3(0.0f);
3173 return false;
3174 }
3175
3176 r_centroid /= num_selected;
3177 return true;
3178}
3179
3181{
3182 Scene &scene = *CTX_data_scene(C);
3183 const Object &object = *CTX_data_active_object(C);
3184 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
3185 float3 &cursor = reinterpret_cast<float3 &>(scene.cursor.location);
3186
3187 float3 centroid, points_min, points_max;
3189 scene, object, grease_pencil, centroid, points_min, points_max))
3190 {
3191 return OPERATOR_FINISHED;
3192 }
3193
3194 switch (scene.toolsettings->transform_pivot_point) {
3196 cursor = math::midpoint(points_min, points_max);
3197 break;
3199 case V3D_AROUND_CURSOR:
3201 case V3D_AROUND_ACTIVE:
3202 cursor = centroid;
3203 break;
3204 default:
3206 }
3207
3210
3211 return OPERATOR_FINISHED;
3212}
3213
3215{
3216 /* identifiers */
3217 ot->name = "Snap Cursor to Selected Points";
3218 ot->idname = "GREASE_PENCIL_OT_snap_cursor_to_selected";
3219 ot->description = "Snap cursor to center of selected points";
3220
3221 /* callbacks */
3224
3225 /* flags */
3226 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3227}
3228
3230{
3231 float4x3 strokemat4x3 = float4x3(strokemat);
3232
3233 /*
3234 * We need the diagonal of ones to start from the bottom right instead top left to properly
3235 * apply the two matrices.
3236 *
3237 * i.e.
3238 * # # # # # # # #
3239 * We need # # # # Instead of # # # #
3240 * 0 0 0 1 0 0 1 0
3241 *
3242 */
3243 strokemat4x3[2][2] = 0.0f;
3244 strokemat4x3[3][2] = 1.0f;
3245
3246 return strokemat4x3;
3247}
3248
3250{
3251 const Scene *scene = CTX_data_scene(C);
3252 Object *object = CTX_data_active_object(C);
3253 ARegion *region = CTX_wm_region(C);
3254 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3255
3256 std::atomic<bool> changed = false;
3257 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
3258 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
3259 IndexMaskMemory memory;
3261 *object, info.drawing, info.layer_index, memory);
3262 if (strokes.is_empty()) {
3263 return;
3264 }
3265
3266 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
3267 const float4x4 layer_space_to_world_space = layer.to_world_space(*object);
3268
3269 /* Calculate screen space points. */
3270 const float2 screen_start(RNA_int_get(op->ptr, "xstart"), RNA_int_get(op->ptr, "ystart"));
3271 const float2 screen_end(RNA_int_get(op->ptr, "xend"), RNA_int_get(op->ptr, "yend"));
3272 const float2 screen_direction = screen_end - screen_start;
3273 const float2 screen_tangent = screen_start + float2(-screen_direction[1], screen_direction[0]);
3274
3275 const bke::CurvesGeometry &curves = info.drawing.strokes();
3276 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
3277 const Span<float3> positions = curves.positions();
3279 const VArray<int> materials = *curves.attributes().lookup_or_default<int>(
3280 "material_index", bke::AttrDomain::Curve, 0);
3281
3282 Array<float4x2> texture_matrices(strokes.size());
3283
3284 strokes.foreach_index([&](const int curve_i, const int pos) {
3285 const int material_index = materials[curve_i];
3286
3287 const MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(object,
3288 material_index + 1);
3289 const bool is_radial = gp_style->gradient_type == GP_MATERIAL_GRADIENT_RADIAL;
3290
3291 const float texture_angle = gp_style->texture_angle;
3292 const float2 texture_scale = float2(gp_style->texture_scale);
3293 const float2 texture_offset = float2(gp_style->texture_offset);
3294
3295 const float2x2 texture_rotation = math::from_rotation<float2x2>(
3296 math::AngleRadian(texture_angle));
3297
3298 const float3 point = math::transform_point(layer_space_to_world_space,
3299 positions[points_by_curve[curve_i].first()]);
3300 const float3 normal = math::transform_direction(layer_space_to_world_space,
3301 normals[curve_i]);
3302
3303 const float4 plane = float4(normal, -math::dot(normal, point));
3304
3305 float3 start;
3306 float3 tangent;
3307 float3 end;
3308 ED_view3d_win_to_3d_on_plane(region, plane, screen_start, false, start);
3309 ED_view3d_win_to_3d_on_plane(region, plane, screen_tangent, false, tangent);
3310 ED_view3d_win_to_3d_on_plane(region, plane, screen_end, false, end);
3311
3312 const float3 origin = start;
3313 /* Invert the length by dividing by the length squared. */
3314 const float3 u_dir = (end - origin) / math::length_squared(end - origin);
3315 float3 v_dir = math::cross(u_dir, normal);
3316
3317 /* Flip the texture if need so that it is not mirrored. */
3318 if (math::dot(tangent - start, v_dir) < 0.0f) {
3319 v_dir = -v_dir;
3320 }
3321
3322 /* Calculate the texture space before the texture offset transformation. */
3323 const float4x2 base_texture_space = math::transpose(float2x4(
3324 float4(u_dir, -math::dot(u_dir, origin)), float4(v_dir, -math::dot(v_dir, origin))));
3325
3326 float3x2 offset_matrix = float3x2::identity();
3327
3328 if (is_radial) {
3329 /* Radial gradients are scaled down by a factor of 2 and have the center at 0.5 */
3330 offset_matrix *= 0.5f;
3331 offset_matrix[2] += float2(0.5f, 0.5f);
3332 }
3333
3334 /* For some reason 0.5 is added to the offset before being rendered, so remove it here. */
3335 offset_matrix[2] -= float2(0.5f, 0.5f);
3336
3337 offset_matrix = math::from_scale<float2x2>(texture_scale) * offset_matrix;
3338 offset_matrix = texture_rotation * offset_matrix;
3339 offset_matrix[2] -= texture_offset;
3340
3341 texture_matrices[pos] = (offset_matrix * expand_4x2_mat(base_texture_space)) *
3342 layer_space_to_world_space;
3343 });
3344
3345 info.drawing.set_texture_matrices(texture_matrices, strokes);
3346
3347 changed.store(true, std::memory_order_relaxed);
3348 });
3349
3350 if (changed) {
3351 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3352 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3353 }
3354
3356}
3357
3359{
3360 int ret = WM_gesture_straightline_modal(C, op, event);
3361
3362 /* Check for mouse release. */
3363 if ((ret & OPERATOR_RUNNING_MODAL) != 0 && event->type == LEFTMOUSE && event->val == KM_RELEASE)
3364 {
3368 }
3369
3370 return ret;
3371}
3372
3374{
3375 /* Invoke interactive line drawing (representing the gradient) in viewport. */
3376 const int ret = WM_gesture_straightline_invoke(C, op, event);
3377
3378 if ((ret & OPERATOR_RUNNING_MODAL) != 0) {
3379 ARegion *region = CTX_wm_region(C);
3380 if (region->regiontype == RGN_TYPE_WINDOW && event->type == LEFTMOUSE &&
3381 event->val == KM_PRESS)
3382 {
3383 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
3384 gesture->is_active = true;
3385 }
3386 }
3387
3388 return ret;
3389}
3390
3392{
3393 /* Identifiers. */
3394 ot->name = "Texture Gradient";
3395 ot->idname = "GREASE_PENCIL_OT_texture_gradient";
3396 ot->description = "Draw a line to set the fill material gradient for the selected strokes";
3397
3398 /* Api callbacks. */
3404
3405 /* Flags. */
3406 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3407
3409}
3410
3412
3413/* -------------------------------------------------------------------- */
3416
3418{
3419 const Scene *scene = CTX_data_scene(C);
3420 Object *object = CTX_data_active_object(C);
3421 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3422
3423 const CurveType dst_type = CurveType(RNA_enum_get(op->ptr, "type"));
3424 const bool use_handles = RNA_boolean_get(op->ptr, "use_handles");
3425
3426 bool changed = false;
3427 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
3428 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
3430 IndexMaskMemory memory;
3432 *object, info.drawing, info.layer_index, memory);
3433 if (strokes.is_empty()) {
3434 return;
3435 }
3436
3438 options.convert_bezier_handles_to_poly_points = use_handles;
3439 options.convert_bezier_handles_to_catmull_rom_points = use_handles;
3440 options.keep_bezier_shape_as_nurbs = use_handles;
3441 options.keep_catmull_rom_shape_as_nurbs = use_handles;
3442
3443 curves = geometry::convert_curves(curves, strokes, dst_type, {}, options);
3445
3446 changed = true;
3447 });
3448
3449 if (changed) {
3450 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3451 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3452 }
3453
3454 return OPERATOR_FINISHED;
3455}
3456
3458{
3459 ot->name = "Set Curve Type";
3460 ot->idname = "GREASE_PENCIL_OT_set_curve_type";
3461 ot->description = "Set type of selected curves";
3462
3463 ot->invoke = WM_menu_invoke;
3466
3467 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3468
3469 ot->prop = RNA_def_enum(
3470 ot->srna, "type", rna_enum_curves_type_items, CURVE_TYPE_POLY, "Type", "Curve type");
3471
3472 RNA_def_boolean(ot->srna,
3473 "use_handles",
3474 false,
3475 "Handles",
3476 "Take handle information into account in the conversion");
3477}
3478
3480
3481/* -------------------------------------------------------------------- */
3484
3486{
3487 const Scene *scene = CTX_data_scene(C);
3488 Object *object = CTX_data_active_object(C);
3489 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3490
3491 const HandleType dst_handle_type = HandleType(RNA_enum_get(op->ptr, "type"));
3492
3493 bool changed = false;
3494 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
3495 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
3497 if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
3498 return;
3499 }
3500 IndexMaskMemory memory;
3502 *object, info.drawing, info.layer_index, memory);
3503 const IndexMask bezier_curves = curves.indices_for_curve_type(
3504 CURVE_TYPE_BEZIER, editable_strokes, memory);
3505
3506 const bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
3507 const VArraySpan<bool> selection = *attributes.lookup_or_default<bool>(
3508 ".selection", bke::AttrDomain::Point, true);
3509 const VArraySpan<bool> selection_left = *attributes.lookup_or_default<bool>(
3510 ".selection_handle_left", bke::AttrDomain::Point, true);
3511 const VArraySpan<bool> selection_right = *attributes.lookup_or_default<bool>(
3512 ".selection_handle_right", bke::AttrDomain::Point, true);
3513
3514 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
3515 MutableSpan<int8_t> handle_types_left = curves.handle_types_left_for_write();
3516 MutableSpan<int8_t> handle_types_right = curves.handle_types_right_for_write();
3517 bezier_curves.foreach_index(GrainSize(256), [&](const int curve_i) {
3518 const IndexRange points = points_by_curve[curve_i];
3519 for (const int point_i : points) {
3520 if (selection_left[point_i] || selection[point_i]) {
3521 handle_types_left[point_i] = int8_t(dst_handle_type);
3522 }
3523 if (selection_right[point_i] || selection[point_i]) {
3524 handle_types_right[point_i] = int8_t(dst_handle_type);
3525 }
3526 }
3527 });
3528
3529 curves.calculate_bezier_auto_handles();
3530 curves.tag_topology_changed();
3532
3533 changed = true;
3534 });
3535
3536 if (changed) {
3537 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3538 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3539 }
3540
3541 return OPERATOR_FINISHED;
3542}
3543
3545{
3546 ot->name = "Set Handle Type";
3547 ot->idname = "GREASE_PENCIL_OT_set_handle_type";
3548 ot->description = "Set the handle type for bezier curves";
3549
3550 ot->invoke = WM_menu_invoke;
3553
3554 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3555
3556 ot->prop = RNA_def_enum(
3557 ot->srna, "type", rna_enum_curves_handle_type_items, CURVE_TYPE_POLY, "Type", nullptr);
3558}
3559
3561
3562/* -------------------------------------------------------------------- */
3565
3567{
3568 const Scene *scene = CTX_data_scene(C);
3569 Object *object = CTX_data_active_object(C);
3570 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3571
3572 const int resolution = RNA_int_get(op->ptr, "resolution");
3573
3574 bool changed = false;
3575 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
3576 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
3578 IndexMaskMemory memory;
3580 *object, info.drawing, info.layer_index, memory);
3581 if (editable_strokes.is_empty()) {
3582 return;
3583 }
3584
3585 if (curves.is_single_type(CURVE_TYPE_POLY)) {
3586 return;
3587 }
3588
3589 index_mask::masked_fill(curves.resolution_for_write(), resolution, editable_strokes);
3591 changed = true;
3592 });
3593
3594 if (changed) {
3595 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3596 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3597 }
3598
3599 return OPERATOR_FINISHED;
3600}
3601
3603{
3604 ot->name = "Set Curve Resolution";
3605 ot->idname = "GREASE_PENCIL_OT_set_curve_resolution";
3606 ot->description = "Set resolution of selected curves";
3607
3610
3611 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3612
3613 RNA_def_int(ot->srna,
3614 "resolution",
3615 12,
3616 0,
3617 10000,
3618 "Resolution",
3619 "The resolution to use for each curve segment",
3620 1,
3621 64);
3622}
3623
3625
3626/* -------------------------------------------------------------------- */
3629
3631{
3632 const Scene *scene = CTX_data_scene(C);
3633 Object *object = CTX_data_active_object(C);
3634 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3635
3636 bool changed = false;
3637 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
3638 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
3640 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
3641 IndexMaskMemory memory;
3643 *object, info.drawing, info.layer_index, memory);
3644 if (editable_strokes.is_empty()) {
3645 return;
3646 }
3647
3648 if (attributes.contains("uv_rotation")) {
3649 if (editable_strokes.size() == curves.curves_num()) {
3650 attributes.remove("uv_rotation");
3651 }
3652 else {
3653 bke::SpanAttributeWriter<float> uv_rotations = attributes.lookup_for_write_span<float>(
3654 "uv_rotation");
3655 index_mask::masked_fill(uv_rotations.span, 0.0f, editable_strokes);
3656 uv_rotations.finish();
3657 }
3658 }
3659
3660 if (attributes.contains("uv_translation")) {
3661 if (editable_strokes.size() == curves.curves_num()) {
3662 attributes.remove("uv_translation");
3663 }
3664 else {
3665 bke::SpanAttributeWriter<float2> uv_translations =
3666 attributes.lookup_for_write_span<float2>("uv_translation");
3667 index_mask::masked_fill(uv_translations.span, float2(0.0f, 0.0f), editable_strokes);
3668 uv_translations.finish();
3669 }
3670 }
3671
3672 if (attributes.contains("uv_scale")) {
3673 if (editable_strokes.size() == curves.curves_num()) {
3674 attributes.remove("uv_scale");
3675 }
3676 else {
3678 "uv_scale");
3679 index_mask::masked_fill(uv_scales.span, float2(1.0f, 1.0f), editable_strokes);
3680 uv_scales.finish();
3681 }
3682 }
3683
3684 if (attributes.contains("uv_shear")) {
3685 if (editable_strokes.size() == curves.curves_num()) {
3686 attributes.remove("uv_shear");
3687 }
3688 else {
3689 bke::SpanAttributeWriter<float> uv_shears = attributes.lookup_for_write_span<float>(
3690 "uv_shear");
3691 index_mask::masked_fill(uv_shears.span, 0.0f, editable_strokes);
3692 uv_shears.finish();
3693 }
3694 }
3695
3697 changed = true;
3698 });
3699
3700 if (changed) {
3701 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3702 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3703 }
3704
3705 return OPERATOR_FINISHED;
3706}
3707
3709{
3710 /* Identifiers. */
3711 ot->name = "Reset UVs";
3712 ot->idname = "GREASE_PENCIL_OT_reset_uvs";
3713 ot->description = "Reset UV transformation to default values";
3714
3715 /* Callbacks. */
3718
3719 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3720}
3721
3723
3724} // namespace blender::ed::greasepencil
3725
3727{
3728 using namespace blender::ed::greasepencil;
3762}
3763
3764/* -------------------------------------------------------------------- */
3767
3768namespace blender::ed::greasepencil {
3769
3770/* Note: the `duplicate_layer` API would be nicer, but only supports duplicating groups from the
3771 * same datablock. */
3774 const bke::greasepencil::Layer &layer_src)
3775{
3776 using namespace blender::bke::greasepencil;
3777
3778 Layer &layer_dst = grease_pencil_dst.add_layer(group_dst, layer_src.name());
3779 BKE_grease_pencil_copy_layer_parameters(layer_src, layer_dst);
3780
3781 layer_dst.frames_for_write() = layer_src.frames();
3782 layer_dst.tag_frames_map_changed();
3783
3784 return layer_dst;
3785}
3786
3788 GreasePencil &grease_pencil_dst,
3790 const bke::greasepencil::LayerGroup &group_src,
3791 Map<StringRefNull, StringRefNull> &layer_name_map);
3792
3793static void copy_layer_group_content(GreasePencil &grease_pencil_dst,
3795 const bke::greasepencil::LayerGroup &group_src,
3796 Map<StringRefNull, StringRefNull> &layer_name_map)
3797{
3798 using namespace blender::bke::greasepencil;
3799
3800 for (const bke::greasepencil::TreeNode *node : group_src.nodes()) {
3801 if (node->is_group()) {
3802 copy_layer_group_recursive(grease_pencil_dst, group_dst, node->as_group(), layer_name_map);
3803 }
3804 if (node->is_layer()) {
3805 Layer &layer_dst = copy_layer(grease_pencil_dst, group_dst, node->as_layer());
3806 layer_name_map.add_new(node->as_layer().name(), layer_dst.name());
3807 }
3808 }
3809}
3810
3812 GreasePencil &grease_pencil_dst,
3814 const bke::greasepencil::LayerGroup &group_src,
3815 Map<StringRefNull, StringRefNull> &layer_name_map)
3816{
3817 bke::greasepencil::LayerGroup &group_dst = grease_pencil_dst.add_layer_group(
3818 parent_dst, group_src.base.name);
3820
3821 copy_layer_group_content(grease_pencil_dst, group_dst, group_src, layer_name_map);
3822 return group_dst;
3823}
3824
3826 VectorSet<Material *> &materials)
3827{
3828 Array<int> material_index_map(grease_pencil.material_array_num);
3829 for (const int i : material_index_map.index_range()) {
3830 Material *material = grease_pencil.material_array[i];
3831 material_index_map[i] = materials.index_of_or_add(material);
3832 }
3833 return material_index_map;
3834}
3835
3837 const Span<int> material_index_map)
3838{
3840 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
3841 /* Validate material indices and add missing materials. */
3842 bke::SpanAttributeWriter<int> material_writer = attributes.lookup_or_add_for_write_span<int>(
3843 "material_index", bke::AttrDomain::Curve);
3844 threading::parallel_for(curves.curves_range(), 1024, [&](const IndexRange range) {
3845 for (const int curve_i : range) {
3846 material_writer.span[curve_i] = material_index_map[material_writer.span[curve_i]];
3847 }
3848 });
3849 material_writer.finish();
3850}
3851
3853 GreasePencil &grease_pencil,
3854 const ListBase &vertex_group_names)
3855{
3856 Map<StringRefNull, StringRefNull> vertex_group_map;
3857 LISTBASE_FOREACH (bDeformGroup *, dg, &vertex_group_names) {
3858 bDeformGroup *vgroup = static_cast<bDeformGroup *>(MEM_dupallocN(dg));
3859 BKE_object_defgroup_unique_name(vgroup, &object);
3860 BLI_addtail(&grease_pencil.vertex_group_names, vgroup);
3861 vertex_group_map.add_new(dg->name, vgroup->name);
3862 }
3863 return vertex_group_map;
3864}
3865
3867 const Map<StringRefNull, StringRefNull> &vertex_group_map)
3868{
3870 STRNCPY(dg->name, vertex_group_map.lookup(dg->name).c_str());
3871 }
3872
3873 /* Indices in vertex weights remain valid, they are local to the drawing's vertex groups.
3874 * Only the names of the groups change. */
3875}
3876
3878 Object &ob_src,
3879 Object &ob_dst,
3880 VectorSet<Material *> &materials)
3881{
3882 using namespace blender::bke::greasepencil;
3883
3884 /* Skip if the datablock is already used by the active object. */
3885 if (ob_src.data == ob_dst.data) {
3886 return;
3887 }
3888
3889 BLI_assert(ob_src.type == OB_GREASE_PENCIL);
3890 BLI_assert(ob_dst.type == OB_GREASE_PENCIL);
3891 GreasePencil &grease_pencil_src = *static_cast<GreasePencil *>(ob_src.data);
3892 GreasePencil &grease_pencil_dst = *static_cast<GreasePencil *>(ob_dst.data);
3893 /* Number of existing layers that don't need to be updated. */
3894 const int orig_layers_num = grease_pencil_dst.layers().size();
3895
3897 ob_dst, grease_pencil_dst, grease_pencil_src.vertex_group_names);
3898 const Array<int> material_index_map = add_materials_to_map(grease_pencil_src, materials);
3899
3900 /* Concatenate drawing arrays. Existing drawings in dst keep their position, new drawings are
3901 * mapped to the new index range. */
3902 const int new_drawing_array_num = grease_pencil_dst.drawing_array_num +
3903 grease_pencil_src.drawing_array_num;
3904 GreasePencilDrawingBase **new_drawing_array = static_cast<GreasePencilDrawingBase **>(
3905 MEM_malloc_arrayN(new_drawing_array_num, sizeof(GreasePencilDrawingBase *), __func__));
3906 MutableSpan<GreasePencilDrawingBase *> new_drawings = {new_drawing_array, new_drawing_array_num};
3907 const IndexRange new_drawings_dst = IndexRange::from_begin_size(
3908 0, grease_pencil_dst.drawing_array_num);
3909 const IndexRange new_drawings_src = IndexRange::from_begin_size(
3910 grease_pencil_dst.drawing_array_num, grease_pencil_src.drawing_array_num);
3911
3912 copy_drawing_array(grease_pencil_dst.drawings(), new_drawings.slice(new_drawings_dst));
3913 copy_drawing_array(grease_pencil_src.drawings(), new_drawings.slice(new_drawings_src));
3914
3915 /* Free existing drawings array. */
3916 grease_pencil_dst.resize_drawings(0);
3917 grease_pencil_dst.drawing_array = new_drawing_array;
3918 grease_pencil_dst.drawing_array_num = new_drawing_array_num;
3919
3920 /* Maps original names of source layers to new unique layer names. */
3921 Map<StringRefNull, StringRefNull> layer_name_map;
3922 /* Only copy the content of the root group, not the root node itself. */
3923 copy_layer_group_content(grease_pencil_dst,
3924 grease_pencil_dst.root_group(),
3925 grease_pencil_src.root_group(),
3926 layer_name_map);
3927
3928 /* Copy custom attributes for new layers. */
3929 CustomData_merge_layout(&grease_pencil_src.layers_data,
3930 &grease_pencil_dst.layers_data,
3933 grease_pencil_dst.layers().size());
3934 CustomData_copy_data(&grease_pencil_src.layers_data,
3935 &grease_pencil_dst.layers_data,
3936 0,
3937 orig_layers_num,
3938 grease_pencil_src.layers().size());
3939
3940 /* Fix names, indices and transforms to keep relationships valid. */
3941 for (const int layer_index : grease_pencil_dst.layers().index_range()) {
3942 Layer &layer = *grease_pencil_dst.layers_for_write()[layer_index];
3943 const bool is_orig_layer = (layer_index < orig_layers_num);
3944 const float4x4 old_layer_to_world = (is_orig_layer ? layer.to_world_space(ob_dst) :
3945 layer.to_world_space(ob_src));
3946
3947 /* Update newly added layers. */
3948 if (!is_orig_layer) {
3949 /* Update name references for masks. */
3950 LISTBASE_FOREACH (GreasePencilLayerMask *, dst_mask, &layer.masks) {
3951 const StringRefNull *new_mask_name = layer_name_map.lookup_ptr(dst_mask->layer_name);
3952 if (new_mask_name) {
3953 MEM_SAFE_FREE(dst_mask->layer_name);
3954 dst_mask->layer_name = BLI_strdup(new_mask_name->c_str());
3955 }
3956 }
3957 /* Shift drawing indices to match the new drawings array. */
3958 for (const int key : layer.frames_for_write().keys()) {
3959 int &drawing_index = layer.frames_for_write().lookup(key).drawing_index;
3960 drawing_index = new_drawings_src[drawing_index];
3961 }
3962 }
3963
3964 /* Layer parent object may become invalid. This can be an original layer pointing at the joined
3965 * object which gets destroyed, or a new layer that points at the target object which is now
3966 * its owner. */
3967 if (ELEM(layer.parent, &ob_dst, &ob_src)) {
3968 layer.parent = nullptr;
3969 }
3970
3971 /* Apply relative object transform to new drawings to keep world-space positions unchanged.
3972 * Be careful where the matrix is computed: changing the parent pointer (above) can affect
3973 * this! */
3974 const float4x4 new_layer_to_world = layer.to_world_space(ob_dst);
3975 for (const int key : layer.frames_for_write().keys()) {
3976 const int drawing_index = layer.frames_for_write().lookup(key).drawing_index;
3977 GreasePencilDrawingBase *drawing_base = grease_pencil_dst.drawings()[drawing_index];
3978 if (drawing_base->type != GP_DRAWING) {
3979 continue;
3980 }
3981 Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base)->wrap();
3983 curves.transform(math::invert(new_layer_to_world) * old_layer_to_world);
3984
3985 if (!is_orig_layer) {
3986 remap_vertex_groups(drawing, vertex_group_map);
3987 remap_material_indices(drawing, material_index_map);
3988 }
3989 }
3990 }
3991
3992 /* Rename animation paths to layers. */
3993 BKE_fcurves_main_cb(&bmain, [&](ID *id, FCurve *fcu) {
3994 if (id == &grease_pencil_src.id && fcu->rna_path && strstr(fcu->rna_path, "layers[")) {
3995 /* Have to use linear search, the layer name map only contains sub-strings of RNA paths. */
3996 for (auto [name_src, name_dst] : layer_name_map.items()) {
3997 if (name_dst != name_src) {
3998 const char *old_path = fcu->rna_path;
4000 id, fcu->rna_path, "layers", name_src.c_str(), name_dst.c_str(), 0, 0, false);
4001 if (old_path != fcu->rna_path) {
4002 /* Stop after first match. */
4003 break;
4004 }
4005 }
4006 }
4007 }
4008 /* Fix driver targets. */
4009 if (fcu->driver) {
4010 LISTBASE_FOREACH (DriverVar *, dvar, &fcu->driver->variables) {
4011 /* Only change the used targets, since the others will need fixing manually anyway. */
4013 if (dtar->id != &grease_pencil_src.id) {
4014 continue;
4015 }
4016 dtar->id = &grease_pencil_dst.id;
4017
4018 if (dtar->rna_path && strstr(dtar->rna_path, "layers[")) {
4019 for (auto [name_src, name_dst] : layer_name_map.items()) {
4020 if (name_dst != name_src) {
4021 const char *old_path = fcu->rna_path;
4022 dtar->rna_path = BKE_animsys_fix_rna_path_rename(
4023 id, dtar->rna_path, "layers", name_src.c_str(), name_dst.c_str(), 0, 0, false);
4024 if (old_path != dtar->rna_path) {
4025 break;
4026 }
4027 }
4028 }
4029 }
4030 }
4032 }
4033 }
4034 });
4035
4036 /* Merge animation data of objects and grease pencil datablocks. */
4037 if (ob_src.adt) {
4038 if (ob_dst.adt == nullptr) {
4039 ob_dst.adt = BKE_animdata_copy(&bmain, ob_src.adt, 0);
4040 }
4041 else {
4042 BKE_animdata_merge_copy(&bmain, &ob_dst.id, &ob_src.id, ADT_MERGECOPY_KEEP_DST, false);
4043 }
4044
4045 if (ob_dst.adt->action) {
4047 }
4048 }
4049 if (grease_pencil_src.adt) {
4050 if (grease_pencil_dst.adt == nullptr) {
4051 grease_pencil_dst.adt = BKE_animdata_copy(&bmain, grease_pencil_src.adt, 0);
4052 }
4053 else {
4055 &bmain, &grease_pencil_dst.id, &grease_pencil_src.id, ADT_MERGECOPY_KEEP_DST, false);
4056 }
4057
4058 if (grease_pencil_dst.adt->action) {
4060 }
4061 }
4062}
4063
4064} // namespace blender::ed::greasepencil
4065
4067{
4068 Main *bmain = CTX_data_main(C);
4069 Scene *scene = CTX_data_scene(C);
4070 Object *ob_active = CTX_data_active_object(C);
4071
4072 /* Ensure we're in right mode and that the active object is correct. */
4073 if (!ob_active || ob_active->type != OB_GREASE_PENCIL) {
4074 return OPERATOR_CANCELLED;
4075 }
4076
4077 bool ok = false;
4078 CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
4079 if (ob_iter == ob_active) {
4080 ok = true;
4081 break;
4082 }
4083 }
4085 /* Active object must always selected. */
4086 if (ok == false) {
4087 BKE_report(op->reports, RPT_WARNING, "Active object is not a selected grease pencil");
4088 return OPERATOR_CANCELLED;
4089 }
4090
4091 Object *ob_dst = ob_active;
4092 GreasePencil *grease_pencil_dst = static_cast<GreasePencil *>(ob_dst->data);
4093
4096 *grease_pencil_dst, materials);
4097 /* Reassign material indices in the original layers, in case materials are deduplicated. */
4098 for (GreasePencilDrawingBase *drawing_base : grease_pencil_dst->drawings()) {
4099 if (drawing_base->type != GP_DRAWING) {
4100 continue;
4101 }
4103 reinterpret_cast<GreasePencilDrawing *>(drawing_base)->wrap();
4104 blender::ed::greasepencil::remap_material_indices(drawing, material_index_map);
4105 }
4106
4107 /* Loop and join all data. */
4108 CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
4109 if (ob_iter->type != OB_GREASE_PENCIL || ob_iter == ob_active) {
4110 continue;
4111 }
4112
4113 blender::ed::greasepencil::join_object_with_active(*bmain, *ob_iter, *ob_dst, materials);
4114
4115 /* Free the old object. */
4116 blender::ed::object::base_free_and_unlink(bmain, scene, ob_iter);
4117 }
4119
4120 /* Transfer material pointers. The material indices are updated for each drawing separately. */
4121 if (!materials.is_empty()) {
4122 /* Old C API, needs a const_cast but doesn't actually change anything. */
4123 Material **materials_ptr = const_cast<Material **>(materials.data());
4125 bmain, DEG_get_original_object(ob_dst), &materials_ptr, materials.size(), false);
4126 }
4127
4128 DEG_id_tag_update(&grease_pencil_dst->id, ID_RECALC_GEOMETRY);
4130
4133
4134 return OPERATOR_FINISHED;
4135}
4136
void BKE_animdata_merge_copy(Main *bmain, ID *dst_id, ID *src_id, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers)
Definition anim_data.cc:530
AnimData * BKE_animdata_copy(Main *bmain, AnimData *adt, int flag)
Definition anim_data.cc:447
@ ADT_MERGECOPY_KEEP_DST
void BKE_fcurves_main_cb(struct Main *bmain, blender::FunctionRef< void(ID *, FCurve *)> func)
char * BKE_animsys_fix_rna_path_rename(struct ID *owner_id, char *old_path, const char *prefix, const char *oldName, const char *newName, int oldSubscript, int newSubscript, bool verify_paths)
Definition anim_data.cc:981
#define CTX_DATA_BEGIN(C, Type, instance, member)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Base * CTX_data_active_base(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
@ CD_SET_DEFAULT
void CustomData_copy_data(const CustomData *source, CustomData *dest, int source_index, int dest_index, int count)
bool CustomData_merge_layout(const CustomData *source, CustomData *dest, eCustomDataMask mask, eCDAllocType alloctype, int totelem)
support for deformation groups and hooks.
void BKE_object_defgroup_unique_name(bDeformGroup *dg, Object *ob)
Definition deform.cc:752
void BKE_defgroup_copy_list(ListBase *outbase, const ListBase *inbase)
Definition deform.cc:76
#define DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar)
#define DRIVER_TARGETS_LOOPER_END
Low-level operations for grease pencil.
void * BKE_grease_pencil_add(Main *bmain, const char *name)
Material * BKE_grease_pencil_object_material_new(Main *bmain, Object *ob, const char *name, int *r_index)
void BKE_grease_pencil_copy_layer_parameters(const blender::bke::greasepencil::Layer &src, blender::bke::greasepencil::Layer &dst)
void BKE_grease_pencil_copy_layer_group_parameters(const blender::bke::greasepencil::LayerGroup &src, blender::bke::greasepencil::LayerGroup &dst)
ID * BKE_libblock_find_name(Main *bmain, short type, const char *name, const std::optional< Library * > lib=std::nullopt) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition lib_id.cc:1657
General operations, lookup, etc. for materials.
bool BKE_object_material_slot_remove(struct Main *bmain, struct Object *ob)
struct Material * BKE_object_material_get(struct Object *ob, short act)
bool BKE_object_material_slot_used(struct Object *object, short actcol)
short * BKE_object_material_len_p(struct Object *ob)
struct Material *** BKE_object_material_array_p(struct Object *ob)
int BKE_object_material_ensure(Main *bmain, Object *ob, Material *material)
void BKE_object_material_array_assign(struct Main *bmain, struct Object *ob, struct Material ***matar, int totcol, bool to_object_only)
struct MaterialGPencilStyle * BKE_gpencil_material_settings(struct Object *ob, short act)
int BKE_object_material_index_get(Object *ob, const Material *ma)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
Definition scene.cc:2647
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
MINLINE float interpf(float target, float origin, float t)
float dist_to_line_v3(const float p[3], const float l1[3], const float l2[3])
Definition math_geom.cc:539
#define BLI_SCOPED_DEFER(function_to_defer)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define STRNCPY(dst, src)
Definition BLI_string.h:593
unsigned int uint
#define ELEM(...)
#define TIP_(msgid)
#define BLT_I18NCONTEXT_ID_GPENCIL
#define IFACE_(msgid)
#define BLT_I18NCONTEXT_ID_MOVIECLIP
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
float DEG_get_ctime(const Depsgraph *graph)
Object * DEG_get_original_object(Object *object)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1021
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ ID_RECALC_ANIMATION_NO_FLUSH
Definition DNA_ID.h:1143
#define MAX_ID_NAME
Definition DNA_ID.h:377
@ ID_MA
CurveType
@ CURVE_TYPE_BEZIER
@ CURVE_TYPE_POLY
HandleType
#define CD_MASK_ALL
@ GP_STROKE_CAP_TYPE_FLAT
@ GP_STROKE_CAP_TYPE_ROUND
@ GP_MATERIAL_GRADIENT_RADIAL
@ OB_MODE_OBJECT
Object is a sort of wrapper for general info.
@ OB_GREASE_PENCIL
@ RGN_TYPE_WINDOW
@ SPACE_VIEW3D
eDupli_ID_Flags
@ USER_DUP_ACT
@ V3D_AROUND_ACTIVE
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_CURSOR
@ V3D_AROUND_CENTER_MEDIAN
@ V3D_AROUND_LOCAL_ORIGINS
@ OPERATOR_RUNNING_MODAL
float ED_view3d_grid_view_scale(const Scene *scene, const View3D *v3d, const ARegion *region, const char **r_grid_unit)
void ED_view3d_depth_override(Depsgraph *depsgraph, ARegion *region, View3D *v3d, Object *obact, eV3DDepthOverrideMode mode, bool use_overlay, ViewDepths **r_depths)
void ED_view3d_depths_free(ViewDepths *depths)
@ V3D_DEPTH_NO_GPENCIL
Definition ED_view3d.hh:188
bool ED_view3d_win_to_3d_on_plane(const ARegion *region, const float plane[4], const float mval[2], bool do_clip, float r_out[3])
MatBase< float, 2, 4 > float2x4
#define MEM_SAFE_FREE(v)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
#define RNA_ENUM_ITEM_SEPR
Definition RNA_types.hh:528
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
#define C
Definition RandGen.cpp:29
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DRAW
Definition WM_types.hh:428
#define ND_OB_ACTIVE
Definition WM_types.hh:407
#define ND_DATA
Definition WM_types.hh:475
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_SCENE
Definition WM_types.hh:345
#define ND_LAYER_CONTENT
Definition WM_types.hh:420
#define NA_EDITED
Definition WM_types.hh:550
#define NC_GPENCIL
Definition WM_types.hh:366
@ KM_PRESS
Definition WM_types.hh:284
@ KM_RELEASE
Definition WM_types.hh:285
#define ND_SPACE_VIEW3D
Definition WM_types.hh:494
#define NC_OBJECT
Definition WM_types.hh:346
#define NC_SPACE
Definition WM_types.hh:359
volatile int lock
#define U
#define A
BPy_StructRNA * depsgraph
VecBase< float, 3 > float3
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static constexpr IndexRange from_begin_size(const int64_t begin, const int64_t size)
KeyIterator keys() const
Definition BLI_map.hh:837
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
constexpr MutableSpan drop_front(const int64_t n) const
Definition BLI_span.hh:608
static VArray ForContainer(ContainerT container)
static VArray ForSingle(T value, const int64_t size)
void append(const T &value)
IndexRange index_range() const
const T & first() const
void clear()
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
IndexRange index_range() const
Definition BLI_array.hh:349
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
constexpr int64_t first() const
constexpr IndexRange shift(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr IndexRange drop_front(int64_t n) const
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:484
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:271
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:531
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:241
ItemIterator items() const
Definition BLI_map.hh:864
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:619
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr const T & first() const
Definition BLI_span.hh:316
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool contains(const T &value) const
Definition BLI_span.hh:278
constexpr const char * c_str() const
std::optional< T > get_if_single() const
int64_t size() const
const Key * data() const
int64_t index_of_or_add(const Key &key)
int64_t size() const
void append(const T &value)
void insert(const int64_t insert_index, const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
IndexRange index_range() const
MutableSpan< T > as_mutable_span()
Span< T > as_span() const
const T & first() const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
bool contains(const StringRef attribute_id) const
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
void resize(int points_num, int curves_num)
void remove_curves(const IndexMask &curves_to_delete, const AttributeFilter &attribute_filter)
MutableSpan< int > offsets_for_write()
VArray< bool > cyclic() const
MutableSpan< bool > cyclic_for_write()
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bool remove(const StringRef attribute_id)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
Span< float3 > curve_plane_normals() const
MutableSpan< float > opacities_for_write()
MutableSpan< float > radii_for_write()
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
VArray< float > opacities() const
void set_texture_matrices(Span< float4x2 > matrices, const IndexMask &selection)
Span< const TreeNode * > nodes() const
float4x4 to_world_space(const Object &object) const
const Map< FramesMapKeyT, GreasePencilFrame > & frames() const
bool has_drawing_at(const int frame_number) const
std::optional< int > start_frame_at(int frame_number) const
float4x4 to_object_space(const Object &object) const
Map< FramesMapKeyT, GreasePencilFrame > & frames_for_write()
void to_indices(MutableSpan< T > r_indices) const
IndexMask slice_content(IndexRange range) const
void foreach_index_optimized(Fn &&fn) const
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
VecBase< float, 2 > float2
CCL_NAMESPACE_BEGIN struct Options options
static bool is_cyclic(const Nurb *nu)
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static ushort indices[]
static float normals[][3]
void ED_operatortypes_grease_pencil_edit()
int ED_grease_pencil_join_objects_exec(bContext *C, wmOperator *op)
blender::bke::AttrDomain ED_grease_pencil_edit_selection_domain_get(const ToolSettings *tool_settings)
void GREASE_PENCIL_OT_stroke_trim(wmOperatorType *ot)
int count
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
ccl_device_inline float4 mask(const int4 mask, const float4 a)
#define B
void invert_booleans(MutableSpan< bool > span)
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
Vector< IndexRange > find_all_ranges(const Span< T > span, const T &value)
BooleanMix booleans_mix_calc(const VArray< bool > &varray, IndexRange range_to_check)
void fill_index_range(MutableSpan< T > span, const T start=0)
void fill_points(OffsetIndices< int > points_by_curve, const IndexMask &curve_selection, GPointer value, GMutableSpan dst)
void copy_drawing_array(Span< const GreasePencilDrawingBase * > src_drawings, MutableSpan< GreasePencilDrawingBase * > dst_drawings)
CurvesGeometry curves_copy_curve_selection(const CurvesGeometry &curves, const IndexMask &curves_to_copy, const AttributeFilter &attribute_filter)
CurvesGeometry curves_copy_point_selection(const CurvesGeometry &curves, const IndexMask &points_to_copy, const AttributeFilter &attribute_filter)
auto attribute_filter_from_skip_ref(const Span< StringRef > skip)
void gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
Curves * curves_new_nomain(int points_num, int curves_num)
static bool has_anything_selected(const Span< Curves * > curves_ids)
IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
void duplicate_curves(bke::CurvesGeometry &curves, const IndexMask &mask)
void duplicate_points(bke::CurvesGeometry &curves, const IndexMask &mask)
void fill_selection_false(GMutableSpan selection)
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, eCustomDataType create_type, StringRef attribute_name)
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
IndexMask retrieve_editable_and_selected_elements(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, const bke::AttrDomain selection_domain, IndexMaskMemory &memory)
static void GREASE_PENCIL_OT_extrude(wmOperatorType *ot)
static void GREASE_PENCIL_OT_texture_gradient(wmOperatorType *ot)
bool active_grease_pencil_poll(bContext *C)
static int grease_pencil_clean_loose_invoke(bContext *C, wmOperator *op, const wmEvent *event)
IndexMask retrieve_editable_and_selected_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static int grease_pencil_set_material_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_caps_set(wmOperatorType *ot)
static int grease_pencil_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *event)
blender::bke::CurvesGeometry curves_merge_by_distance(const bke::CurvesGeometry &src_curves, const float merge_distance, const IndexMask &selection, const bke::AttributeFilter &attribute_filter)
static bke::greasepencil::Layer & copy_layer(GreasePencil &grease_pencil_dst, bke::greasepencil::LayerGroup &group_dst, const bke::greasepencil::Layer &layer_src)
static void grease_pencil_reproject_ui(bContext *, wmOperator *op)
IndexRange clipboard_paste_strokes(Main &bmain, Object &object, bke::greasepencil::Drawing &drawing, const float4x4 &transform, const bool keep_world_transform, const bool paste_back)
static void GREASE_PENCIL_OT_stroke_simplify(wmOperatorType *ot)
static int grease_pencil_stroke_merge_by_distance_exec(bContext *C, wmOperator *op)
static void join_object_with_active(Main &bmain, Object &ob_src, Object &ob_dst, VectorSet< Material * > &materials)
IndexMask retrieve_editable_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static int grease_pencil_clean_loose_exec(bContext *C, wmOperator *op)
bool ensure_active_keyframe(const Scene &scene, GreasePencil &grease_pencil, bke::greasepencil::Layer &layer, const bool duplicate_previous_key, bool &r_inserted_keyframe)
const bke::CurvesGeometry & clipboard_curves()
static int grease_pencil_texture_gradient_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int grease_pencil_copy_strokes_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_set_curve_type(wmOperatorType *ot)
static void toggle_caps(MutableSpan< int8_t > caps, const IndexMask &strokes)
static void GREASE_PENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
static Array< int > clipboard_materials_remap(Main &bmain, Object &object)
static int grease_pencil_reproject_exec(bContext *C, wmOperator *op)
static bool grease_pencil_paste_strokes_poll(bContext *C)
bool editable_grease_pencil_point_selection_poll(bContext *C)
static bke::CurvesGeometry extrude_grease_pencil_curves(const bke::CurvesGeometry &src, const IndexMask &points_to_extrude)
static int grease_pencil_reset_uvs_exec(bContext *C, wmOperator *)
static void GREASE_PENCIL_OT_delete(wmOperatorType *ot)
static int grease_pencil_snap_to_grid_exec(bContext *C, wmOperator *)
static int grease_pencil_delete_exec(bContext *C, wmOperator *)
static bool grease_pencil_separate_material(bContext &C, Main &bmain, Scene &scene, ViewLayer &view_layer, Base &base_prev, Object &object_src)
static int grease_pencil_snap_cursor_to_sel_exec(bContext *C, wmOperator *)
static void remove_unused_materials(Main *bmain, Object *object)
static void GREASE_PENCIL_OT_stroke_switch_direction(wmOperatorType *ot)
static int grease_pencil_duplicate_exec(bContext *C, wmOperator *)
Vector< DrawingInfo > retrieve_visible_drawings(const Scene &scene, const GreasePencil &grease_pencil, const bool do_onion_skinning)
static void GREASE_PENCIL_OT_set_active_material(wmOperatorType *ot)
static Clipboard & ensure_grease_pencil_clipboard()
static int grease_pencil_stroke_switch_direction_exec(bContext *C, wmOperator *)
static void remap_material_indices(bke::greasepencil::Drawing &drawing, const Span< int > material_index_map)
static int grease_pencil_set_curve_resolution_exec(bContext *C, wmOperator *op)
IndexMask retrieve_editable_strokes_by_material(Object &object, const bke::greasepencil::Drawing &drawing, const int mat_i, IndexMaskMemory &memory)
static int grease_pencil_snap_to_cursor_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem prop_separate_modes[]
static bke::greasepencil::Layer & find_or_create_layer_in_dst_by_name(const int layer_index, const GreasePencil &grease_pencil_src, GreasePencil &grease_pencil_dst)
static int grease_pencil_stroke_smooth_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_copy(wmOperatorType *ot)
static void GREASE_PENCIL_OT_separate(wmOperatorType *ot)
static int64_t stroke_simplify(const IndexRange points, const bool cyclic, const float epsilon, const FunctionRef< float(int64_t, int64_t, int64_t)> dist_function, MutableSpan< bool > points_to_delete)
static bool grease_pencil_separate_layer(bContext &C, Main &bmain, Scene &scene, ViewLayer &view_layer, Base &base_prev, Object &object_src)
static void GREASE_PENCIL_OT_delete_frame(wmOperatorType *ot)
static bke::greasepencil::LayerGroup & copy_layer_group_recursive(GreasePencil &grease_pencil_dst, bke::greasepencil::LayerGroup &parent_dst, const bke::greasepencil::LayerGroup &group_src, Map< StringRefNull, StringRefNull > &layer_name_map)
static void GREASE_PENCIL_OT_set_curve_resolution(wmOperatorType *ot)
static float4x3 expand_4x2_mat(float4x2 strokemat)
static int grease_pencil_delete_frame_exec(bContext *C, wmOperator *op)
bool editable_grease_pencil_poll(bContext *C)
static void remap_vertex_groups(bke::greasepencil::Drawing &drawing, const Map< StringRefNull, StringRefNull > &vertex_group_map)
static int grease_pencil_dissolve_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_set_uniform_opacity(wmOperatorType *ot)
static float dist_to_interpolated(float3 pos, float3 posA, float3 posB, float val, float valA, float valB)
static int grease_pencil_set_uniform_thickness_exec(bContext *C, wmOperator *op)
static int grease_pencil_cyclical_set_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_move_to_layer(wmOperatorType *ot)
static void GREASE_PENCIL_OT_snap_to_grid(wmOperatorType *ot)
static void GREASE_PENCIL_OT_clean_loose(wmOperatorType *ot)
static void GREASE_PENCIL_OT_stroke_smooth(wmOperatorType *ot)
static int grease_pencil_set_active_material_exec(bContext *C, wmOperator *)
IndexMask retrieve_editable_and_selected_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static int grease_pencil_set_curve_type_exec(bContext *C, wmOperator *op)
static int grease_pencil_stroke_material_set_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_set_handle_type(wmOperatorType *ot)
static int grease_pencil_stroke_simplify_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_stroke_reorder(wmOperatorType *ot)
static void GREASE_PENCIL_OT_stroke_merge_by_distance(wmOperatorType *ot)
static int grease_pencil_stroke_reorder_exec(bContext *C, wmOperator *op)
static bool grease_pencil_snap_compute_centroid(const Scene &scene, const Object &object, const GreasePencil &grease_pencil, float3 &r_centroid, float3 &r_min, float3 &r_max)
static const EnumPropertyItem prop_dissolve_types[]
static int grease_pencil_caps_set_exec(bContext *C, wmOperator *op)
Vector< MutableDrawingInfo > retrieve_editable_drawings_from_layer(const Scene &scene, GreasePencil &grease_pencil, const blender::bke::greasepencil::Layer &layer)
static int grease_pencil_texture_gradient_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_stroke_subdivide(wmOperatorType *ot)
static bool grease_pencil_snap_poll(bContext *C)
static int grease_pencil_extrude_exec(bContext *C, wmOperator *)
static Array< int > add_materials_to_map(const GreasePencil &grease_pencil, VectorSet< Material * > &materials)
static int grease_pencil_set_handle_type_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_reproject(wmOperatorType *ot)
static int grease_pencil_move_to_layer_exec(bContext *C, wmOperator *op)
static Map< StringRefNull, StringRefNull > add_vertex_groups(Object &object, GreasePencil &grease_pencil, const ListBase &vertex_group_names)
static bke::CurvesGeometry remove_points_and_split(const bke::CurvesGeometry &curves, const IndexMask &mask)
static void copy_layer_group_content(GreasePencil &grease_pencil_dst, bke::greasepencil::LayerGroup &group_dst, const bke::greasepencil::LayerGroup &group_src, Map< StringRefNull, StringRefNull > &layer_name_map)
static int grease_pencil_separate_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_snap_to_cursor(wmOperatorType *ot)
IndexMask retrieve_editable_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
Vector< MutableDrawingInfo > retrieve_editable_drawings(const Scene &scene, GreasePencil &grease_pencil)
static struct blender::ed::greasepencil::Clipboard * grease_pencil_clipboard
static void GREASE_PENCIL_OT_stroke_material_set(wmOperatorType *ot)
static void GREASE_PENCIL_OT_set_uniform_thickness(wmOperatorType *ot)
static void GREASE_PENCIL_OT_reset_uvs(wmOperatorType *ot)
static void GREASE_PENCIL_OT_duplicate(wmOperatorType *ot)
static const EnumPropertyItem * material_enum_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
static Object * duplicate_grease_pencil_object(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base_prev, const GreasePencil &grease_pencil_src)
static void GREASE_PENCIL_OT_paste(wmOperatorType *ot)
static int gpencil_stroke_subdivide_exec(bContext *C, wmOperator *op)
static bke::GeometrySet join_geometries_with_transform(Span< bke::GeometrySet > geometries, Span< float4x4 > transforms)
static int grease_pencil_texture_gradient_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void GREASE_PENCIL_OT_cyclical_set(wmOperatorType *ot)
Array< Vector< MutableDrawingInfo > > retrieve_editable_drawings_grouped_per_frame(const Scene &scene, GreasePencil &grease_pencil)
static const EnumPropertyItem prop_cyclical_types[]
static void GREASE_PENCIL_OT_dissolve(wmOperatorType *ot)
bool active_grease_pencil_layer_poll(bContext *C)
static bool grease_pencil_separate_selected(bContext &C, Main &bmain, Scene &scene, ViewLayer &view_layer, Base &base_prev, Object &object_src)
static const EnumPropertyItem prop_greasepencil_deleteframe_types[]
static int grease_pencil_paste_strokes_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_set_material(wmOperatorType *ot)
static Array< bool > get_points_to_dissolve(bke::CurvesGeometry &curves, const IndexMask &mask, const DissolveMode mode)
static Array< int > get_reordered_indices(const IndexRange universe, const IndexMask &selected, const ReorderDirection direction)
static int grease_pencil_set_uniform_opacity_exec(bContext *C, wmOperator *op)
Base * add_duplicate(Main *bmain, Scene *scene, ViewLayer *view_layer, Base *base, eDupli_ID_Flags dupflag)
void base_free_and_unlink(Main *bmain, Scene *scene, Object *ob)
bke::CurvesGeometry reorder_curves_geometry(const bke::CurvesGeometry &src_curves, Span< int > old_by_new_map, const bke::AttributeFilter &attribute_filter)
Definition reorder.cc:258
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)
bke::GeometrySet join_geometries(Span< bke::GeometrySet > geometries, const bke::AttributeFilter &attribute_filter, const std::optional< Span< bke::GeometryComponent::Type > > &component_types_to_join=std::nullopt)
bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, const IndexMask &selection, const VArray< int > &cuts, const bke::AttributeFilter &attribute_filter={})
bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves, const IndexMask &selection, CurveType dst_type, const bke::AttributeFilter &attribute_filter, const ConvertCurvesOptions &options={})
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
AngleRadianBase< float > AngleRadian
T length_squared(const VecBase< T, Size > &a)
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
T floor(const T &a)
T distance(const T &a, const T &b)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
CartesianBasis invert(const CartesianBasis &basis)
T midpoint(const T &a, const T &b)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
MatT from_scale(const VecBase< typename MatT::base_type, ScaleDim > &scale)
void min_max(const T &value, T &min, T &max)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
T max(const T &a, const T &b)
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
MatT from_rotation(const RotationT &rotation)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void copy_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask, MutableSpan< int > sizes)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:58
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
MatBase< float, 2, 2 > float2x2
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< float, 2 > float2
MatBase< float, 4, 2 > float4x2
MatBase< float, 3, 2 > float3x2
MatBase< float, 4, 3 > float4x3
VecBase< float, 3 > float3
float wrap(float value, float max, float min)
Definition node_math.h:71
static void unique_name(bNode *node)
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
return ret
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
int RNA_int_get(PointerRNA *ptr, const char *name)
char * RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen, int *r_len)
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
const EnumPropertyItem rna_enum_curves_handle_type_items[]
Definition rna_curves.cc:30
const EnumPropertyItem rna_enum_curves_type_items[]
Definition rna_curves.cc:22
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
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)
void RNA_enum_item_end(EnumPropertyItem **items, int *totitem)
void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item)
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)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
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_dummy_DEFAULT_items[]
Definition rna_rna.cc:35
__int64 int64_t
Definition stdint.h:89
#define INT16_MAX
Definition stdint.h:135
signed char int8_t
Definition stdint.h:75
bAction * action
struct Object * object
ListBase vertex_group_names
CurvesGeometry geometry
char * rna_path
ChannelDriver * driver
GreasePencilLayerTreeNode base
struct Material ** material_array
GreasePencilDrawingBase ** drawing_array
struct AnimData * adt
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
unsigned int session_uid
Definition DNA_ID.h:454
ListBase materials
Definition BKE_main.hh:216
struct AnimData * adt
struct ToolSettings * toolsettings
struct RenderData r
View3DCursor cursor
float * depths
Definition ED_view3d.hh:85
static GeometrySet from_instances(Instances *instances, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
static GeometrySet from_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Vector< std::pair< uint, int > > materials
short val
Definition WM_types.hh:724
short type
Definition WM_types.hh:722
uint is_active
Definition WM_types.hh:605
struct ReportList * reports
struct uiLayout * layout
struct PointerRNA * ptr
VecBase< float, 4 > float4
ParamHandle ** handles
void WM_cursor_wait(bool val)
@ WM_CURSOR_EDIT
Definition wm_cursors.hh:19
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ LEFTMOUSE
wmOperatorType * ot
Definition wm_files.cc:4125
int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_straightline_cancel(bContext *C, wmOperator *op)
int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *event)
void WM_operator_properties_gesture_straightline(wmOperatorType *ot, int cursor)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
int WM_operator_props_popup_confirm_ex(bContext *C, wmOperator *op, const wmEvent *, std::optional< std::string > title, std::optional< std::string > confirm_text, const bool cancel_default)