Blender V5.0
grease_pencil_select.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_attribute.hh"
10#include "BKE_context.hh"
11#include "BKE_curves.hh"
12#include "BKE_grease_pencil.hh"
13#include "BKE_object.hh"
14
16#include "BLI_index_mask.hh"
17#include "BLI_offset_indices.hh"
18#include "BLI_task.hh"
19
20#include "DNA_object_types.h"
21
22#include "DEG_depsgraph.hh"
24
25#include "ED_curves.hh"
26#include "ED_grease_pencil.hh"
27#include "ED_screen.hh"
28#include "ED_select_utils.hh"
29#include "ED_view3d.hh"
30
31#include "RNA_access.hh"
32#include "RNA_define.hh"
33#include "RNA_enum_types.hh"
34
35#include "WM_api.hh"
36
38
39/* -------------------------------------------------------------------- */
42
43inline int clamp_range(const IndexRange range, const int index)
44{
45 BLI_assert(!range.is_empty());
46 return std::clamp(index, int(range.first()), int(range.last()));
47}
48
56template<typename Fn>
57static int foreach_curve_segment(const CurveSegmentsData &segment_data,
58 const int curve_index,
59 const IndexRange points,
60 Fn &&fn)
61{
62 if (points.is_empty()) {
63 return 0;
64 }
65
66 const OffsetIndices segments_by_curve = OffsetIndices<int>(segment_data.segment_offsets);
67 const IndexRange segments = segments_by_curve[curve_index];
68
69 for (const int segment_i : segments) {
70 const int segment_point_i = segment_data.segment_start_points[segment_i];
71 const float segment_fraction = segment_data.segment_start_fractions[segment_i];
72
73 if (segment_i < segments.last()) {
74 const int next_segment_i = segment_i + 1;
75 const int next_segment_point_i = segment_data.segment_start_points[next_segment_i];
76 const float next_segment_fraction = segment_data.segment_start_fractions[next_segment_i];
77
78 /* Start point with zero fraction is included. */
79 const int first_point_i = (segment_fraction == 0.0f ?
80 segment_point_i :
81 clamp_range(points, segment_point_i + 1));
82 const int next_first_point_i = (next_segment_fraction == 0.0f ?
83 next_segment_point_i :
84 clamp_range(points, next_segment_point_i + 1));
85 const IndexRange points_range = IndexRange::from_begin_end(first_point_i,
86 next_first_point_i);
87 fn(segment_i, points_range, IndexRange());
88 }
89 else {
90 const int first_segment_point_i = segment_data.segment_start_points[segments.first()];
91 const float first_segment_fraction = segment_data.segment_start_fractions[segments.first()];
92 /* Start point with zero fraction is included. */
93 const int first_point_i = (segment_fraction == 0.0f ?
94 segment_point_i :
95 clamp_range(points, segment_point_i + 1));
96 /* End point with zero fraction is excluded. */
97 const int next_first_point_i = (first_segment_fraction == 0.0f ?
98 first_segment_point_i :
99 clamp_range(points, first_segment_point_i + 1));
100 const IndexRange points_range1 = IndexRange::from_begin_end(points.first(),
101 next_first_point_i);
102 const IndexRange points_range2 = IndexRange::from_begin_end_inclusive(first_point_i,
103 points.last());
104
105 fn(segment_i, points_range1, points_range2);
106 }
107 }
108 return segments.size();
109}
110
112 const IndexMask &selection_mask,
113 const bke::AttrDomain selection_domain,
114 const StringRef attribute_name,
115 const GrainSize grain_size,
116 const eSelectOp sel_op)
117{
118 if (selection_mask.is_empty()) {
119 return false;
120 }
121
123 curves, selection_domain, bke::AttrType::Bool, attribute_name);
124
125 selection_mask.foreach_index(grain_size, [&](const int64_t element_i) {
126 ed::curves::apply_selection_operation_at_index(writer.span, element_i, sel_op);
127 });
128
129 writer.finish();
130
131 return true;
132}
133
135 const IndexMask &point_selection_mask,
136 const StringRef attribute_name,
137 const Curves2DBVHTree &tree_data,
138 const IndexRange tree_data_range,
139 const GrainSize grain_size,
140 const eSelectOp sel_op)
141{
142 /* Use regular selection for anything other than the ".selection" attribute. */
143 if (attribute_name != ".selection") {
145 curves, point_selection_mask, bke::AttrDomain::Point, attribute_name, grain_size, sel_op);
146 }
147
148 if (point_selection_mask.is_empty()) {
149 return false;
150 }
151 IndexMaskMemory memory;
152
153 const IndexMask changed_curve_mask = ed::curves::curve_mask_from_points(
154 curves, point_selection_mask, GrainSize(512), memory);
155
156 const OffsetIndices points_by_curve = curves.points_by_curve();
157 const Span<float2> screen_space_positions = tree_data.start_positions.as_span().slice(
158 tree_data_range);
159
161 curves, changed_curve_mask, screen_space_positions, tree_data, tree_data_range);
162
163 const OffsetIndices<int> segments_by_curve = OffsetIndices<int>(segment_data.segment_offsets);
166
167 /* Find all segments that have changed points and fill them. */
168 Array<bool> changed_points(curves.points_num());
169 point_selection_mask.to_bools(changed_points);
170
171 auto test_points_range = [&](const IndexRange range) -> bool {
172 for (const int point_i : range) {
173 if (changed_points[point_i]) {
174 return true;
175 }
176 }
177 return false;
178 };
179 auto update_points_range = [&](const IndexRange range) {
180 for (const int point_i : range) {
181 ed::curves::apply_selection_operation_at_index(attribute_writer.span, point_i, sel_op);
182 }
183 };
184
186 segments_by_curve.index_range(), grain_size.value, [&](const IndexRange range) {
187 for (const int curve_i : range) {
188 const IndexRange points = points_by_curve[curve_i];
189
190 const int num_segments = foreach_curve_segment(
191 segment_data,
192 curve_i,
193 points,
194 [&](const int /*segment_i*/, const IndexRange points1, const IndexRange points2) {
195 if (test_points_range(points1) || test_points_range(points2)) {
196 update_points_range(points1);
197 update_points_range(points2);
198 }
199 });
200 if (num_segments == 0 && test_points_range(points)) {
201 /* Cyclic curve without cuts, select all. */
202 update_points_range(points);
203 }
204 }
205 });
206
207 attribute_writer.finish();
208 return true;
209}
210
212 const eSelectOp sel_op,
213 SelectionUpdateFunc select_operation)
214{
215 using namespace blender;
216
217 Object *object = (vc->obedit ? vc->obedit : vc->obact);
218 const Object *ob_eval = DEG_get_evaluated(vc->depsgraph, object);
219 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
220
221 /* Get selection domain from tool settings. */
223 vc->scene->toolsettings, object);
224 const bool use_segment_selection = ED_grease_pencil_segment_selection_enabled(
225 vc->scene->toolsettings, object);
226
227 bool changed = false;
230
231 for (const Span<ed::greasepencil::MutableDrawingInfo> drawings : drawings_by_frame) {
232 if (drawings.is_empty()) {
233 continue;
234 }
235 const int frame_number = drawings.first().frame_number;
236
237 /* Construct BVH tree for all drawings on the same frame. */
240 if (use_segment_selection) {
242 *vc, *ob_eval, grease_pencil, drawings, frame_number);
243 }
244 OffsetIndices tree_data_by_drawing = OffsetIndices<int>(tree_data.drawing_offsets);
245
246 for (const int i_drawing : drawings.index_range()) {
247 // TODO optimize by lazy-initializing the tree data ONLY IF the changed_element_mask is not
248 // empty.
249
250 const ed::greasepencil::MutableDrawingInfo &info = drawings[i_drawing];
252 const Span<StringRef> selection_attribute_names =
254
255 IndexMaskMemory memory;
257 *object, info, selection_domain, memory);
258 if (elements.is_empty()) {
259 continue;
260 }
261
262 for (const StringRef attribute_name : selection_attribute_names) {
263 const IndexMask changed_element_mask = select_operation(
264 info, elements, attribute_name, memory);
265
266 /* Modes that un-set all elements not in the mask. */
267 if (ELEM(sel_op, SEL_OP_SET, SEL_OP_AND)) {
268 if (bke::SpanAttributeWriter<bool> selection =
269 curves.attributes_for_write().lookup_or_add_for_write_span<bool>(
270 attribute_name, selection_domain))
271 {
272 const IndexMask not_in_mask = changed_element_mask.complement(
273 selection.span.index_range(), memory);
274 ed::curves::fill_selection_false(selection.span, not_in_mask);
275 changed = true;
276 selection.finish();
277 }
278 }
279
280 if (use_segment_selection) {
281 /* Range of points in tree data matching this curve, for re-using screen space
282 * positions. */
283 const IndexRange tree_data_range = tree_data_by_drawing[i_drawing];
285 changed_element_mask,
286 attribute_name,
287 tree_data,
288 tree_data_range,
289 GrainSize(4096),
290 sel_op);
291 }
292 else {
294 changed_element_mask,
295 selection_domain,
296 attribute_name,
297 GrainSize(4096),
298 sel_op);
299 }
300 }
301 }
302 }
303
304 if (changed) {
305 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
306 * generic attribute for now. */
307 DEG_id_tag_update(static_cast<ID *>(object->data), ID_RECALC_GEOMETRY);
309 }
310
311 return changed;
312}
313
315
317{
318 int action = RNA_enum_get(op->ptr, "action");
319 Scene *scene = CTX_data_scene(C);
321 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
323 object);
324
325 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
326 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
327 IndexMaskMemory memory;
328 const IndexMask selectable_elements = retrieve_editable_elements(
329 *object, info, selection_domain, memory);
330 if (selectable_elements.is_empty()) {
331 return;
332 }
333 if (action == SEL_TOGGLE) {
335 selection_domain) ?
338 }
340 info.drawing.strokes_for_write(), selectable_elements, selection_domain, action);
341 });
342
343 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
344 * attribute for now. */
345 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
346 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
347
348 return OPERATOR_FINISHED;
349}
350
352{
353 ot->name = "(De)select All Strokes";
354 ot->idname = "GREASE_PENCIL_OT_select_all";
355 ot->description = "(De)select all visible strokes";
356
357 ot->exec = select_all_exec;
359
361
363}
364
366{
368 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
370
374 const IndexMask & /*universe*/,
375 StringRef attribute_name,
376 IndexMaskMemory &memory) {
378 info.drawing.strokes(), attribute_name, false, memory);
379 });
380
381 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
382 * attribute for now. */
383 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
384 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
385
386 return OPERATOR_FINISHED;
387}
388
390{
391 ot->name = "Select More";
392 ot->idname = "GREASE_PENCIL_OT_select_more";
393 ot->description = "Grow the selection by one point";
394
395 ot->exec = select_more_exec;
397
399}
400
402{
404 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
406
410 const IndexMask & /*universe*/,
411 StringRef attribute_name,
412 IndexMaskMemory &memory) {
414 info.drawing.strokes(), attribute_name, true, memory);
415 });
416
417 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
418 * attribute for now. */
419 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
420 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
421
422 return OPERATOR_FINISHED;
423}
424
426{
427 ot->name = "Select Less";
428 ot->idname = "GREASE_PENCIL_OT_select_less";
429 ot->description = "Shrink the selection by one point";
430
431 ot->exec = select_less_exec;
433
435}
436
438{
439 Scene *scene = CTX_data_scene(C);
441 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
442
443 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
444 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
445 IndexMaskMemory memory;
447 *object, info.drawing, info.layer_index, memory);
448 if (selectable_strokes.is_empty()) {
449 return;
450 }
452 });
453
454 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
455 * attribute for now. */
456 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
457 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
458
459 return OPERATOR_FINISHED;
460}
461
463{
464 ot->name = "Select Linked";
465 ot->idname = "GREASE_PENCIL_OT_select_linked";
466 ot->description = "Select all points in curves with any point selection";
467
468 ot->exec = select_linked_exec;
470
472}
473
475{
476 using namespace blender;
477 const float ratio = RNA_float_get(op->ptr, "ratio");
479 Scene *scene = CTX_data_scene(C);
481 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
483 object);
485
486 /* Note: For segment selection this doesn't work very well, because it is based on random point
487 * selection. A segment has a high probability of getting at least one selected point and be
488 * itself selected.
489 * For better distribution the random value must be generated per segment and possibly weighted
490 * by segment length.
491 */
493 &vc,
496 const IndexMask & /*universe*/,
497 StringRef /*attribute_name*/,
498 IndexMaskMemory &memory) -> IndexMask {
499 const IndexMask selectable_elements = retrieve_editable_elements(
500 *object, info, selection_domain, memory);
501 if (selectable_elements.is_empty()) {
502 return {};
503 }
504 return random_mask(selectable_elements,
505 info.drawing.strokes().points_num(),
507 ratio,
508 memory);
509 });
510
511 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
512 * attribute for now. */
513 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
514 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
515
516 return OPERATOR_FINISHED;
517}
518
520{
521 ot->name = "Select Random";
522 ot->idname = "GREASE_PENCIL_OT_select_random";
523 ot->description = "Selects random points from the current strokes selection";
524
525 ot->exec = select_random_exec;
527
529
531}
532
534{
535 const bool deselect_ends = RNA_boolean_get(op->ptr, "deselect_ends");
536 Scene *scene = CTX_data_scene(C);
538 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
539
540 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
541 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
543 });
544
545 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
546 * attribute for now. */
547 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
548 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
549
550 return OPERATOR_FINISHED;
551}
552
554{
555 ot->name = "Select Alternate";
556 ot->idname = "GREASE_PENCIL_OT_select_alternate";
557 ot->description = "Select alternated points in strokes with already selected points";
558
561
563
564 RNA_def_boolean(ot->srna,
565 "deselect_ends",
566 false,
567 "Deselect Ends",
568 "(De)select the first and last point of each stroke");
569}
570
578
580 {int(SelectSimilarMode::LAYER), "LAYER", 0, "Layer", ""},
581 {int(SelectSimilarMode::MATERIAL), "MATERIAL", 0, "Material", ""},
582 {int(SelectSimilarMode::VERTEX_COLOR), "VERTEX_COLOR", 0, "Vertex Color", ""},
583 {int(SelectSimilarMode::RADIUS), "RADIUS", 0, "Radius", ""},
584 {int(SelectSimilarMode::OPACITY), "OPACITY", 0, "Opacity", ""},
585 {0, nullptr, 0, nullptr, nullptr},
586};
587
588template<typename T>
590 const MutableDrawingInfo &info,
591 const bke::AttrDomain domain,
592 const StringRef attribute_id,
593 const int handle_display,
594 blender::Set<T> &r_value_set)
595{
596 T default_value;
597 CPPType::get<T>().default_construct(&default_value);
598
600 const bke::AttributeAccessor attributes = curves.attributes();
601 const VArraySpan<T> values = *attributes.lookup_or_default<T>(
602 attribute_id, domain, default_value);
603
605 IndexMaskMemory memory;
606 if (domain == bke::AttrDomain::Point) {
608 *object, info.drawing, info.layer_index, handle_display, memory);
609 points.foreach_index(GrainSize(1024), [&](const int index) {
610 Set<T> &local_value_set = value_set_by_thread.local();
611 local_value_set.add(values[index]);
612 });
613 }
614 else if (domain == bke::AttrDomain::Curve) {
616 *object, info.drawing, info.layer_index, memory);
617
618 strokes.foreach_index(GrainSize(1024), [&](const int index) {
619 Set<T> &local_value_set = value_set_by_thread.local();
620 local_value_set.add(values[index]);
621 });
622 }
623 else {
625 }
626
627 for (const Set<T> &local_value_set : value_set_by_thread) {
628 /* TODO is there a union function that can do this more efficiently? */
629 for (const T &key : local_value_set) {
630 r_value_set.add(key);
631 }
632 }
633}
634
635template<typename T, typename DistanceFn>
637 Object *object,
638 GreasePencil &grease_pencil,
639 const bke::AttrDomain selection_domain,
640 const StringRef attribute_id,
641 const int handle_display,
642 float threshold,
643 DistanceFn distance_fn)
644{
645 using namespace blender::ed::greasepencil;
646
647 T default_value;
648 CPPType::get<T>().default_construct(&default_value);
649
651 grease_pencil);
652
653 blender::Set<T> selected_values;
654 for (const MutableDrawingInfo &info : drawings) {
656 object, info, selection_domain, attribute_id, handle_display, selected_values);
657 }
658
659 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
660 IndexMaskMemory memory;
662 *object, info.drawing, info.layer_index, memory);
664 const VArraySpan<T> values = *curves.attributes().lookup_or_default<T>(
665 attribute_id, selection_domain, default_value);
666
668 curves);
669 for (const int i : selection_attribute_names.index_range()) {
671 curves, selection_domain, bke::AttrType::Bool, selection_attribute_names[i]);
672 MutableSpan<bool> selection = selection_writer.span.typed<bool>();
673
674 mask.foreach_index(GrainSize(1024), [&](const int index) {
675 if (selection[index]) {
676 return;
677 }
678 for (const T &test_value : selected_values) {
679 if (distance_fn(values[index], test_value) <= threshold) {
680 selection[index] = true;
681 }
682 }
683 });
684
685 selection_writer.finish();
686 }
687 });
688}
689
691 Object *object,
692 GreasePencil &grease_pencil,
693 bke::AttrDomain domain)
694{
696 grease_pencil);
697
698 blender::Set<int> selected_layers;
699 /* Layer is selected if any point is selected. */
700 for (const MutableDrawingInfo &info : drawings) {
701 const VArraySpan<bool> selection =
702 *info.drawing.strokes().attributes().lookup_or_default<bool>(".selection", domain, true);
703 for (const int i : selection.index_range()) {
704 if (selection[i]) {
705 selected_layers.add(info.layer_index);
706 break;
707 }
708 }
709 }
710
711 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
712 if (!selected_layers.contains(info.layer_index)) {
713 return;
714 }
715
716 IndexMaskMemory memory;
717 const IndexMask editable_elements = retrieve_editable_elements(*object, info, domain, memory);
718 if (editable_elements.is_empty()) {
719 return;
720 }
722 info.drawing.strokes_for_write(), editable_elements, domain, SEL_SELECT);
723 });
724}
725
727{
728 const SelectSimilarMode mode = SelectSimilarMode(RNA_enum_get(op->ptr, "mode"));
729 const float threshold = RNA_float_get(op->ptr, "threshold");
730 Scene *scene = CTX_data_scene(C);
731 View3D *v3d = CTX_wm_view3d(C);
733 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
735 object);
736 const int handle_display = v3d->overlay.handle_display;
737
738 switch (mode) {
740 select_similar_by_layer(scene, object, grease_pencil, selection_domain);
741 break;
744 scene,
745 object,
746 grease_pencil,
747 selection_domain,
748 "material_index",
749 handle_display,
750 threshold,
751 [](const int a, const int b) -> float { return float(math::distance(a, b)); });
752 break;
755 scene,
756 object,
757 grease_pencil,
758 selection_domain,
759 "vertex_color",
760 handle_display,
761 threshold,
762 [](const ColorGeometry4f &a, const ColorGeometry4f &b) -> float {
763 return math::distance(float4(a), float4(b));
764 });
765 break;
768 scene,
769 object,
770 grease_pencil,
771 selection_domain,
772 "radius",
773 handle_display,
774 threshold,
775 [](const float a, const float b) -> float { return math::distance(a, b); });
776 break;
779 scene,
780 object,
781 grease_pencil,
782 selection_domain,
783 "opacity",
784 handle_display,
785 threshold,
786 [](const float a, const float b) -> float { return math::distance(a, b); });
787 break;
788 }
789
790 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
791 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
792
793 return OPERATOR_FINISHED;
794}
795
797{
798 ot->name = "Select Similar";
799 ot->idname = "GREASE_PENCIL_OT_select_similar";
800 ot->description = "Select all strokes with similar characteristics";
801
802 ot->invoke = WM_menu_invoke;
803 ot->exec = select_similar_exec;
805
807
808 ot->prop = RNA_def_enum(
809 ot->srna, "mode", select_similar_mode_items, int(SelectSimilarMode::LAYER), "Mode", "");
810
811 RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, FLT_MAX, "Threshold", "", 0.0f, 10.0f);
812}
813
815{
816 const int amount_start = RNA_int_get(op->ptr, "amount_start");
817 const int amount_end = RNA_int_get(op->ptr, "amount_end");
819 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
821
823 &vc,
826 const IndexMask & /*universe*/,
827 StringRef /*attribute_name*/,
828 IndexMaskMemory &memory) {
830 *object, info.drawing, info.layer_index, memory);
832 info.drawing.strokes(), selectable_strokes, amount_start, amount_end, false, memory);
833 });
834
835 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
836 * attribute for now. */
837 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
838 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
839
840 return OPERATOR_FINISHED;
841}
842
844{
845 ot->name = "Select Ends";
846 ot->idname = "GREASE_PENCIL_OT_select_ends";
847 ot->description = "Select end points of strokes";
848
849 ot->exec = select_ends_exec;
851
853
854 RNA_def_int(ot->srna,
855 "amount_start",
856 0,
857 0,
858 INT32_MAX,
859 "Amount Start",
860 "Number of points to select from the start",
861 0,
862 INT32_MAX);
863 RNA_def_int(ot->srna,
864 "amount_end",
865 1,
866 0,
867 INT32_MAX,
868 "Amount End",
869 "Number of points to select from the end",
870 0,
871 INT32_MAX);
872}
873
875{
876 bool changed = false;
877
878 /* Convert all drawings of the active GP to the new selection domain. */
880 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
881 Span<GreasePencilDrawingBase *> drawings = grease_pencil.drawings();
882
883 for (const int index : drawings.index_range()) {
884 GreasePencilDrawingBase *drawing_base = drawings[index];
885 if (drawing_base->type != GP_DRAWING) {
886 continue;
887 }
888
889 GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
890 bke::CurvesGeometry &curves = drawing->wrap().strokes_for_write();
891 if (curves.is_empty()) {
892 continue;
893 }
894
895 /* Skip curve when the selection domain already matches, or when there is no selection
896 * at all. */
897 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
898 const std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(
899 ".selection");
900 if ((!meta_data) || (meta_data->domain == domain)) {
901 continue;
902 }
903
904 /* When the new selection domain is 'curve', ensure all curves with a point selection
905 * are selected. */
906 if (domain == bke::AttrDomain::Curve) {
908 }
909
910 /* Convert selection domain. */
911 const GVArray src = *attributes.lookup(".selection", domain);
912 if (src) {
913 const CPPType &type = src.type();
914 void *dst = MEM_malloc_arrayN(attributes.domain_size(domain), type.size, __func__);
915 src.materialize(dst);
916
917 attributes.remove(".selection");
918 if (!attributes.add(".selection",
919 domain,
922 {
923 MEM_freeN(dst);
924 }
925
926 changed = true;
927
928 /* TODO: expand point selection to segments when in 'segment' mode. */
929 }
930 }
931
932 return changed;
933}
934
936{
937 using namespace blender::bke::greasepencil;
938
939 /* Set new selection mode. */
940 const int mode_new = RNA_enum_get(op->ptr, "mode");
943
944 bool changed = false;
946 changed = (mode_new != ts->gpencil_selectmode_edit);
947 ts->gpencil_selectmode_edit = mode_new;
948 }
950 changed = (mode_new != ts->gpencil_selectmode_sculpt);
951 ts->gpencil_selectmode_sculpt = mode_new;
952 }
954 changed = (mode_new != ts->gpencil_selectmode_vertex);
955 ts->gpencil_selectmode_vertex = mode_new;
956 }
957
958 changed |= ensure_selection_domain(ts, ob);
959
960 if (changed) {
961 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
962 * attribute for now. */
963 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
964 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
965 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
966
968 }
969
970 return OPERATOR_FINISHED;
971}
972
974{
975 PropertyRNA *prop;
976
977 ot->name = "Select Mode";
978 ot->idname = __func__;
979 ot->description = "Change the selection mode for Grease Pencil strokes";
980
981 ot->exec = select_set_mode_exec;
983
985
986 ot->prop = prop = RNA_def_enum(
987 ot->srna, "mode", rna_enum_grease_pencil_selectmode_items, 0, "Mode", "");
989}
990
992{
993 const Scene *scene = CTX_data_scene(C);
996 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
997 const bool select = !RNA_boolean_get(op->ptr, "deselect");
998 const int material_index = object->actcol - 1;
1000
1001 if (material_index == -1) {
1002 return OPERATOR_CANCELLED;
1003 }
1004
1005 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
1006 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
1008
1009 IndexMaskMemory memory;
1011 *object, info.drawing, material_index, memory);
1012 if (strokes.is_empty()) {
1013 return;
1014 }
1015
1016 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
1017 const Span<StringRef> selection_attribute_names =
1019
1020 for (const int i : selection_attribute_names.index_range()) {
1022 curves, domain, bke::AttrType::Bool, selection_attribute_names[i]);
1023 switch (domain) {
1025 index_mask::masked_fill(selection.span.typed<bool>(), select, strokes);
1026 break;
1027 }
1029 strokes.foreach_index([&](const int curve_index) {
1030 const IndexRange points = points_by_curve[curve_index];
1031 ed::curves::fill_selection(selection.span.slice(points), select);
1032 });
1033 break;
1034 }
1035 default:
1037 }
1038 selection.finish();
1039 }
1040 });
1041
1042 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
1043 WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_EDITED, &grease_pencil);
1044
1045 return OPERATOR_FINISHED;
1046}
1047
1049{
1050 /* identifiers */
1051 ot->name = "Select Material";
1052 ot->idname = "GREASE_PENCIL_OT_material_select";
1053 ot->description = "Select/Deselect all Grease Pencil strokes using current material";
1054
1055 /* callbacks. */
1058
1059 /* flags. */
1060 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1061
1062 /* props */
1063 ot->prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Unselect strokes");
1065}
1066
1067} // namespace blender::ed::greasepencil
1068
1082
1084 const ToolSettings *tool_settings)
1085{
1086 const int selectmode = tool_settings->gpencil_selectmode_sculpt;
1089 }
1090 if (selectmode & (GP_SCULPT_MASK_SELECTMODE_STROKE)) {
1092 }
1094}
1095
1097 const ToolSettings *tool_settings)
1098{
1099 const int selectmode = tool_settings->gpencil_selectmode_vertex;
1102 }
1103 if (selectmode & (GP_VERTEX_MASK_SELECTMODE_STROKE)) {
1105 }
1107}
1108
1110 const Object *object)
1111{
1112 if (object->mode & OB_MODE_EDIT) {
1113 return ED_grease_pencil_edit_selection_domain_get(tool_settings);
1114 }
1117 }
1120 }
1122}
1123
1125{
1126 const int selectmode = tool_settings->gpencil_selectmode_vertex;
1129 {
1130 return true;
1131 }
1132 return false;
1133}
1134
1136{
1137 return tool_settings->gpencil_selectmode_edit == GP_SELECTMODE_SEGMENT;
1138}
1139
1144
1149
1151 const Object *object)
1152{
1153 if (object->mode & OB_MODE_EDIT) {
1155 }
1158 }
1161 }
1162 return false;
1163}
1164
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
Low-level operations for curves.
Low-level operations for grease pencil.
General operations, lookup, etc. for blender objects.
bool BKE_object_is_mode_compat(const Object *ob, eObjectMode object_mode)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_SCOPED_DEFER(function_to_defer)
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ OB_MODE_VERTEX_GREASE_PENCIL
@ OB_MODE_EDIT
@ OB_MODE_SCULPT_GREASE_PENCIL
Object is a sort of wrapper for general info.
@ GP_SELECTMODE_POINT
@ GP_SELECTMODE_SEGMENT
@ GP_SELECTMODE_STROKE
@ GP_SCULPT_MASK_SELECTMODE_POINT
@ GP_SCULPT_MASK_SELECTMODE_STROKE
@ GP_SCULPT_MASK_SELECTMODE_SEGMENT
@ GP_VERTEX_MASK_SELECTMODE_SEGMENT
@ GP_VERTEX_MASK_SELECTMODE_STROKE
@ GP_VERTEX_MASK_SELECTMODE_POINT
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
eSelectOp
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_SELECT
@ SEL_DESELECT
@ SEL_TOGGLE
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
#define ND_SPACE_VIEW3D
Definition WM_types.hh:528
#define NC_SPACE
Definition WM_types.hh:392
long long int int64_t
static unsigned long seed
Definition btSoftBody.h:39
static const CPPType & get()
void default_construct(void *ptr) const
GMutableSpan slice(const int64_t start, int64_t size) const
MutableSpan< T > typed() const
void materialize(void *dst) const
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr bool is_empty() const
static constexpr IndexRange from_begin_end(const int64_t begin, const int64_t end)
static constexpr IndexRange from_begin_end_inclusive(const int64_t begin, const int64_t last)
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
GAttributeReader lookup(const StringRef attribute_id) const
int domain_size(const AttrDomain domain) const
std::optional< AttributeMetaData > lookup_meta_data(StringRef attribute_id) const
bool add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
bool remove(const StringRef attribute_id)
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void to_bools(MutableSpan< bool > r_bools) const
void foreach_index(Fn &&fn) const
nullptr float
static wmOperatorStatus select_linked_exec(bContext *C, wmOperator *)
#define INT32_MAX
#define select(A, B, C)
VecBase< float, 4 > float4
blender::bke::AttrDomain ED_grease_pencil_sculpt_selection_domain_get(const ToolSettings *tool_settings)
bool ED_grease_pencil_sculpt_segment_selection_enabled(const ToolSettings *tool_settings)
blender::bke::AttrDomain ED_grease_pencil_edit_selection_domain_get(const ToolSettings *tool_settings)
bool ED_grease_pencil_edit_segment_selection_enabled(const ToolSettings *tool_settings)
blender::bke::AttrDomain ED_grease_pencil_vertex_selection_domain_get(const ToolSettings *tool_settings)
bool ED_grease_pencil_any_vertex_mask_selection(const ToolSettings *tool_settings)
void ED_operatortypes_grease_pencil_select()
blender::bke::AttrDomain ED_grease_pencil_selection_domain_get(const ToolSettings *tool_settings, const Object *object)
bool ED_grease_pencil_segment_selection_enabled(const ToolSettings *tool_settings, const Object *object)
bool ED_grease_pencil_vertex_segment_selection_enabled(const ToolSettings *tool_settings)
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static wmOperatorStatus select_all_exec(bContext *C, wmOperator *op)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
#define T
AttrType cpp_type_to_attribute_type(const CPPType &type)
void select_linked(bke::CurvesGeometry &curves, const IndexMask &curves_mask)
void apply_selection_operation_at_index(GMutableSpan selection, const int index, const eSelectOp sel_op)
static bool has_anything_selected(const Span< Curves * > curves_ids)
void select_all(bke::CurvesGeometry &curves, const IndexMask &mask, const bke::AttrDomain selection_domain, int action)
void select_alternate(bke::CurvesGeometry &curves, const IndexMask &curves_mask, const bool deselect_ends)
IndexMask curve_mask_from_points(const bke::CurvesGeometry &curves, const IndexMask &point_mask, const GrainSize grain_size, IndexMaskMemory &memory)
void fill_selection_false(GMutableSpan selection)
IndexMask select_adjacent_mask(const bke::CurvesGeometry &curves, const IndexMask &curves_mask, const StringRef attribute_name, const bool deselect, IndexMaskMemory &memory)
Span< StringRef > get_curves_selection_attribute_names(const bke::CurvesGeometry &curves)
void fill_selection(GMutableSpan selection, bool value)
IndexMask end_points(const bke::CurvesGeometry &curves, const IndexMask &curves_mask, const int amount_start, const int amount_end, const bool inverted, IndexMaskMemory &memory)
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, bke::AttrType create_type, StringRef attribute_name)
static void GREASE_PENCIL_OT_select_linked(wmOperatorType *ot)
IndexMask retrieve_editable_and_selected_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static wmOperatorStatus select_ends_exec(bContext *C, wmOperator *op)
FunctionRef< IndexMask(const ed::greasepencil::MutableDrawingInfo &info, const IndexMask &universe, StringRef attribute_name, IndexMaskMemory &memory)> SelectionUpdateFunc
IndexMask retrieve_editable_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static void GREASE_PENCIL_OT_select_more(wmOperatorType *ot)
static wmOperatorStatus select_set_mode_exec(bContext *C, wmOperator *op)
bool selection_update(const ViewContext *vc, const eSelectOp sel_op, SelectionUpdateFunc select_operation)
bool ensure_selection_domain(ToolSettings *ts, Object *object)
IndexMask retrieve_editable_and_all_selected_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, int handle_display, IndexMaskMemory &memory)
static wmOperatorStatus select_more_exec(bContext *C, wmOperator *)
bool editable_grease_pencil_point_selection_poll(bContext *C)
IndexMask retrieve_editable_elements(Object &object, const MutableDrawingInfo &info, const bke::AttrDomain selection_domain, IndexMaskMemory &memory)
void insert_selected_values(Object *object, const MutableDrawingInfo &info, const bke::AttrDomain domain, const StringRef attribute_id, const int handle_display, blender::Set< T > &r_value_set)
void free_curves_2d_bvh_data(Curves2DBVHTree &data)
static const EnumPropertyItem select_similar_mode_items[]
CurveSegmentsData find_curve_segments(const bke::CurvesGeometry &curves, const IndexMask &curve_mask, const Span< float2 > screen_space_positions, const Curves2DBVHTree &tree_data, const IndexRange tree_data_range)
static wmOperatorStatus select_alternate_exec(bContext *C, wmOperator *op)
static void select_similar_by_layer(Scene *scene, Object *object, GreasePencil &grease_pencil, bke::AttrDomain domain)
IndexMask retrieve_editable_strokes_by_material(Object &object, const bke::greasepencil::Drawing &drawing, const int mat_i, IndexMaskMemory &memory)
static wmOperatorStatus select_all_exec(bContext *C, wmOperator *op)
static wmOperatorStatus select_random_exec(bContext *C, wmOperator *op)
int clamp_range(const IndexRange range, const int index)
bool editable_grease_pencil_poll(bContext *C)
static wmOperatorStatus select_similar_exec(bContext *C, wmOperator *op)
static int foreach_curve_segment(const CurveSegmentsData &segment_data, const int curve_index, const IndexRange points, Fn &&fn)
static void GREASE_PENCIL_OT_select_less(wmOperatorType *ot)
bool apply_mask_as_selection(bke::CurvesGeometry &curves, const IndexMask &selection_mask, const bke::AttrDomain selection_domain, const StringRef attribute_name, const GrainSize grain_size, const eSelectOp sel_op)
static void select_similar_by_value(Scene *scene, Object *object, GreasePencil &grease_pencil, const bke::AttrDomain selection_domain, const StringRef attribute_id, const int handle_display, float threshold, DistanceFn distance_fn)
static void GREASE_PENCIL_OT_select_all(wmOperatorType *ot)
static void GREASE_PENCIL_OT_select_alternate(wmOperatorType *ot)
static void GREASE_PENCIL_OT_select_ends(wmOperatorType *ot)
static void GREASE_PENCIL_OT_select_similar(wmOperatorType *ot)
IndexMask retrieve_editable_strokes(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
static wmOperatorStatus select_less_exec(bContext *C, wmOperator *)
Vector< MutableDrawingInfo > retrieve_editable_drawings(const Scene &scene, GreasePencil &grease_pencil)
Curves2DBVHTree build_curves_2d_bvh_from_visible(const ViewContext &vc, const Object &object, const GreasePencil &grease_pencil, Span< MutableDrawingInfo > drawings, const int frame_number)
static wmOperatorStatus select_linked_exec(bContext *C, wmOperator *)
static void GREASE_PENCIL_OT_select_random(wmOperatorType *ot)
static wmOperatorStatus grease_pencil_material_select_exec(bContext *C, wmOperator *op)
static void GREASE_PENCIL_OT_material_select(wmOperatorType *ot)
static void GREASE_PENCIL_OT_set_selection_mode(wmOperatorType *ot)
Array< Vector< MutableDrawingInfo > > retrieve_editable_drawings_grouped_per_frame(const Scene &scene, GreasePencil &grease_pencil)
bool apply_mask_as_segment_selection(bke::CurvesGeometry &curves, const IndexMask &point_selection_mask, const StringRef attribute_name, const Curves2DBVHTree &tree_data, const IndexRange tree_data_range, const GrainSize grain_size, const eSelectOp sel_op)
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
T distance(const T &a, const T &b)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:56
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:93
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
static wmOperatorStatus select_random_exec(bContext *C, wmOperator *op)
static wmOperatorStatus select_less_exec(bContext *C, wmOperator *)
static wmOperatorStatus select_more_exec(bContext *C, wmOperator *)
int RNA_int_get(PointerRNA *ptr, const char *name)
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)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
const EnumPropertyItem rna_enum_grease_pencil_selectmode_items[]
Definition rna_scene.cc:686
#define FLT_MAX
Definition stdcycles.h:14
Definition DNA_ID.h:414
struct ToolSettings * toolsettings
char gpencil_selectmode_vertex
char gpencil_selectmode_sculpt
View3DOverlay overlay
Scene * scene
Definition ED_view3d.hh:73
bContext * C
Definition ED_view3d.hh:66
Object * obact
Definition ED_view3d.hh:75
Object * obedit
Definition ED_view3d.hh:76
Depsgraph * depsgraph
Definition ED_view3d.hh:72
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
int WM_operator_properties_select_random_seed_increment_get(wmOperator *op)
void WM_operator_properties_select_random(wmOperatorType *ot)
void WM_operator_properties_select_all(wmOperatorType *ot)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)