Blender V4.3
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
15#include "BLI_offset_indices.hh"
16#include "BLI_task.hh"
17
18#include "DNA_object_types.h"
19
20#include "DEG_depsgraph.hh"
22
23#include "ED_curves.hh"
24#include "ED_grease_pencil.hh"
25#include "ED_screen.hh"
26#include "ED_select_utils.hh"
27#include "ED_view3d.hh"
28
29#include "RNA_access.hh"
30#include "RNA_define.hh"
31#include "RNA_enum_types.hh"
32
33#include "WM_api.hh"
34
36
37/* -------------------------------------------------------------------- */
40
41inline int clamp_range(const IndexRange range, const int index)
42{
43 BLI_assert(!range.is_empty());
44 return std::clamp(index, int(range.first()), int(range.last()));
45}
46
54template<typename Fn>
55static int foreach_curve_segment(const CurveSegmentsData &segment_data,
56 const int curve_index,
57 const IndexRange points,
58 Fn &&fn)
59{
60 if (points.is_empty()) {
61 return 0;
62 }
63
64 const OffsetIndices segments_by_curve = OffsetIndices<int>(segment_data.segment_offsets);
65 const IndexRange segments = segments_by_curve[curve_index];
66
67 for (const int segment_i : segments) {
68 const int segment_point_i = segment_data.segment_start_points[segment_i];
69 const float segment_fraction = segment_data.segment_start_fractions[segment_i];
70
71 if (segment_i < segments.last()) {
72 const int next_segment_i = segment_i + 1;
73 const int next_segment_point_i = segment_data.segment_start_points[next_segment_i];
74 const float next_segment_fraction = segment_data.segment_start_fractions[next_segment_i];
75
76 /* Start point with zero fraction is included. */
77 const int first_point_i = (segment_fraction == 0.0f ?
78 segment_point_i :
79 clamp_range(points, segment_point_i + 1));
80 const int next_first_point_i = (next_segment_fraction == 0.0f ?
81 next_segment_point_i :
82 clamp_range(points, next_segment_point_i + 1));
83 const IndexRange points_range = IndexRange::from_begin_end(first_point_i,
84 next_first_point_i);
85 fn(segment_i, points_range, IndexRange());
86 }
87 else {
88 const int first_segment_point_i = segment_data.segment_start_points[segments.first()];
89 const float first_segment_fraction = segment_data.segment_start_fractions[segments.first()];
90 /* Start point with zero fraction is included. */
91 const int first_point_i = (segment_fraction == 0.0f ?
92 segment_point_i :
93 clamp_range(points, segment_point_i + 1));
94 /* End point with zero fraction is excluded. */
95 const int next_first_point_i = (first_segment_fraction == 0.0f ?
96 first_segment_point_i :
97 clamp_range(points, first_segment_point_i + 1));
98 const IndexRange points_range1 = IndexRange::from_begin_end(points.first(),
99 next_first_point_i);
100 const IndexRange points_range2 = IndexRange::from_begin_end_inclusive(first_point_i,
101 points.last());
102
103 fn(segment_i, points_range1, points_range2);
104 }
105 }
106 return segments.size();
107}
108
110 const IndexMask &selection_mask,
111 const bke::AttrDomain selection_domain,
112 const StringRef attribute_name,
113 const GrainSize grain_size,
114 const eSelectOp sel_op)
115{
116 if (selection_mask.is_empty()) {
117 return false;
118 }
119
120 const eCustomDataType create_type = CD_PROP_BOOL;
122 curves, selection_domain, create_type, attribute_name);
123
124 selection_mask.foreach_index(grain_size, [&](const int64_t element_i) {
125 ed::curves::apply_selection_operation_at_index(writer.span, element_i, sel_op);
126 });
127
128 writer.finish();
129
130 return true;
131}
132
134 const IndexMask &point_selection_mask,
135 const StringRef attribute_name,
136 const Curves2DBVHTree &tree_data,
137 const IndexRange tree_data_range,
138 const GrainSize grain_size,
139 const eSelectOp sel_op)
140{
141 /* Use regular selection for anything other than the ".selection" attribute. */
142 if (attribute_name != ".selection") {
144 curves, point_selection_mask, bke::AttrDomain::Point, attribute_name, grain_size, sel_op);
145 }
146
147 if (point_selection_mask.is_empty()) {
148 return false;
149 }
150 IndexMaskMemory memory;
151
152 const IndexMask changed_curve_mask = ed::curves::curve_mask_from_points(
153 curves, point_selection_mask, GrainSize(512), memory);
154
155 const OffsetIndices points_by_curve = curves.points_by_curve();
156 const Span<float2> screen_space_positions = tree_data.start_positions.as_span().slice(
157 tree_data_range);
158
160 curves, changed_curve_mask, screen_space_positions, tree_data, tree_data_range);
161
162 const OffsetIndices<int> segments_by_curve = OffsetIndices<int>(segment_data.segment_offsets);
163 const eCustomDataType create_type = CD_PROP_BOOL;
165 curves, bke::AttrDomain::Point, create_type, attribute_name);
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_object(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)) {
269 curves.attributes_for_write().lookup_or_add_for_write_span<bool>(attribute_name,
270 selection_domain);
272 selection.finish();
273 }
274
275 if (use_segment_selection) {
276 /* Range of points in tree data matching this curve, for re-using screen space
277 * positions. */
278 const IndexRange tree_data_range = tree_data_by_drawing[i_drawing];
280 changed_element_mask,
281 attribute_name,
282 tree_data,
283 tree_data_range,
284 GrainSize(4096),
285 sel_op);
286 }
287 else {
289 changed_element_mask,
290 selection_domain,
291 attribute_name,
292 GrainSize(4096),
293 sel_op);
294 }
295 }
296 }
297 }
298
299 if (changed) {
300 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
301 * generic attribute for now. */
302 DEG_id_tag_update(static_cast<ID *>(object->data), ID_RECALC_GEOMETRY);
304 }
305
306 return changed;
307}
308
310
312{
313 int action = RNA_enum_get(op->ptr, "action");
314 Scene *scene = CTX_data_scene(C);
316 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
318 object);
319
320 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
321 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
322 IndexMaskMemory memory;
323 const IndexMask selectable_elements = retrieve_editable_elements(
324 *object, info, selection_domain, memory);
325 if (selectable_elements.is_empty()) {
326 return;
327 }
328 if (action == SEL_TOGGLE) {
330 selection_domain) ?
333 }
335 info.drawing.strokes_for_write(), selectable_elements, selection_domain, action);
336 });
337
338 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
339 * attribute for now. */
340 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
341 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
342
343 return OPERATOR_FINISHED;
344}
345
347{
348 ot->name = "(De)select All Strokes";
349 ot->idname = "GREASE_PENCIL_OT_select_all";
350 ot->description = "(De)select all visible strokes";
351
352 ot->exec = select_all_exec;
354
356
358}
359
360static int select_more_exec(bContext *C, wmOperator * /*op*/)
361{
363 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
365
369 const IndexMask & /*universe*/,
370 StringRef attribute_name,
371 IndexMaskMemory &memory) {
373 info.drawing.strokes(), attribute_name, false, memory);
374 });
375
376 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
377 * attribute for now. */
378 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
379 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
380
381 return OPERATOR_FINISHED;
382}
383
385{
386 ot->name = "Select More";
387 ot->idname = "GREASE_PENCIL_OT_select_more";
388 ot->description = "Grow the selection by one point";
389
390 ot->exec = select_more_exec;
392
394}
395
396static int select_less_exec(bContext *C, wmOperator * /*op*/)
397{
399 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
401
405 const IndexMask & /*universe*/,
406 StringRef attribute_name,
407 IndexMaskMemory &memory) {
409 info.drawing.strokes(), attribute_name, true, memory);
410 });
411
412 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
413 * attribute for now. */
414 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
415 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
416
417 return OPERATOR_FINISHED;
418}
419
421{
422 ot->name = "Select Less";
423 ot->idname = "GREASE_PENCIL_OT_select_less";
424 ot->description = "Shrink the selection by one point";
425
426 ot->exec = select_less_exec;
428
430}
431
433{
434 Scene *scene = CTX_data_scene(C);
436 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
437
438 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
439 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
440 IndexMaskMemory memory;
442 *object, info.drawing, info.layer_index, memory);
443 if (selectable_strokes.is_empty()) {
444 return;
445 }
447 });
448
449 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
450 * attribute for now. */
451 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
452 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
453
454 return OPERATOR_FINISHED;
455}
456
458{
459 ot->name = "Select Linked";
460 ot->idname = "GREASE_PENCIL_OT_select_linked";
461 ot->description = "Select all points in curves with any point selection";
462
463 ot->exec = select_linked_exec;
465
467}
468
470{
471 using namespace blender;
472 const float ratio = RNA_float_get(op->ptr, "ratio");
474 Scene *scene = CTX_data_scene(C);
476 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
478 object);
480
481 /* Note: For segment selection this doesn't work very well, because it is based on random point
482 * selection. A segment has a high probability of getting at least one selected point and be
483 * itself selected.
484 * For better distribution the random value must be generated per segment and possibly weighted
485 * by segment length.
486 */
488 &vc,
491 const IndexMask & /*universe*/,
492 StringRef /*attribute_name*/,
493 IndexMaskMemory &memory) -> IndexMask {
494 const IndexMask selectable_elements = retrieve_editable_elements(
495 *object, info, selection_domain, memory);
496 if (selectable_elements.is_empty()) {
497 return {};
498 }
500 selectable_elements,
501 selection_domain,
503 ratio,
504 memory);
505 });
506
507 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
508 * attribute for now. */
509 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
510 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
511
512 return OPERATOR_FINISHED;
513}
514
516{
517 ot->name = "Select Random";
518 ot->idname = "GREASE_PENCIL_OT_select_random";
519 ot->description = "Selects random points from the current strokes selection";
520
521 ot->exec = select_random_exec;
523
525
527}
528
530{
531 const bool deselect_ends = RNA_boolean_get(op->ptr, "deselect_ends");
532 Scene *scene = CTX_data_scene(C);
534 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
535
536 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
537 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
539 });
540
541 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
542 * attribute for now. */
543 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
544 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
545
546 return OPERATOR_FINISHED;
547}
548
550{
551 ot->name = "Select Alternate";
552 ot->idname = "GREASE_PENCIL_OT_select_alternate";
553 ot->description = "Select alternated points in strokes with already selected points";
554
557
559
560 RNA_def_boolean(ot->srna,
561 "deselect_ends",
562 false,
563 "Deselect Ends",
564 "(De)select the first and last point of each stroke");
565}
566
574
576 {int(SelectSimilarMode::LAYER), "LAYER", 0, "Layer", ""},
577 {int(SelectSimilarMode::MATERIAL), "MATERIAL", 0, "Material", ""},
578 {int(SelectSimilarMode::VERTEX_COLOR), "VERTEX_COLOR", 0, "Vertex Color", ""},
579 {int(SelectSimilarMode::RADIUS), "RADIUS", 0, "Radius", ""},
580 {int(SelectSimilarMode::OPACITY), "OPACITY", 0, "Opacity", ""},
581 {0, nullptr, 0, nullptr, nullptr},
582};
583
584template<typename T>
586 const bke::AttrDomain domain,
587 const StringRef attribute_id,
588 blender::Set<T> &r_value_set)
589{
590 T default_value;
591 CPPType::get<T>().default_construct(&default_value);
592
593 const bke::AttributeAccessor attributes = curves.attributes();
594 const VArraySpan<bool> selection = *attributes.lookup_or_default<bool>(
595 ".selection", domain, true);
596 const VArraySpan<T> values = *attributes.lookup_or_default<T>(
597 attribute_id, domain, default_value);
598
601 IndexRange(attributes.domain_size(domain)), 1024, [&](const IndexRange range) {
602 Set<T> &local_value_set = value_set_by_thread.local();
603 for (const int i : range) {
604 if (selection[i]) {
605 local_value_set.add(values[i]);
606 }
607 }
608 });
609
610 for (const Set<T> &local_value_set : value_set_by_thread) {
611 /* TODO is there a union function that can do this more efficiently? */
612 for (const T &key : local_value_set) {
613 r_value_set.add(key);
614 }
615 }
616}
617
618template<typename T, typename DistanceFn>
620 Object *object,
621 GreasePencil &grease_pencil,
622 const bke::AttrDomain domain,
623 const StringRef attribute_id,
624 float threshold,
625 DistanceFn distance_fn)
626{
627 using namespace blender::ed::greasepencil;
628
629 T default_value;
630 CPPType::get<T>().default_construct(&default_value);
631
633 grease_pencil);
634
635 blender::Set<T> selected_values;
636 for (const MutableDrawingInfo &info : drawings) {
637 insert_selected_values(info.drawing.strokes(), domain, attribute_id, selected_values);
638 }
639
640 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
643 const int domain_size = attributes.domain_size(domain);
644 bke::SpanAttributeWriter<bool> selection_writer =
645 attributes.lookup_or_add_for_write_span<bool>(
646 ".selection",
647 domain,
649 const VArraySpan<T> values = *attributes.lookup_or_default<T>(
650 attribute_id, domain, default_value);
651
652 IndexMaskMemory memory;
654 *object, info.drawing, info.layer_index, memory);
655
656 mask.foreach_index(GrainSize(1024), [&](const int index) {
657 if (selection_writer.span[index]) {
658 return;
659 }
660 for (const T &test_value : selected_values) {
661 if (distance_fn(values[index], test_value) <= threshold) {
662 selection_writer.span[index] = true;
663 }
664 }
665 });
666
667 selection_writer.finish();
668 });
669}
670
672 Object *object,
673 GreasePencil &grease_pencil,
674 bke::AttrDomain domain)
675{
677 grease_pencil);
678
679 blender::Set<int> selected_layers;
680 /* Layer is selected if any point is selected. */
681 for (const MutableDrawingInfo &info : drawings) {
683 *info.drawing.strokes().attributes().lookup_or_default<bool>(".selection", domain, true);
684 for (const int i : selection.index_range()) {
685 if (selection[i]) {
686 selected_layers.add(info.layer_index);
687 break;
688 }
689 }
690 }
691
692 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
693 if (!selected_layers.contains(info.layer_index)) {
694 return;
695 }
696
697 IndexMaskMemory memory;
698 const IndexMask editable_elements = retrieve_editable_elements(*object, info, domain, memory);
699 if (editable_elements.is_empty()) {
700 return;
701 }
703 info.drawing.strokes_for_write(), editable_elements, domain, SEL_SELECT);
704 });
705}
706
708{
709 const SelectSimilarMode mode = SelectSimilarMode(RNA_enum_get(op->ptr, "mode"));
710 const float threshold = RNA_float_get(op->ptr, "threshold");
711 Scene *scene = CTX_data_scene(C);
713 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
715 object);
716
717 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
718
719 switch (mode) {
721 select_similar_by_layer(scene, object, grease_pencil, selection_domain);
722 break;
725 scene,
726 object,
727 grease_pencil,
728 selection_domain,
729 "material_index",
730 threshold,
731 [](const int a, const int b) -> float { return float(math::distance(a, b)); });
732 break;
735 scene,
736 object,
737 grease_pencil,
738 selection_domain,
739 "vertex_color",
740 threshold,
741 [](const ColorGeometry4f &a, const ColorGeometry4f &b) -> float {
742 return math::distance(float4(a), float4(b));
743 });
744 break;
747 scene,
748 object,
749 grease_pencil,
750 selection_domain,
751 "radius",
752 threshold,
753 [](const float a, const float b) -> float { return math::distance(a, b); });
754 break;
757 scene,
758 object,
759 grease_pencil,
760 selection_domain,
761 "opacity",
762 threshold,
763 [](const float a, const float b) -> float { return math::distance(a, b); });
764 break;
765 }
766
767 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
768 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
769
770 return OPERATOR_FINISHED;
771}
772
774{
775 ot->name = "Select Similar";
776 ot->idname = "GREASE_PENCIL_OT_select_similar";
777 ot->description = "Select all strokes with similar characteristics";
778
779 ot->invoke = WM_menu_invoke;
780 ot->exec = select_similar_exec;
782
784
785 ot->prop = RNA_def_enum(
786 ot->srna, "mode", select_similar_mode_items, int(SelectSimilarMode::LAYER), "Mode", "");
787
788 RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, FLT_MAX, "Threshold", "", 0.0f, 10.0f);
789}
790
792{
793 const int amount_start = RNA_int_get(op->ptr, "amount_start");
794 const int amount_end = RNA_int_get(op->ptr, "amount_end");
796 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
798
800 &vc,
803 const IndexMask & /*universe*/,
804 StringRef /*attribute_name*/,
805 IndexMaskMemory &memory) {
807 *object, info.drawing, info.layer_index, memory);
809 info.drawing.strokes(), selectable_strokes, amount_start, amount_end, false, memory);
810 });
811
812 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
813 * attribute for now. */
814 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
815 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
816
817 return OPERATOR_FINISHED;
818}
819
821{
822 ot->name = "Select Ends";
823 ot->idname = "GREASE_PENCIL_OT_select_ends";
824 ot->description = "Select end points of strokes";
825
826 ot->exec = select_ends_exec;
828
830
831 RNA_def_int(ot->srna,
832 "amount_start",
833 0,
834 0,
835 INT32_MAX,
836 "Amount Start",
837 "Number of points to select from the start",
838 0,
839 INT32_MAX);
840 RNA_def_int(ot->srna,
841 "amount_end",
842 1,
843 0,
844 INT32_MAX,
845 "Amount End",
846 "Number of points to select from the end",
847 0,
848 INT32_MAX);
849}
850
852{
853 using namespace blender::bke::greasepencil;
854
855 /* Set new selection mode. */
856 const int mode_new = RNA_enum_get(op->ptr, "mode");
858
859 bool changed = (mode_new != ts->gpencil_selectmode_edit);
860 ts->gpencil_selectmode_edit = mode_new;
861
862 /* Convert all drawings of the active GP to the new selection domain. */
865 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
866 Span<GreasePencilDrawingBase *> drawings = grease_pencil.drawings();
867
868 for (const int index : drawings.index_range()) {
869 GreasePencilDrawingBase *drawing_base = drawings[index];
870 if (drawing_base->type != GP_DRAWING) {
871 continue;
872 }
873
874 GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
875 bke::CurvesGeometry &curves = drawing->wrap().strokes_for_write();
876 if (curves.points_num() == 0) {
877 continue;
878 }
879
880 /* Skip curve when the selection domain already matches, or when there is no selection
881 * at all. */
882 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
883 const std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(
884 ".selection");
885 if ((!meta_data) || (meta_data->domain == domain)) {
886 continue;
887 }
888
889 /* When the new selection domain is 'curve', ensure all curves with a point selection
890 * are selected. */
891 if (domain == bke::AttrDomain::Curve) {
893 }
894
895 /* Convert selection domain. */
896 const GVArray src = *attributes.lookup(".selection", domain);
897 if (src) {
898 const CPPType &type = src.type();
899 void *dst = MEM_malloc_arrayN(attributes.domain_size(domain), type.size(), __func__);
900 src.materialize(dst);
901
902 attributes.remove(".selection");
903 if (!attributes.add(".selection",
904 domain,
907 {
908 MEM_freeN(dst);
909 }
910
911 changed = true;
912
913 /* TODO: expand point selection to segments when in 'segment' mode. */
914 }
915 }
916
917 if (changed) {
918 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
919 * attribute for now. */
920 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
921 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
922
924 }
925
926 return OPERATOR_FINISHED;
927}
928
930{
931 PropertyRNA *prop;
932
933 ot->name = "Select Mode";
934 ot->idname = __func__;
935 ot->description = "Change the selection mode for Grease Pencil strokes";
936
937 ot->exec = select_set_mode_exec;
939
941
942 ot->prop = prop = RNA_def_enum(
943 ot->srna, "mode", rna_enum_grease_pencil_selectmode_items, 0, "Mode", "");
945}
946
948{
949 const Scene *scene = CTX_data_scene(C);
951 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
952 const bool select = !RNA_boolean_get(op->ptr, "deselect");
953 const int material_index = object->actcol - 1;
954
955 if (material_index == -1) {
956 return OPERATOR_CANCELLED;
957 }
958
959 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
960 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
962
963 IndexMaskMemory memory;
965 *object, info.drawing, material_index, memory);
966 if (strokes.is_empty()) {
967 return;
968 }
971 index_mask::masked_fill(selection.span.typed<bool>(), select, strokes);
972 selection.finish();
973 });
974
975 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
976 WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_EDITED, &grease_pencil);
977
978 return OPERATOR_FINISHED;
979}
980
982{
983 /* identifiers */
984 ot->name = "Select Material";
985 ot->idname = "GREASE_PENCIL_OT_material_select";
986 ot->description = "Select/Deselect all Grease Pencil strokes using current material";
987
988 /* callbacks. */
991
992 /* flags. */
994
995 /* props */
996 ot->prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Unselect strokes");
998}
999
1000} // namespace blender::ed::greasepencil
1001
1015
1017 const ToolSettings *tool_settings)
1018{
1019 const int selectmode = tool_settings->gpencil_selectmode_sculpt;
1022 }
1023 if (selectmode & (GP_SCULPT_MASK_SELECTMODE_STROKE)) {
1025 }
1027}
1028
1030 const ToolSettings *tool_settings)
1031{
1032 const int selectmode = tool_settings->gpencil_selectmode_vertex;
1035 }
1036 if (selectmode & (GP_VERTEX_MASK_SELECTMODE_STROKE)) {
1038 }
1040}
1041
1043 const Object *object)
1044{
1045 if (object->mode & OB_MODE_EDIT) {
1046 return ED_grease_pencil_edit_selection_domain_get(tool_settings);
1047 }
1050 }
1053 }
1055}
1056
1058{
1059 return tool_settings->gpencil_selectmode_edit == GP_SELECTMODE_SEGMENT;
1060}
1061
1066
1071
1073 const Object *object)
1074{
1075 if (object->mode & OB_MODE_EDIT) {
1077 }
1080 }
1083 }
1084 return false;
1085}
1086
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)
Low-level operations for curves.
Low-level operations for grease pencil.
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_SCOPED_DEFER(function_to_defer)
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ 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
@ SEL_SELECT
@ SEL_DESELECT
@ SEL_TOGGLE
eSelectOp
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
PropertyFlag
Definition RNA_types.hh:201
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NA_EDITED
Definition WM_types.hh:550
#define ND_SPACE_VIEW3D
Definition WM_types.hh:494
#define NC_SPACE
Definition WM_types.hh:359
static unsigned long seed
Definition btSoftBody.h:39
static const CPPType & get()
int64_t size() 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:291
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
static VArray ForSingle(T value, const int64_t size)
GAttributeReader lookup(const StringRef attribute_id) const
std::optional< AttributeMetaData > lookup_meta_data(const StringRef attribute_id) const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
int domain_size(const AttrDomain domain) const
MutableAttributeAccessor attributes_for_write()
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bool remove(const StringRef attribute_id)
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
void to_bools(MutableSpan< bool > r_bools) const
void foreach_index(Fn &&fn) const
local_group_size(16, 16) .push_constant(Type b
static int select_linked_exec(bContext *C, wmOperator *)
draw_view in_light_buf[] float
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
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)
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:45
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
static int select_all_exec(bContext *C, wmOperator *op)
ccl_device_inline float4 mask(const int4 mask, const float4 a)
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
#define T
eCustomDataType cpp_type_to_custom_data_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)
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, eCustomDataType create_type, StringRef attribute_name)
IndexMask random_mask(const bke::CurvesGeometry &curves, const IndexMask &mask, const bke::AttrDomain selection_domain, const uint32_t random_seed, const float probability, IndexMaskMemory &memory)
static void GREASE_PENCIL_OT_select_linked(wmOperatorType *ot)
IndexMask retrieve_editable_points(Object &object, const bke::greasepencil::Drawing &drawing, int layer_index, IndexMaskMemory &memory)
FunctionRef< IndexMask(const ed::greasepencil::MutableDrawingInfo &info, const IndexMask &universe, StringRef attribute_name, IndexMaskMemory &memory)> SelectionUpdateFunc
static void GREASE_PENCIL_OT_select_more(wmOperatorType *ot)
static int select_less_exec(bContext *C, wmOperator *)
bool selection_update(const ViewContext *vc, const eSelectOp sel_op, SelectionUpdateFunc select_operation)
static int select_ends_exec(bContext *C, wmOperator *op)
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)
static void select_similar_by_value(Scene *scene, Object *object, GreasePencil &grease_pencil, const bke::AttrDomain domain, const StringRef attribute_id, float threshold, DistanceFn distance_fn)
static int select_set_mode_exec(bContext *C, wmOperator *op)
static int select_linked_exec(bContext *C, wmOperator *)
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 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 int select_alternate_exec(bContext *C, wmOperator *op)
static int grease_pencil_material_select_exec(bContext *C, wmOperator *op)
int clamp_range(const IndexRange range, const int index)
bool editable_grease_pencil_poll(bContext *C)
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)
static int select_random_exec(bContext *C, wmOperator *op)
static int select_more_exec(bContext *C, wmOperator *)
void insert_selected_values(const bke::CurvesGeometry &curves, const bke::AttrDomain domain, const StringRef attribute_id, blender::Set< T > &r_value_set)
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 int select_all_exec(bContext *C, wmOperator *op)
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)
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 void GREASE_PENCIL_OT_select_random(wmOperatorType *ot)
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)
static int select_similar_exec(bContext *C, wmOperator *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: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
uint64_t get_default_hash(const T &v)
Definition BLI_hash.hh:219
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:337
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
static int select_random_exec(bContext *C, wmOperator *op)
static int select_more_exec(bContext *C, wmOperator *)
static int select_less_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:673
#define FLT_MAX
Definition stdcycles.h:14
#define INT32_MAX
Definition stdint.h:137
__int64 int64_t
Definition stdint.h:89
Definition DNA_ID.h:413
struct ToolSettings * toolsettings
char gpencil_selectmode_vertex
char gpencil_selectmode_sculpt
Scene * scene
Definition ED_view3d.hh:69
bContext * C
Definition ED_view3d.hh:62
Object * obact
Definition ED_view3d.hh:71
Object * obedit
Definition ED_view3d.hh:72
Depsgraph * depsgraph
Definition ED_view3d.hh:68
struct PointerRNA * ptr
VecBase< float, 4 > float4
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:4125
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 *))
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)