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
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/* -------------------------------------------------------------------- */
83{
84 const Scene *scene = CTX_data_scene(C);
85 Object *object = CTX_data_active_object(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
201/* -------------------------------------------------------------------- */
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);
256 Object *object = CTX_data_active_object(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;
270 const IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
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
364/* -------------------------------------------------------------------- */
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));
440 offset_indices::accumulate_counts_to_offsets(new_curve_offsets);
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,
447 bke::AttrDomain::Curve,
448 bke::AttrDomain::Curve,
449 bke::attribute_filter_from_skip_ref({"cyclic"}),
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,
456 bke::AttrDomain::Point,
457 bke::AttrDomain::Point,
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);
471 Object *object = CTX_data_active_object(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;
481 const IndexMask elements = ed::greasepencil::retrieve_editable_and_selected_elements(
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) {
492 curves = remove_points_and_split(curves, elements);
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
519/* -------------------------------------------------------------------- */
523enum class DissolveMode : int8_t {
525 POINTS = 0,
527 BETWEEN = 1,
529 UNSELECT = 2,
530};
531
533 {int(DissolveMode::POINTS), "POINTS", 0, "Dissolve", "Dissolve selected points"},
534 {int(DissolveMode::BETWEEN),
535 "BETWEEN",
536 0,
537 "Dissolve Between",
538 "Dissolve points between selected points"},
539 {int(DissolveMode::UNSELECT),
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. */
563 BLI_assert(ELEM(mode, DissolveMode::BETWEEN, DissolveMode::UNSELECT));
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);
612 Object *object = CTX_data_active_object(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;
626 const IndexMask points = ed::greasepencil::retrieve_editable_and_selected_points(
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
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
673/* -------------------------------------------------------------------- */
679 ACTIVE_FRAME = 0,
681 ALL_FRAMES = 1,
682};
683
685 {int(DeleteFrameMode::ACTIVE_FRAME),
686 "ACTIVE_FRAME",
687 0,
688 "Active Frame",
689 "Deletes current frame in the active layer"},
690 {int(DeleteFrameMode::ALL_FRAMES),
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);
701 Object *object = CTX_data_active_object(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
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}
755/* -------------------------------------------------------------------- */
760{
761 using namespace blender;
762 Main *bmain = CTX_data_main(C);
763 const Scene *scene = CTX_data_scene(C);
764 Object *object = CTX_data_active_object(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;
790 IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
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",
799 bke::AttrDomain::Curve);
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
822 ot->srna, "material", nullptr, MAX_ID_NAME - 2, "Material", "Name of the material");
824}
827/* -------------------------------------------------------------------- */
831enum class CyclicalMode : int8_t {
833 CLOSE = 0,
835 OPEN = 1,
837 TOGGLE = 2,
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);
850 Object *object = CTX_data_active_object(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;
865 const IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
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) {
873 case CyclicalMode::CLOSE:
874 index_mask::masked_fill(cyclic, true, strokes);
875 break;
876 case CyclicalMode::OPEN:
877 index_mask::masked_fill(cyclic, false, strokes);
878 break;
879 case CyclicalMode::TOGGLE:
880 array_utils::invert_booleans(cyclic, strokes);
881 break;
882 }
883
884 /* Remove the attribute if it is empty. */
885 if (mode != CyclicalMode::CLOSE) {
886 if (array_utils::booleans_mix_calc(curves.cyclic()) == array_utils::BooleanMix::AllFalse) {
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
912
914
916 ot->srna, "type", prop_cyclical_types, int(CyclicalMode::TOGGLE), "Type", "");
917}
918
921/* -------------------------------------------------------------------- */
926{
927 const Scene *scene = CTX_data_scene(C);
928 Object *object = CTX_data_active_object(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;
938 const IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
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}
969/* -------------------------------------------------------------------- */
974{
975 const Scene *scene = CTX_data_scene(C);
976 Object *object = CTX_data_active_object(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;
986 const IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
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
1017
1019 ot->srna, "thickness", 0.1f, 0.0f, 1000.0f, "Thickness", "Thickness", 0.0f, 1000.0f);
1020}
1021
1023/* -------------------------------------------------------------------- */
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;
1039 const IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
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
1070
1071 ot->prop = RNA_def_float(ot->srna, "opacity", 1.0f, 0.0f, 1.0f, "Opacity", "", 0.0f, 1.0f);
1072}
1073
1076/* -------------------------------------------------------------------- */
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;
1090 const IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
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
1123}
1124
1127/* -------------------------------------------------------------------- */
1131enum class CapsMode : int8_t {
1133 FLAT = 0,
1135 START = 1,
1137 END = 2,
1139 ROUND = 3,
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;
1167 const IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
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)) {
1177 attributes.lookup_or_add_for_write_span<int8_t>("start_cap", bke::AttrDomain::Curve);
1178 bke::SpanAttributeWriter<int8_t> end_caps = attributes.lookup_or_add_for_write_span<int8_t>(
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: {
1192 bke::SpanAttributeWriter<int8_t> caps = attributes.lookup_or_add_for_write_span<int8_t>(
1193 "start_cap", bke::AttrDomain::Curve);
1194 toggle_caps(caps.span, strokes);
1195 caps.finish();
1196 break;
1197 }
1198 case CapsMode::END: {
1199 bke::SpanAttributeWriter<int8_t> caps = attributes.lookup_or_add_for_write_span<int8_t>(
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
1240
1242
1243 ot->prop = RNA_def_enum(ot->srna, "type", prop_caps_types, int(CapsMode::ROUND), "Type", "");
1244}
1245
1248/* -------------------------------------------------------------------- */
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
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}
1319/* -------------------------------------------------------------------- */
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) {
1344 curves::duplicate_curves(curves, elements);
1345 }
1346 else if (selection_domain == bke::AttrDomain::Point) {
1347 curves::duplicate_points(curves, elements);
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
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;
1386 const IndexMask editable_strokes = ed::greasepencil::retrieve_editable_strokes(
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
1420
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
1434/* -------------------------------------------------------------------- */
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;
1455 const IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
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
1531 ot->poll = ed::greasepencil::editable_grease_pencil_poll;
1532
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
1540 "only_selected",
1541 true,
1542 "Selected Points",
1543 "Smooth only selected points in the stroke");
1544}
1545
1548/* -------------------------------------------------------------------- */
1554 TOP = 0,
1556 UP = 1,
1558 DOWN = 2,
1560 BOTTOM = 3,
1561};
1562
1564 const IndexMask &selected,
1565 const ReorderDirection direction)
1566{
1567 Array<int> indices(universe.size());
1568
1569 if (ELEM(direction, ReorderDirection::UP, ReorderDirection::DOWN)) {
1570 /* Initialize the indices. */
1571 array_utils::fill_index_range<int>(indices);
1572 }
1573
1574 if (ELEM(direction, ReorderDirection::TOP, ReorderDirection::BOTTOM)) {
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;
1647 const IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
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
1661 curves = geometry::reorder_curves_geometry(curves, indices, {});
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
1693
1694 ot->prop = RNA_def_enum(
1695 ot->srna, "direction", prop_reorder_direction, int(ReorderDirection::TOP), "Direction", "");
1696}
1697
1700/* -------------------------------------------------------------------- */
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);
1748 drawing_dst.strokes_for_write() = bke::curves_copy_curve_selection(
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
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
1825/* -------------------------------------------------------------------- */
1829enum class SeparateMode : int8_t {
1830 /* Selected Points/Strokes. */
1831 SELECTED = 0,
1832 /* By Material. */
1833 MATERIAL = 1,
1834 /* By Active Layer. */
1835 LAYER = 2,
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(),
1894 bke::AttrDomain::Layer,
1895 bke::AttrDomain::Layer,
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. */
1938 drawing_dst->strokes_for_write() = bke::curves_copy_point_selection(
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. */
2015 drawing_dst->strokes_for_write() = bke::curves_copy_curve_selection(
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) {
2123 case SeparateMode::SELECTED: {
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 }
2141 case SeparateMode::MATERIAL: {
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
2185
2187
2188 ot->prop = RNA_def_enum(
2189 ot->srna, "mode", prop_separate_modes, int(SeparateMode::SELECTED), "Mode", "");
2190}
2191
2194/* -------------------------------------------------------------------- */
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) {
2260 bke::GSpanAttributeWriter selection_in_target = ed::curves::ensure_selection_attribute(
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 }
2326 if (!ed::curves::has_anything_selected(curves)) {
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. */
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
2411
2413 ot->srna, "paste_back", false, "Paste on Back", "Add pasted strokes behind all strokes");
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
2432}
2433
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
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 ?
2505 IndexRange(drawing.strokes().curves_num(),
2507
2508 /* Append the geometry from the clipboard to the target layer. */
2509 Curves *clipboard_id = bke::curves_new_nomain(clipboard_curves);
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 :
2519 float4x4::identity());
2520 const Array<float4x4> transforms = paste_back ?
2521 Span<float4x4>{clipboard_transform,
2522 float4x4::identity()} :
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 ?
2563 ed::greasepencil::retrieve_editable_points(
2564 *object, drawing, info.layer_index, memory) :
2565 ed::greasepencil::retrieve_editable_and_selected_points(
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
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
2609/* -------------------------------------------------------------------- */
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);
2622 array_utils::fill_index_range(dst_to_src_points.as_mutable_span());
2623
2624 Vector<int> dst_to_src_curves(old_curves_num);
2625 array_utils::fill_index_range(dst_to_src_curves.as_mutable_span());
2626
2627 Vector<bool> dst_selected(old_points_num, false);
2628
2629 Vector<int> dst_curve_counts(old_curves_num);
2630 offset_indices::copy_group_sizes(
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));
2682 offset_indices::accumulate_counts_to_offsets(new_curve_offsets);
2683
2684 /* Attributes. */
2685 const bke::AttributeAccessor src_attributes = src.attributes();
2687
2688 /* Selection attribute. */
2689 bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
2690 dst, bke::AttrDomain::Point, CD_PROP_BOOL);
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,
2702 bke::AttrDomain::Curve,
2703 bke::AttrDomain::Curve,
2704 bke::attribute_filter_from_skip_ref({"cyclic"}),
2705 dst_to_src_curves,
2706 dst_attributes);
2707
2708 bke::gather_attributes(src_attributes,
2709 bke::AttrDomain::Point,
2710 bke::AttrDomain::Point,
2711 bke::attribute_filter_from_skip_ref({".selection"}),
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
2760}
2761
2764/* -------------------------------------------------------------------- */
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) {
2785 ED_view3d_depth_override(depsgraph, region, v3d, nullptr, V3D_DEPTH_NO_GPENCIL, &view_depths);
2786 }
2787
2789 scene.toolsettings);
2790
2791 const int oldframe = int(DEG_get_ctime(depsgraph));
2792 if (keep_original) {
2793 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
2794 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
2795 IndexMaskMemory memory;
2797 *object, info.drawing, info.layer_index, selection_domain, memory);
2798 if (elements.is_empty()) {
2799 return;
2800 }
2801
2803 if (selection_domain == bke::AttrDomain::Curve) {
2804 curves::duplicate_curves(curves, elements);
2805 }
2806 else if (selection_domain == bke::AttrDomain::Point) {
2807 curves::duplicate_points(curves, elements);
2808 }
2810 });
2811 }
2812
2813 /* TODO: This can probably be optimized further for the non-Surface projection use case by
2814 * considering all drawings for the parallel loop instead of having to partition by frame number.
2815 */
2816 std::atomic<bool> changed = false;
2817 Array<Vector<MutableDrawingInfo>> drawings_per_frame =
2819 for (const Span<MutableDrawingInfo> drawings : drawings_per_frame) {
2820 if (drawings.is_empty()) {
2821 continue;
2822 }
2823 const int current_frame_number = drawings.first().frame_number;
2824
2825 if (mode == ReprojectMode::Surface) {
2826 scene.r.cfra = current_frame_number;
2828 }
2829
2830 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
2831 IndexMaskMemory memory;
2832 const IndexMask points_to_reproject = retrieve_editable_and_selected_points(
2833 *object, info.drawing, info.layer_index, memory);
2834 if (points_to_reproject.is_empty()) {
2835 return;
2836 }
2837
2838 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
2840 const DrawingPlacement drawing_placement(
2841 scene, *region, *v3d, *object, &layer, mode, offset, view_depths);
2842
2843 MutableSpan<float3> positions = curves.positions_for_write();
2844 points_to_reproject.foreach_index(GrainSize(4096), [&](const int point_i) {
2845 positions[point_i] = drawing_placement.reproject(positions[point_i]);
2846 });
2848
2849 changed.store(true, std::memory_order_relaxed);
2850 });
2851 }
2852
2853 if (mode == ReprojectMode::Surface) {
2854 scene.r.cfra = oldframe;
2856 }
2857
2858 if (changed) {
2859 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
2860 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
2861 }
2862
2863 return OPERATOR_FINISHED;
2864}
2865
2867{
2868 uiLayout *layout = op->layout;
2869 uiLayout *row;
2870
2871 const ReprojectMode type = ReprojectMode(RNA_enum_get(op->ptr, "type"));
2872
2873 uiLayoutSetPropSep(layout, true);
2874 uiLayoutSetPropDecorate(layout, false);
2875 row = uiLayoutRow(layout, true);
2876 uiItemR(row, op->ptr, "type", UI_ITEM_NONE, nullptr, ICON_NONE);
2877
2878 if (type == ReprojectMode::Surface) {
2879 row = uiLayoutRow(layout, true);
2880 uiItemR(row, op->ptr, "offset", UI_ITEM_NONE, nullptr, ICON_NONE);
2881 }
2882 row = uiLayoutRow(layout, true);
2883 uiItemR(row, op->ptr, "keep_original", UI_ITEM_NONE, nullptr, ICON_NONE);
2884}
2885
2887{
2888 static const EnumPropertyItem reproject_type[] = {
2889 {int(ReprojectMode::Front),
2890 "FRONT",
2891 0,
2892 "Front",
2893 "Reproject the strokes using the X-Z plane"},
2894 {int(ReprojectMode::Side), "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
2895 {int(ReprojectMode::Top), "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
2896 {int(ReprojectMode::View),
2897 "VIEW",
2898 0,
2899 "View",
2900 "Reproject the strokes to end up on the same plane, as if drawn from the current "
2901 "viewpoint "
2902 "using 'Cursor' Stroke Placement"},
2903 {int(ReprojectMode::Surface),
2904 "SURFACE",
2905 0,
2906 "Surface",
2907 "Reproject the strokes on to the scene geometry, as if drawn using 'Surface' placement"},
2908 {int(ReprojectMode::Cursor),
2909 "CURSOR",
2910 0,
2911 "Cursor",
2912 "Reproject the strokes using the orientation of 3D cursor"},
2913 {0, nullptr, 0, nullptr, nullptr},
2914 };
2915
2916 /* identifiers */
2917 ot->name = "Reproject Strokes";
2918 ot->idname = "GREASE_PENCIL_OT_reproject";
2919 ot->description =
2920 "Reproject the selected strokes from the current viewpoint as if they had been newly "
2921 "drawn "
2922 "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, "
2923 "or for matching deforming geometry)";
2924
2925 /* callbacks */
2930
2931 /* flags */
2933
2934 /* properties */
2935 ot->prop = RNA_def_enum(
2936 ot->srna, "type", reproject_type, int(ReprojectMode::View), "Projection Type", "");
2937
2939 ot->srna,
2940 "keep_original",
2941 false,
2942 "Keep Original",
2943 "Keep original strokes and create a copy before reprojecting");
2945
2946 RNA_def_float(ot->srna, "offset", 0.0f, 0.0f, 10.0f, "Surface Offset", "", 0.0f, 10.0f);
2947}
2948
2950/* -------------------------------------------------------------------- */
2954/* Poll callback for snap operators */
2955/* NOTE: For now, we only allow these in the 3D view, as other editors do not
2956 * define a cursor or grid-step which can be used.
2957 */
2959{
2961 return false;
2962 }
2963
2964 ScrArea *area = CTX_wm_area(C);
2965 return (area != nullptr) && (area->spacetype == SPACE_VIEW3D);
2966}
2967
2969{
2971
2972 const Scene &scene = *CTX_data_scene(C);
2973 Object &object = *CTX_data_active_object(C);
2974 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
2975 const View3D &v3d = *CTX_wm_view3d(C);
2976 const ARegion &region = *CTX_wm_region(C);
2977 const float grid_size = ED_view3d_grid_view_scale(&scene, &v3d, &region, nullptr);
2978
2979 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
2980 for (const MutableDrawingInfo &drawing_info : drawings) {
2981 bke::CurvesGeometry &curves = drawing_info.drawing.strokes_for_write();
2982 if (curves.curves_num() == 0) {
2983 continue;
2984 }
2985 if (!ed::curves::has_anything_selected(curves)) {
2986 continue;
2987 }
2988
2989 IndexMaskMemory memory;
2990 const IndexMask selected_points = ed::curves::retrieve_selected_points(curves, memory);
2991
2992 const Layer &layer = grease_pencil.layer(drawing_info.layer_index);
2993 const float4x4 layer_to_world = layer.to_world_space(object);
2994 const float4x4 world_to_layer = math::invert(layer_to_world);
2995
2996 MutableSpan<float3> positions = curves.positions_for_write();
2997 selected_points.foreach_index(GrainSize(4096), [&](const int point_i) {
2998 const float3 pos_world = math::transform_point(layer_to_world, positions[point_i]);
2999 const float3 pos_snapped = grid_size * math::floor(pos_world / grid_size + 0.5f);
3000 positions[point_i] = math::transform_point(world_to_layer, pos_snapped);
3001 });
3002
3003 drawing_info.drawing.tag_positions_changed();
3007 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, &grease_pencil);
3008 }
3009
3010 return OPERATOR_FINISHED;
3011}
3012
3014{
3015 ot->name = "Snap Selection to Grid";
3016 ot->idname = "GREASE_PENCIL_OT_snap_to_grid";
3017 ot->description = "Snap selected points to the nearest grid points";
3018
3021
3023}
3024
3027/* -------------------------------------------------------------------- */
3032{
3034
3035 const Scene &scene = *CTX_data_scene(C);
3036 Object &object = *CTX_data_active_object(C);
3037 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
3038 const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
3039 const float3 cursor_world = scene.cursor.location;
3040
3041 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(scene, grease_pencil);
3042 for (const MutableDrawingInfo &drawing_info : drawings) {
3043 bke::CurvesGeometry &curves = drawing_info.drawing.strokes_for_write();
3044 if (curves.curves_num() == 0) {
3045 continue;
3046 }
3047 if (!ed::curves::has_anything_selected(curves)) {
3048 continue;
3049 }
3050
3051 IndexMaskMemory selected_points_memory;
3052 const IndexMask selected_points = ed::curves::retrieve_selected_points(curves,
3053 selected_points_memory);
3054
3055 const Layer &layer = grease_pencil.layer(drawing_info.layer_index);
3056 const float4x4 layer_to_world = layer.to_world_space(object);
3057 const float4x4 world_to_layer = math::invert(layer_to_world);
3058 const float3 cursor_layer = math::transform_point(world_to_layer, cursor_world);
3059
3060 MutableSpan<float3> positions = curves.positions_for_write();
3061 if (use_offset) {
3062 const OffsetIndices points_by_curve = curves.points_by_curve();
3063 IndexMaskMemory selected_curves_memory;
3064 const IndexMask selected_curves = ed::curves::retrieve_selected_curves(
3065 curves, selected_curves_memory);
3066
3067 selected_curves.foreach_index(GrainSize(512), [&](const int curve_i) {
3068 const IndexRange points = points_by_curve[curve_i];
3069
3070 /* Offset from first point of the curve. */
3071 const float3 offset = cursor_layer - positions[points.first()];
3072 selected_points.slice_content(points).foreach_index(
3073 GrainSize(4096), [&](const int point_i) { positions[point_i] += offset; });
3074 });
3075 }
3076 else {
3077 /* Set all selected positions to the cursor location. */
3078 index_mask::masked_fill(positions, cursor_layer, selected_points);
3079 }
3080
3081 drawing_info.drawing.tag_positions_changed();
3085 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, &grease_pencil);
3086 }
3087
3088 return OPERATOR_FINISHED;
3089}
3090
3092{
3093 /* identifiers */
3094 ot->name = "Snap Selection to Cursor";
3095 ot->idname = "GREASE_PENCIL_OT_snap_to_cursor";
3096 ot->description = "Snap selected points/strokes to the cursor";
3097
3098 /* callbacks */
3101
3102 /* flags */
3104
3105 /* props */
3107 "use_offset",
3108 true,
3109 "With Offset",
3110 "Offset the entire stroke instead of selected points only");
3111}
3112
3115/* -------------------------------------------------------------------- */
3120 const Object &object,
3121 const GreasePencil &grease_pencil,
3122 float3 &r_centroid,
3123 float3 &r_min,
3124 float3 &r_max)
3125{
3127
3128 int num_selected = 0;
3129 r_centroid = float3(0.0f);
3130 r_min = float3(std::numeric_limits<float>::max());
3131 r_max = float3(std::numeric_limits<float>::lowest());
3132
3133 const Vector<DrawingInfo> drawings = retrieve_visible_drawings(scene, grease_pencil, false);
3134 for (const DrawingInfo &drawing_info : drawings) {
3135 const Layer &layer = grease_pencil.layer(drawing_info.layer_index);
3136 if (layer.is_locked()) {
3137 continue;
3138 }
3139 const bke::CurvesGeometry &curves = drawing_info.drawing.strokes();
3140 if (curves.curves_num() == 0) {
3141 continue;
3142 }
3143 if (!ed::curves::has_anything_selected(curves)) {
3144 continue;
3145 }
3146
3147 IndexMaskMemory selected_points_memory;
3148 const IndexMask selected_points = ed::curves::retrieve_selected_points(curves,
3149 selected_points_memory);
3150 const float4x4 layer_to_world = layer.to_world_space(object);
3151
3152 Span<float3> positions = curves.positions();
3153 selected_points.foreach_index(GrainSize(4096), [&](const int point_i) {
3154 const float3 pos_world = math::transform_point(layer_to_world, positions[point_i]);
3155 r_centroid += pos_world;
3156 math::min_max(pos_world, r_min, r_max);
3157 });
3158 num_selected += selected_points.size();
3159 }
3160 if (num_selected == 0) {
3161 r_min = r_max = float3(0.0f);
3162 return false;
3163 }
3164
3165 r_centroid /= num_selected;
3166 return true;
3167}
3168
3170{
3171 Scene &scene = *CTX_data_scene(C);
3172 const Object &object = *CTX_data_active_object(C);
3173 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
3174 float3 &cursor = reinterpret_cast<float3 &>(scene.cursor.location);
3175
3176 float3 centroid, points_min, points_max;
3178 scene, object, grease_pencil, centroid, points_min, points_max))
3179 {
3180 return OPERATOR_FINISHED;
3181 }
3182
3183 switch (scene.toolsettings->transform_pivot_point) {
3185 cursor = math::midpoint(points_min, points_max);
3186 break;
3188 case V3D_AROUND_CURSOR:
3190 case V3D_AROUND_ACTIVE:
3191 cursor = centroid;
3192 break;
3193 default:
3195 }
3196
3199
3200 return OPERATOR_FINISHED;
3201}
3202
3204{
3205 /* identifiers */
3206 ot->name = "Snap Cursor to Selected Points";
3207 ot->idname = "GREASE_PENCIL_OT_snap_cursor_to_selected";
3208 ot->description = "Snap cursor to center of selected points";
3209
3210 /* callbacks */
3213
3214 /* flags */
3216}
3217
3219{
3220 float4x3 strokemat4x3 = float4x3(strokemat);
3221
3222 /*
3223 * We need the diagonal of ones to start from the bottom right instead top left to properly
3224 * apply the two matrices.
3225 *
3226 * i.e.
3227 * # # # # # # # #
3228 * We need # # # # Instead of # # # #
3229 * 0 0 0 1 0 0 1 0
3230 *
3231 */
3232 strokemat4x3[2][2] = 0.0f;
3233 strokemat4x3[3][2] = 1.0f;
3234
3235 return strokemat4x3;
3236}
3237
3239{
3240 const Scene *scene = CTX_data_scene(C);
3241 Object *object = CTX_data_active_object(C);
3242 ARegion *region = CTX_wm_region(C);
3243 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3244
3245 std::atomic<bool> changed = false;
3246 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
3247 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
3248 IndexMaskMemory memory;
3249 const IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
3250 *object, info.drawing, info.layer_index, memory);
3251 if (strokes.is_empty()) {
3252 return;
3253 }
3254
3255 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
3256 const float4x4 layer_space_to_world_space = layer.to_world_space(*object);
3257
3258 /* Calculate screen space points. */
3259 const float2 screen_start(RNA_int_get(op->ptr, "xstart"), RNA_int_get(op->ptr, "ystart"));
3260 const float2 screen_end(RNA_int_get(op->ptr, "xend"), RNA_int_get(op->ptr, "yend"));
3261 const float2 screen_direction = screen_end - screen_start;
3262 const float2 screen_tangent = screen_start + float2(-screen_direction[1], screen_direction[0]);
3263
3264 const bke::CurvesGeometry &curves = info.drawing.strokes();
3265 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
3266 const Span<float3> positions = curves.positions();
3267 const Span<float3> normals = info.drawing.curve_plane_normals();
3268 const VArray<int> materials = *curves.attributes().lookup_or_default<int>(
3269 "material_index", bke::AttrDomain::Curve, 0);
3270
3271 Array<float4x2> texture_matrices(strokes.size());
3272
3273 strokes.foreach_index([&](const int curve_i, const int pos) {
3274 const int material_index = materials[curve_i];
3275
3276 const MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(object,
3277 material_index + 1);
3278 const bool is_radial = gp_style->gradient_type == GP_MATERIAL_GRADIENT_RADIAL;
3279
3280 const float texture_angle = gp_style->texture_angle;
3281 const float2 texture_scale = float2(gp_style->texture_scale);
3282 const float2 texture_offset = float2(gp_style->texture_offset);
3283
3284 const float2x2 texture_rotation = math::from_rotation<float2x2>(
3285 math::AngleRadian(texture_angle));
3286
3287 const float3 point = math::transform_point(layer_space_to_world_space,
3288 positions[points_by_curve[curve_i].first()]);
3289 const float3 normal = math::transform_direction(layer_space_to_world_space,
3290 normals[curve_i]);
3291
3292 const float4 plane = float4(normal, -math::dot(normal, point));
3293
3294 float3 start;
3295 float3 tangent;
3296 float3 end;
3297 ED_view3d_win_to_3d_on_plane(region, plane, screen_start, false, start);
3298 ED_view3d_win_to_3d_on_plane(region, plane, screen_tangent, false, tangent);
3299 ED_view3d_win_to_3d_on_plane(region, plane, screen_end, false, end);
3300
3301 const float3 origin = start;
3302 /* Invert the length by dividing by the length squared. */
3303 const float3 u_dir = (end - origin) / math::length_squared(end - origin);
3304 float3 v_dir = math::cross(u_dir, normal);
3305
3306 /* Flip the texture if need so that it is not mirrored. */
3307 if (math::dot(tangent - start, v_dir) < 0.0f) {
3308 v_dir = -v_dir;
3309 }
3310
3311 /* Calculate the texture space before the texture offset transformation. */
3312 const float4x2 base_texture_space = math::transpose(float2x4(
3313 float4(u_dir, -math::dot(u_dir, origin)), float4(v_dir, -math::dot(v_dir, origin))));
3314
3315 float3x2 offset_matrix = float3x2::identity();
3316
3317 if (is_radial) {
3318 /* Radial gradients are scaled down by a factor of 2 and have the center at 0.5 */
3319 offset_matrix *= 0.5f;
3320 offset_matrix[2] += float2(0.5f, 0.5f);
3321 }
3322
3323 /* For some reason 0.5 is added to the offset before being rendered, so remove it here. */
3324 offset_matrix[2] -= float2(0.5f, 0.5f);
3325
3326 offset_matrix = math::from_scale<float2x2>(texture_scale) * offset_matrix;
3327 offset_matrix = texture_rotation * offset_matrix;
3328 offset_matrix[2] -= texture_offset;
3329
3330 texture_matrices[pos] = (offset_matrix * expand_4x2_mat(base_texture_space)) *
3331 layer_space_to_world_space;
3332 });
3333
3334 info.drawing.set_texture_matrices(texture_matrices, strokes);
3335
3336 changed.store(true, std::memory_order_relaxed);
3337 });
3338
3339 if (changed) {
3340 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3341 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3342 }
3343
3345}
3346
3348{
3349 int ret = WM_gesture_straightline_modal(C, op, event);
3350
3351 /* Check for mouse release. */
3352 if ((ret & OPERATOR_RUNNING_MODAL) != 0 && event->type == LEFTMOUSE && event->val == KM_RELEASE)
3353 {
3355 ret &= ~OPERATOR_RUNNING_MODAL;
3357 }
3358
3359 return ret;
3360}
3361
3363{
3364 /* Invoke interactive line drawing (representing the gradient) in viewport. */
3365 const int ret = WM_gesture_straightline_invoke(C, op, event);
3366
3367 if ((ret & OPERATOR_RUNNING_MODAL) != 0) {
3368 ARegion *region = CTX_wm_region(C);
3369 if (region->regiontype == RGN_TYPE_WINDOW && event->type == LEFTMOUSE &&
3370 event->val == KM_PRESS)
3371 {
3372 wmGesture *gesture = static_cast<wmGesture *>(op->customdata);
3373 gesture->is_active = true;
3374 }
3375 }
3376
3377 return ret;
3378}
3379
3381{
3382 /* Identifiers. */
3383 ot->name = "Texture Gradient";
3384 ot->idname = "GREASE_PENCIL_OT_texture_gradient";
3385 ot->description = "Draw a line to set the fill material gradient for the selected strokes";
3386
3387 /* Api callbacks. */
3393
3394 /* Flags. */
3396
3398}
3399
3402/* -------------------------------------------------------------------- */
3407{
3408 const Scene *scene = CTX_data_scene(C);
3409 Object *object = CTX_data_active_object(C);
3410 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3411
3412 const CurveType dst_type = CurveType(RNA_enum_get(op->ptr, "type"));
3413 const bool use_handles = RNA_boolean_get(op->ptr, "use_handles");
3414
3415 bool changed = false;
3416 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
3417 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
3419 IndexMaskMemory memory;
3420 const IndexMask strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
3421 *object, info.drawing, info.layer_index, memory);
3422 if (strokes.is_empty()) {
3423 return;
3424 }
3425
3427 options.convert_bezier_handles_to_poly_points = use_handles;
3428 options.convert_bezier_handles_to_catmull_rom_points = use_handles;
3429 options.keep_bezier_shape_as_nurbs = use_handles;
3430 options.keep_catmull_rom_shape_as_nurbs = use_handles;
3431
3432 curves = geometry::convert_curves(curves, strokes, dst_type, {}, options);
3434
3435 changed = true;
3436 });
3437
3438 if (changed) {
3439 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3440 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3441 }
3442
3443 return OPERATOR_FINISHED;
3444}
3445
3447{
3448 ot->name = "Set Curve Type";
3449 ot->idname = "GREASE_PENCIL_OT_set_curve_type";
3450 ot->description = "Set type of selected curves";
3451
3455
3457
3458 ot->prop = RNA_def_enum(
3459 ot->srna, "type", rna_enum_curves_type_items, CURVE_TYPE_POLY, "Type", "Curve type");
3460
3462 "use_handles",
3463 false,
3464 "Handles",
3465 "Take handle information into account in the conversion");
3466}
3467
3470/* -------------------------------------------------------------------- */
3475{
3476 const Scene *scene = CTX_data_scene(C);
3477 Object *object = CTX_data_active_object(C);
3478 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3479
3480 const HandleType dst_handle_type = HandleType(RNA_enum_get(op->ptr, "type"));
3481
3482 bool changed = false;
3483 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
3484 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
3486 if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
3487 return;
3488 }
3489 IndexMaskMemory memory;
3490 const IndexMask editable_strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
3491 *object, info.drawing, info.layer_index, memory);
3492 const IndexMask bezier_curves = curves.indices_for_curve_type(
3493 CURVE_TYPE_BEZIER, editable_strokes, memory);
3494
3495 const bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
3496 const VArraySpan<bool> selection = *attributes.lookup_or_default<bool>(
3497 ".selection", bke::AttrDomain::Point, true);
3498 const VArraySpan<bool> selection_left = *attributes.lookup_or_default<bool>(
3499 ".selection_handle_left", bke::AttrDomain::Point, true);
3500 const VArraySpan<bool> selection_right = *attributes.lookup_or_default<bool>(
3501 ".selection_handle_right", bke::AttrDomain::Point, true);
3502
3503 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
3504 MutableSpan<int8_t> handle_types_left = curves.handle_types_left_for_write();
3505 MutableSpan<int8_t> handle_types_right = curves.handle_types_right_for_write();
3506 bezier_curves.foreach_index(GrainSize(256), [&](const int curve_i) {
3507 const IndexRange points = points_by_curve[curve_i];
3508 for (const int point_i : points) {
3509 if (selection_left[point_i] || selection[point_i]) {
3510 handle_types_left[point_i] = int8_t(dst_handle_type);
3511 }
3512 if (selection_right[point_i] || selection[point_i]) {
3513 handle_types_right[point_i] = int8_t(dst_handle_type);
3514 }
3515 }
3516 });
3517
3518 curves.calculate_bezier_auto_handles();
3519 curves.tag_topology_changed();
3521
3522 changed = true;
3523 });
3524
3525 if (changed) {
3526 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3527 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3528 }
3529
3530 return OPERATOR_FINISHED;
3531}
3532
3534{
3535 ot->name = "Set Handle Type";
3536 ot->idname = "GREASE_PENCIL_OT_set_handle_type";
3537 ot->description = "Set the handle type for bezier curves";
3538
3542
3544
3545 ot->prop = RNA_def_enum(
3546 ot->srna, "type", rna_enum_curves_handle_type_items, CURVE_TYPE_POLY, "Type", nullptr);
3547}
3548
3551/* -------------------------------------------------------------------- */
3556{
3557 const Scene *scene = CTX_data_scene(C);
3558 Object *object = CTX_data_active_object(C);
3559 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3560
3561 const int resolution = RNA_int_get(op->ptr, "resolution");
3562
3563 bool changed = false;
3564 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
3565 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
3567 IndexMaskMemory memory;
3568 const IndexMask editable_strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
3569 *object, info.drawing, info.layer_index, memory);
3570 if (editable_strokes.is_empty()) {
3571 return;
3572 }
3573
3574 if (curves.is_single_type(CURVE_TYPE_POLY)) {
3575 return;
3576 }
3577
3578 index_mask::masked_fill(curves.resolution_for_write(), resolution, editable_strokes);
3580 changed = true;
3581 });
3582
3583 if (changed) {
3584 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3585 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3586 }
3587
3588 return OPERATOR_FINISHED;
3589}
3590
3592{
3593 ot->name = "Set Curve Resolution";
3594 ot->idname = "GREASE_PENCIL_OT_set_curve_resolution";
3595 ot->description = "Set resolution of selected curves";
3596
3599
3601
3603 "resolution",
3604 12,
3605 0,
3606 10000,
3607 "Resolution",
3608 "The resolution to use for each curve segment",
3609 1,
3610 64);
3611}
3612
3615/* -------------------------------------------------------------------- */
3620{
3621 const Scene *scene = CTX_data_scene(C);
3622 Object *object = CTX_data_active_object(C);
3623 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3624
3625 bool changed = false;
3626 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
3627 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
3629 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
3630 IndexMaskMemory memory;
3631 const IndexMask editable_strokes = ed::greasepencil::retrieve_editable_and_selected_strokes(
3632 *object, info.drawing, info.layer_index, memory);
3633 if (editable_strokes.is_empty()) {
3634 return;
3635 }
3636
3637 if (attributes.contains("uv_rotation")) {
3638 if (editable_strokes.size() == curves.curves_num()) {
3639 attributes.remove("uv_rotation");
3640 }
3641 else {
3642 bke::SpanAttributeWriter<float> uv_rotations = attributes.lookup_for_write_span<float>(
3643 "uv_rotation");
3644 index_mask::masked_fill(uv_rotations.span, 0.0f, editable_strokes);
3645 uv_rotations.finish();
3646 }
3647 }
3648
3649 if (attributes.contains("uv_translation")) {
3650 if (editable_strokes.size() == curves.curves_num()) {
3651 attributes.remove("uv_translation");
3652 }
3653 else {
3654 bke::SpanAttributeWriter<float2> uv_translations =
3655 attributes.lookup_for_write_span<float2>("uv_translation");
3656 index_mask::masked_fill(uv_translations.span, float2(0.0f, 0.0f), editable_strokes);
3657 uv_translations.finish();
3658 }
3659 }
3660
3661 if (attributes.contains("uv_scale")) {
3662 if (editable_strokes.size() == curves.curves_num()) {
3663 attributes.remove("uv_scale");
3664 }
3665 else {
3666 bke::SpanAttributeWriter<float2> uv_scales = attributes.lookup_for_write_span<float2>(
3667 "uv_scale");
3668 index_mask::masked_fill(uv_scales.span, float2(1.0f, 1.0f), editable_strokes);
3669 uv_scales.finish();
3670 }
3671 }
3672
3673 if (attributes.contains("uv_shear")) {
3674 if (editable_strokes.size() == curves.curves_num()) {
3675 attributes.remove("uv_shear");
3676 }
3677 else {
3678 bke::SpanAttributeWriter<float> uv_shears = attributes.lookup_for_write_span<float>(
3679 "uv_shear");
3680 index_mask::masked_fill(uv_shears.span, 0.0f, editable_strokes);
3681 uv_shears.finish();
3682 }
3683 }
3684
3686 changed = true;
3687 });
3688
3689 if (changed) {
3690 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3691 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3692 }
3693
3694 return OPERATOR_FINISHED;
3695}
3696
3698{
3699 /* Identifiers. */
3700 ot->name = "Reset UVs";
3701 ot->idname = "GREASE_PENCIL_OT_reset_uvs";
3702 ot->description = "Reset UV transformation to default values";
3703
3704 /* Callbacks. */
3707
3709}
3710
3713} // namespace blender::ed::greasepencil
3714
3716{
3717 using namespace blender::ed::greasepencil;
3718 WM_operatortype_append(GREASE_PENCIL_OT_stroke_smooth);
3719 WM_operatortype_append(GREASE_PENCIL_OT_stroke_simplify);
3720 WM_operatortype_append(GREASE_PENCIL_OT_delete);
3721 WM_operatortype_append(GREASE_PENCIL_OT_dissolve);
3722 WM_operatortype_append(GREASE_PENCIL_OT_delete_frame);
3723 WM_operatortype_append(GREASE_PENCIL_OT_stroke_material_set);
3724 WM_operatortype_append(GREASE_PENCIL_OT_cyclical_set);
3725 WM_operatortype_append(GREASE_PENCIL_OT_set_active_material);
3726 WM_operatortype_append(GREASE_PENCIL_OT_stroke_switch_direction);
3727 WM_operatortype_append(GREASE_PENCIL_OT_set_uniform_thickness);
3728 WM_operatortype_append(GREASE_PENCIL_OT_set_uniform_opacity);
3729 WM_operatortype_append(GREASE_PENCIL_OT_caps_set);
3730 WM_operatortype_append(GREASE_PENCIL_OT_duplicate);
3731 WM_operatortype_append(GREASE_PENCIL_OT_set_material);
3732 WM_operatortype_append(GREASE_PENCIL_OT_clean_loose);
3733 WM_operatortype_append(GREASE_PENCIL_OT_separate);
3734 WM_operatortype_append(GREASE_PENCIL_OT_stroke_subdivide);
3735 WM_operatortype_append(GREASE_PENCIL_OT_stroke_reorder);
3736 WM_operatortype_append(GREASE_PENCIL_OT_move_to_layer);
3737 WM_operatortype_append(GREASE_PENCIL_OT_copy);
3738 WM_operatortype_append(GREASE_PENCIL_OT_paste);
3739 WM_operatortype_append(GREASE_PENCIL_OT_stroke_merge_by_distance);
3741 WM_operatortype_append(GREASE_PENCIL_OT_extrude);
3742 WM_operatortype_append(GREASE_PENCIL_OT_reproject);
3743 WM_operatortype_append(GREASE_PENCIL_OT_snap_to_grid);
3744 WM_operatortype_append(GREASE_PENCIL_OT_snap_to_cursor);
3745 WM_operatortype_append(GREASE_PENCIL_OT_snap_cursor_to_selected);
3746 WM_operatortype_append(GREASE_PENCIL_OT_set_curve_type);
3747 WM_operatortype_append(GREASE_PENCIL_OT_set_curve_resolution);
3748 WM_operatortype_append(GREASE_PENCIL_OT_set_handle_type);
3749 WM_operatortype_append(GREASE_PENCIL_OT_reset_uvs);
3750 WM_operatortype_append(GREASE_PENCIL_OT_texture_gradient);
3751}
3752
3753/* -------------------------------------------------------------------- */
3757namespace blender::ed::greasepencil {
3758
3759/* Note: the `duplicate_layer` API would be nicer, but only supports duplicating groups from the
3760 * same datablock. */
3763 const bke::greasepencil::Layer &layer_src)
3764{
3765 using namespace blender::bke::greasepencil;
3766
3767 Layer &layer_dst = grease_pencil_dst.add_layer(group_dst, layer_src.name());
3768 BKE_grease_pencil_copy_layer_parameters(layer_src, layer_dst);
3769
3770 layer_dst.frames_for_write() = layer_src.frames();
3771 layer_dst.tag_frames_map_changed();
3772
3773 return layer_dst;
3774}
3775
3777 GreasePencil &grease_pencil_dst,
3779 const bke::greasepencil::LayerGroup &group_src,
3780 Map<StringRefNull, StringRefNull> &layer_name_map);
3781
3782static void copy_layer_group_content(GreasePencil &grease_pencil_dst,
3784 const bke::greasepencil::LayerGroup &group_src,
3785 Map<StringRefNull, StringRefNull> &layer_name_map)
3786{
3787 using namespace blender::bke::greasepencil;
3788
3789 for (const bke::greasepencil::TreeNode *node : group_src.nodes()) {
3790 if (node->is_group()) {
3791 copy_layer_group_recursive(grease_pencil_dst, group_dst, node->as_group(), layer_name_map);
3792 }
3793 if (node->is_layer()) {
3794 Layer &layer_dst = copy_layer(grease_pencil_dst, group_dst, node->as_layer());
3795 layer_name_map.add_new(node->as_layer().name(), layer_dst.name());
3796 }
3797 }
3798}
3799
3801 GreasePencil &grease_pencil_dst,
3803 const bke::greasepencil::LayerGroup &group_src,
3804 Map<StringRefNull, StringRefNull> &layer_name_map)
3805{
3806 bke::greasepencil::LayerGroup &group_dst = grease_pencil_dst.add_layer_group(
3807 parent_dst, group_src.base.name);
3809
3810 copy_layer_group_content(grease_pencil_dst, group_dst, group_src, layer_name_map);
3811 return group_dst;
3812}
3813
3815 VectorSet<Material *> &materials)
3816{
3817 Array<int> material_index_map(grease_pencil.material_array_num);
3818 for (const int i : material_index_map.index_range()) {
3819 Material *material = grease_pencil.material_array[i];
3820 material_index_map[i] = materials.index_of_or_add(material);
3821 }
3822 return material_index_map;
3823}
3824
3826 const Span<int> material_index_map)
3827{
3828 bke::CurvesGeometry &curves = drawing.strokes_for_write();
3829 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
3830 /* Validate material indices and add missing materials. */
3831 bke::SpanAttributeWriter<int> material_writer = attributes.lookup_or_add_for_write_span<int>(
3832 "material_index", bke::AttrDomain::Curve);
3833 threading::parallel_for(curves.curves_range(), 1024, [&](const IndexRange range) {
3834 for (const int curve_i : range) {
3835 material_writer.span[curve_i] = material_index_map[material_writer.span[curve_i]];
3836 }
3837 });
3838 material_writer.finish();
3839}
3840
3842 GreasePencil &grease_pencil,
3843 const ListBase &vertex_group_names)
3844{
3845 Map<StringRefNull, StringRefNull> vertex_group_map;
3846 LISTBASE_FOREACH (bDeformGroup *, dg, &vertex_group_names) {
3847 bDeformGroup *vgroup = static_cast<bDeformGroup *>(MEM_dupallocN(dg));
3848 BKE_object_defgroup_unique_name(vgroup, &object);
3849 BLI_addtail(&grease_pencil.vertex_group_names, vgroup);
3850 vertex_group_map.add_new(dg->name, vgroup->name);
3851 }
3852 return vertex_group_map;
3853}
3854
3856 const Map<StringRefNull, StringRefNull> &vertex_group_map)
3857{
3859 STRNCPY(dg->name, vertex_group_map.lookup(dg->name).c_str());
3860 }
3861
3862 /* Indices in vertex weights remain valid, they are local to the drawing's vertex groups.
3863 * Only the names of the groups change. */
3864}
3865
3867 Object &ob_src,
3868 Object &ob_dst,
3869 VectorSet<Material *> &materials)
3870{
3871 using namespace blender::bke::greasepencil;
3872
3873 /* Skip if the datablock is already used by the active object. */
3874 if (ob_src.data == ob_dst.data) {
3875 return;
3876 }
3877
3878 BLI_assert(ob_src.type == OB_GREASE_PENCIL);
3879 BLI_assert(ob_dst.type == OB_GREASE_PENCIL);
3880 GreasePencil &grease_pencil_src = *static_cast<GreasePencil *>(ob_src.data);
3881 GreasePencil &grease_pencil_dst = *static_cast<GreasePencil *>(ob_dst.data);
3882 /* Number of existing layers that don't need to be updated. */
3883 const int orig_layers_num = grease_pencil_dst.layers().size();
3884
3886 ob_dst, grease_pencil_dst, grease_pencil_src.vertex_group_names);
3887 const Array<int> material_index_map = add_materials_to_map(grease_pencil_src, materials);
3888
3889 /* Concatenate drawing arrays. Existing drawings in dst keep their position, new drawings are
3890 * mapped to the new index range. */
3891 const int new_drawing_array_num = grease_pencil_dst.drawing_array_num +
3892 grease_pencil_src.drawing_array_num;
3893 GreasePencilDrawingBase **new_drawing_array = static_cast<GreasePencilDrawingBase **>(
3894 MEM_malloc_arrayN(new_drawing_array_num, sizeof(GreasePencilDrawingBase *), __func__));
3895 MutableSpan<GreasePencilDrawingBase *> new_drawings = {new_drawing_array, new_drawing_array_num};
3896 const IndexRange new_drawings_dst = IndexRange::from_begin_size(
3897 0, grease_pencil_dst.drawing_array_num);
3898 const IndexRange new_drawings_src = IndexRange::from_begin_size(
3899 grease_pencil_dst.drawing_array_num, grease_pencil_src.drawing_array_num);
3900
3901 copy_drawing_array(grease_pencil_dst.drawings(), new_drawings.slice(new_drawings_dst));
3902 copy_drawing_array(grease_pencil_src.drawings(), new_drawings.slice(new_drawings_src));
3903
3904 /* Free existing drawings array. */
3905 grease_pencil_dst.resize_drawings(0);
3906 grease_pencil_dst.drawing_array = new_drawing_array;
3907 grease_pencil_dst.drawing_array_num = new_drawing_array_num;
3908
3909 /* Maps original names of source layers to new unique layer names. */
3910 Map<StringRefNull, StringRefNull> layer_name_map;
3911 /* Only copy the content of the root group, not the root node itself. */
3912 copy_layer_group_content(grease_pencil_dst,
3913 grease_pencil_dst.root_group(),
3914 grease_pencil_src.root_group(),
3915 layer_name_map);
3916
3917 /* Copy custom attributes for new layers. */
3918 CustomData_merge_layout(&grease_pencil_src.layers_data,
3919 &grease_pencil_dst.layers_data,
3922 grease_pencil_dst.layers().size());
3923 CustomData_copy_data(&grease_pencil_src.layers_data,
3924 &grease_pencil_dst.layers_data,
3925 0,
3926 orig_layers_num,
3927 grease_pencil_src.layers().size());
3928
3929 /* Fix names, indices and transforms to keep relationships valid. */
3930 for (const int layer_index : grease_pencil_dst.layers().index_range()) {
3931 Layer &layer = *grease_pencil_dst.layers_for_write()[layer_index];
3932 const bool is_orig_layer = (layer_index < orig_layers_num);
3933 const float4x4 old_layer_to_world = (is_orig_layer ? layer.to_world_space(ob_dst) :
3934 layer.to_world_space(ob_src));
3935
3936 /* Update newly added layers. */
3937 if (!is_orig_layer) {
3938 /* Update name references for masks. */
3939 LISTBASE_FOREACH (GreasePencilLayerMask *, dst_mask, &layer.masks) {
3940 const StringRefNull *new_mask_name = layer_name_map.lookup_ptr(dst_mask->layer_name);
3941 if (new_mask_name) {
3942 MEM_SAFE_FREE(dst_mask->layer_name);
3943 dst_mask->layer_name = BLI_strdup(new_mask_name->c_str());
3944 }
3945 }
3946 /* Shift drawing indices to match the new drawings array. */
3947 for (const int key : layer.frames_for_write().keys()) {
3948 int &drawing_index = layer.frames_for_write().lookup(key).drawing_index;
3949 drawing_index = new_drawings_src[drawing_index];
3950 }
3951 }
3952
3953 /* Layer parent object may become invalid. This can be an original layer pointing at the joined
3954 * object which gets destroyed, or a new layer that points at the target object which is now
3955 * its owner. */
3956 if (ELEM(layer.parent, &ob_dst, &ob_src)) {
3957 layer.parent = nullptr;
3958 }
3959
3960 /* Apply relative object transform to new drawings to keep world-space positions unchanged.
3961 * Be careful where the matrix is computed: changing the parent pointer (above) can affect
3962 * this! */
3963 const float4x4 new_layer_to_world = layer.to_world_space(ob_dst);
3964 for (const int key : layer.frames_for_write().keys()) {
3965 const int drawing_index = layer.frames_for_write().lookup(key).drawing_index;
3966 GreasePencilDrawingBase *drawing_base = grease_pencil_dst.drawings()[drawing_index];
3967 if (drawing_base->type != GP_DRAWING) {
3968 continue;
3969 }
3970 Drawing &drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base)->wrap();
3971 bke::CurvesGeometry &curves = drawing.strokes_for_write();
3972 curves.transform(math::invert(new_layer_to_world) * old_layer_to_world);
3973
3974 if (!is_orig_layer) {
3975 remap_vertex_groups(drawing, vertex_group_map);
3976 remap_material_indices(drawing, material_index_map);
3977 }
3978 }
3979 }
3980
3981 /* Rename animation paths to layers. */
3982 BKE_fcurves_main_cb(&bmain, [&](ID *id, FCurve *fcu) {
3983 if (id == &grease_pencil_src.id && fcu->rna_path && strstr(fcu->rna_path, "layers[")) {
3984 /* Have to use linear search, the layer name map only contains sub-strings of RNA paths. */
3985 for (auto [name_src, name_dst] : layer_name_map.items()) {
3986 if (name_dst != name_src) {
3987 const char *old_path = fcu->rna_path;
3988 fcu->rna_path = BKE_animsys_fix_rna_path_rename(
3989 id, fcu->rna_path, "layers", name_src.c_str(), name_dst.c_str(), 0, 0, false);
3990 if (old_path != fcu->rna_path) {
3991 /* Stop after first match. */
3992 break;
3993 }
3994 }
3995 }
3996 }
3997 /* Fix driver targets. */
3998 if (fcu->driver) {
3999 LISTBASE_FOREACH (DriverVar *, dvar, &fcu->driver->variables) {
4000 /* Only change the used targets, since the others will need fixing manually anyway. */
4001 DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) {
4002 if (dtar->id != &grease_pencil_src.id) {
4003 continue;
4004 }
4005 dtar->id = &grease_pencil_dst.id;
4006
4007 if (dtar->rna_path && strstr(dtar->rna_path, "layers[")) {
4008 for (auto [name_src, name_dst] : layer_name_map.items()) {
4009 if (name_dst != name_src) {
4010 const char *old_path = fcu->rna_path;
4011 dtar->rna_path = BKE_animsys_fix_rna_path_rename(
4012 id, dtar->rna_path, "layers", name_src.c_str(), name_dst.c_str(), 0, 0, false);
4013 if (old_path != dtar->rna_path) {
4014 break;
4015 }
4016 }
4017 }
4018 }
4019 }
4020 DRIVER_TARGETS_LOOPER_END;
4021 }
4022 }
4023 });
4024
4025 /* Merge animation data of objects and grease pencil datablocks. */
4026 if (ob_src.adt) {
4027 if (ob_dst.adt == nullptr) {
4028 ob_dst.adt = BKE_animdata_copy(&bmain, ob_src.adt, 0);
4029 }
4030 else {
4031 BKE_animdata_merge_copy(&bmain, &ob_dst.id, &ob_src.id, ADT_MERGECOPY_KEEP_DST, false);
4032 }
4033
4034 if (ob_dst.adt->action) {
4035 DEG_id_tag_update(&ob_dst.adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH);
4036 }
4037 }
4038 if (grease_pencil_src.adt) {
4039 if (grease_pencil_dst.adt == nullptr) {
4040 grease_pencil_dst.adt = BKE_animdata_copy(&bmain, grease_pencil_src.adt, 0);
4041 }
4042 else {
4044 &bmain, &grease_pencil_dst.id, &grease_pencil_src.id, ADT_MERGECOPY_KEEP_DST, false);
4045 }
4046
4047 if (grease_pencil_dst.adt->action) {
4048 DEG_id_tag_update(&grease_pencil_dst.adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH);
4049 }
4050 }
4051}
4052
4053} // namespace blender::ed::greasepencil
4054
4056{
4057 Main *bmain = CTX_data_main(C);
4058 Scene *scene = CTX_data_scene(C);
4059 Object *ob_active = CTX_data_active_object(C);
4060
4061 /* Ensure we're in right mode and that the active object is correct. */
4062 if (!ob_active || ob_active->type != OB_GREASE_PENCIL) {
4063 return OPERATOR_CANCELLED;
4064 }
4065
4066 bool ok = false;
4067 CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
4068 if (ob_iter == ob_active) {
4069 ok = true;
4070 break;
4071 }
4072 }
4074 /* Active object must always selected. */
4075 if (ok == false) {
4076 BKE_report(op->reports, RPT_WARNING, "Active object is not a selected grease pencil");
4077 return OPERATOR_CANCELLED;
4078 }
4079
4080 Object *ob_dst = ob_active;
4081 GreasePencil *grease_pencil_dst = static_cast<GreasePencil *>(ob_dst->data);
4082
4085 *grease_pencil_dst, materials);
4086 /* Reassign material indices in the original layers, in case materials are deduplicated. */
4087 for (GreasePencilDrawingBase *drawing_base : grease_pencil_dst->drawings()) {
4088 if (drawing_base->type != GP_DRAWING) {
4089 continue;
4090 }
4092 reinterpret_cast<GreasePencilDrawing *>(drawing_base)->wrap();
4093 blender::ed::greasepencil::remap_material_indices(drawing, material_index_map);
4094 }
4095
4096 /* Loop and join all data. */
4097 CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
4098 if (ob_iter->type != OB_GREASE_PENCIL || ob_iter == ob_active) {
4099 continue;
4100 }
4101
4102 blender::ed::greasepencil::join_object_with_active(*bmain, *ob_iter, *ob_dst, materials);
4103
4104 /* Free the old object. */
4105 blender::ed::object::base_free_and_unlink(bmain, scene, ob_iter);
4106 }
4108
4109 /* Transfer material pointers. The material indices are updated for each drawing separately. */
4110 if (!materials.is_empty()) {
4111 /* Old C API, needs a const_cast but doesn't actually change anything. */
4112 Material **materials_ptr = const_cast<Material **>(materials.data());
4114 bmain, DEG_get_original_object(ob_dst), &materials_ptr, materials.size(), false);
4115 }
4116
4117 DEG_id_tag_update(&grease_pencil_dst->id, ID_RECALC_GEOMETRY);
4119
4122
4123 return OPERATOR_FINISHED;
4124}
4125
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)
#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
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, ViewDepths **r_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])
#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
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)
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#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
@ KM_PRESS
Definition WM_types.hh:284
@ KM_RELEASE
Definition WM_types.hh:285
#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
#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
unsigned int U
Definition btGjkEpa3.h:78
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
const T & first() const
Definition BLI_array.hh:270
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
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 MutableSpan drop_front(const int64_t n) const
Definition BLI_span.hh:608
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 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
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
void reverse_curves(const IndexMask &curves_to_reverse)
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()
void transform(const float4x4 &matrix)
VArray< bool > cyclic() const
MutableSpan< bool > cyclic_for_write()
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
const Map< FramesMapKeyT, GreasePencilFrame > & frames() const
bool has_drawing_at(const int frame_number) const
float4x4 to_object_space(const Object &object) const
Map< FramesMapKeyT, GreasePencilFrame > & frames_for_write()
IndexMask slice_content(IndexRange range) const
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
CCL_NAMESPACE_BEGIN struct Options options
const Depsgraph * depsgraph
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[]
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)
@ TOP
@ DOWN
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
#define B
Vector< IndexRange > find_all_ranges(const Span< T > span, const T &value)
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)
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)
void base_free_and_unlink(Main *bmain, Scene *scene, Object *ob)
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)
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 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
float wrap(float value, float max, float min)
Definition node_math.h:71
static void unique_name(bNode *node)
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
struct Object * object
ListBase vertex_group_names
CurvesGeometry geometry
const char * identifier
Definition RNA_types.hh:506
char * rna_path
ChannelDriver * driver
GreasePencilLayerTreeNode base
struct Material ** material_array
GreasePencilDrawingBase ** drawing_array
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
ListBase materials
Definition BKE_main.hh:216
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
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
void(* ui)(bContext *C, wmOperator *op)
Definition WM_types.hh:1053
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct uiLayout * layout
struct PointerRNA * ptr
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)