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
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/* -------------------------------------------------------------------- */
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;
229 ed::greasepencil::retrieve_editable_drawings_grouped_per_frame(*vc->scene, grease_pencil);
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. */
239 BLI_SCOPED_DEFER([&]() { ed::greasepencil::free_curves_2d_bvh_data(tree_data); });
240 if (use_segment_selection) {
241 tree_data = ed::greasepencil::build_curves_2d_bvh_from_visible(
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 =
253 ed::curves::get_curves_selection_attribute_names(curves);
254
255 IndexMaskMemory memory;
256 const IndexMask elements = ed::greasepencil::retrieve_editable_elements(
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::GSpanAttributeWriter selection =
269 curves.attributes_for_write().lookup_for_write_span(attribute_name))
270 {
271 ed::curves::fill_selection_false(selection.span);
272 selection.finish();
273 }
274 }
275
276 if (use_segment_selection) {
277 /* Range of points in tree data matching this curve, for re-using screen space
278 * positions. */
279 const IndexRange tree_data_range = tree_data_by_drawing[i_drawing];
280 changed |= ed::greasepencil::apply_mask_as_segment_selection(curves,
281 changed_element_mask,
282 attribute_name,
283 tree_data,
284 tree_data_range,
285 GrainSize(4096),
286 sel_op);
287 }
288 else {
289 changed |= ed::greasepencil::apply_mask_as_selection(curves,
290 changed_element_mask,
291 selection_domain,
292 attribute_name,
293 GrainSize(4096),
294 sel_op);
295 }
296 }
297 }
298 }
299
300 if (changed) {
301 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
302 * generic attribute for now. */
303 DEG_id_tag_update(static_cast<ID *>(object->data), ID_RECALC_GEOMETRY);
304 WM_event_add_notifier(vc->C, NC_GEOM | ND_DATA, object->data);
305 }
306
307 return changed;
308}
309
313{
314 int action = RNA_enum_get(op->ptr, "action");
315 Scene *scene = CTX_data_scene(C);
316 Object *object = CTX_data_active_object(C);
317 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
318 bke::AttrDomain selection_domain = ED_grease_pencil_selection_domain_get(scene->toolsettings,
319 object);
320
321 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
322 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
323 IndexMaskMemory memory;
324 const IndexMask selectable_elements = retrieve_editable_elements(
325 *object, info, selection_domain, memory);
326 if (selectable_elements.is_empty()) {
327 return;
328 }
329 if (action == SEL_TOGGLE) {
331 selection_domain) ?
334 }
336 info.drawing.strokes_for_write(), selectable_elements, selection_domain, action);
337 });
338
339 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
340 * attribute for now. */
341 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
342 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
343
344 return OPERATOR_FINISHED;
345}
346
348{
349 ot->name = "(De)select All Strokes";
350 ot->idname = "GREASE_PENCIL_OT_select_all";
351 ot->description = "(De)select all visible strokes";
352
355
357
359}
360
361static int select_more_exec(bContext *C, wmOperator * /*op*/)
362{
363 Object *object = CTX_data_active_object(C);
364 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
366
367 ed::greasepencil::selection_update(&vc,
370 const IndexMask & /*universe*/,
371 StringRef attribute_name,
372 IndexMaskMemory &memory) {
374 info.drawing.strokes(), attribute_name, false, memory);
375 });
376
377 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
378 * attribute for now. */
379 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
380 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
381
382 return OPERATOR_FINISHED;
383}
384
386{
387 ot->name = "Select More";
388 ot->idname = "GREASE_PENCIL_OT_select_more";
389 ot->description = "Grow the selection by one point";
390
393
395}
396
397static int select_less_exec(bContext *C, wmOperator * /*op*/)
398{
399 Object *object = CTX_data_active_object(C);
400 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
402
403 ed::greasepencil::selection_update(&vc,
406 const IndexMask & /*universe*/,
407 StringRef attribute_name,
408 IndexMaskMemory &memory) {
410 info.drawing.strokes(), attribute_name, true, memory);
411 });
412
413 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
414 * attribute for now. */
415 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
416 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
417
418 return OPERATOR_FINISHED;
419}
420
422{
423 ot->name = "Select Less";
424 ot->idname = "GREASE_PENCIL_OT_select_less";
425 ot->description = "Shrink the selection by one point";
426
429
431}
432
433static int select_linked_exec(bContext *C, wmOperator * /*op*/)
434{
435 Scene *scene = CTX_data_scene(C);
436 Object *object = CTX_data_active_object(C);
437 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
438
439 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
440 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
441 IndexMaskMemory memory;
442 const IndexMask selectable_strokes = ed::greasepencil::retrieve_editable_strokes(
443 *object, info.drawing, info.layer_index, memory);
444 if (selectable_strokes.is_empty()) {
445 return;
446 }
448 });
449
450 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
451 * attribute for now. */
452 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
453 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
454
455 return OPERATOR_FINISHED;
456}
457
459{
460 ot->name = "Select Linked";
461 ot->idname = "GREASE_PENCIL_OT_select_linked";
462 ot->description = "Select all points in curves with any point selection";
463
466
468}
469
471{
472 using namespace blender;
473 const float ratio = RNA_float_get(op->ptr, "ratio");
475 Scene *scene = CTX_data_scene(C);
476 Object *object = CTX_data_active_object(C);
477 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
478 bke::AttrDomain selection_domain = ED_grease_pencil_selection_domain_get(scene->toolsettings,
479 object);
481
482 /* Note: For segment selection this doesn't work very well, because it is based on random point
483 * selection. A segment has a high probability of getting at least one selected point and be
484 * itself selected.
485 * For better distribution the random value must be generated per segment and possibly weighted
486 * by segment length.
487 */
488 ed::greasepencil::selection_update(
489 &vc,
492 const IndexMask & /*universe*/,
493 StringRef /*attribute_name*/,
494 IndexMaskMemory &memory) -> IndexMask {
495 const IndexMask selectable_elements = retrieve_editable_elements(
496 *object, info, selection_domain, memory);
497 if (selectable_elements.is_empty()) {
498 return {};
499 }
500 return ed::curves::random_mask(info.drawing.strokes(),
501 selectable_elements,
502 selection_domain,
504 ratio,
505 memory);
506 });
507
508 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
509 * attribute for now. */
510 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
511 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
512
513 return OPERATOR_FINISHED;
514}
515
517{
518 ot->name = "Select Random";
519 ot->idname = "GREASE_PENCIL_OT_select_random";
520 ot->description = "Selects random points from the current strokes selection";
521
524
526
528}
529
531{
532 const bool deselect_ends = RNA_boolean_get(op->ptr, "deselect_ends");
533 Scene *scene = CTX_data_scene(C);
534 Object *object = CTX_data_active_object(C);
535 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
536
537 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
538 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
540 });
541
542 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
543 * attribute for now. */
544 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
545 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
546
547 return OPERATOR_FINISHED;
548}
549
551{
552 ot->name = "Select Alternate";
553 ot->idname = "GREASE_PENCIL_OT_select_alternate";
554 ot->description = "Select alternated points in strokes with already selected points";
555
558
560
562 "deselect_ends",
563 false,
564 "Deselect Ends",
565 "(De)select the first and last point of each stroke");
566}
567
569 LAYER,
570 MATERIAL,
572 RADIUS,
573 OPACITY,
574};
575
577 {int(SelectSimilarMode::LAYER), "LAYER", 0, "Layer", ""},
578 {int(SelectSimilarMode::MATERIAL), "MATERIAL", 0, "Material", ""},
579 {int(SelectSimilarMode::VERTEX_COLOR), "VERTEX_COLOR", 0, "Vertex Color", ""},
580 {int(SelectSimilarMode::RADIUS), "RADIUS", 0, "Radius", ""},
581 {int(SelectSimilarMode::OPACITY), "OPACITY", 0, "Opacity", ""},
582 {0, nullptr, 0, nullptr, nullptr},
583};
584
585template<typename T>
587 const bke::AttrDomain domain,
588 const StringRef attribute_id,
589 blender::Set<T> &r_value_set)
590{
591 T default_value;
592 CPPType::get<T>().default_construct(&default_value);
593
594 const bke::AttributeAccessor attributes = curves.attributes();
595 const VArraySpan<bool> selection = *attributes.lookup_or_default<bool>(
596 ".selection", domain, true);
597 const VArraySpan<T> values = *attributes.lookup_or_default<T>(
598 attribute_id, domain, default_value);
599
601 threading::parallel_for(
602 IndexRange(attributes.domain_size(domain)), 1024, [&](const IndexRange range) {
603 Set<T> &local_value_set = value_set_by_thread.local();
604 for (const int i : range) {
605 if (selection[i]) {
606 local_value_set.add(values[i]);
607 }
608 }
609 });
610
611 for (const Set<T> &local_value_set : value_set_by_thread) {
612 /* TODO is there a union function that can do this more efficiently? */
613 for (const T &key : local_value_set) {
614 r_value_set.add(key);
615 }
616 }
617}
618
619template<typename T, typename DistanceFn>
621 Object *object,
622 GreasePencil &grease_pencil,
623 const bke::AttrDomain domain,
624 const StringRef attribute_id,
625 float threshold,
626 DistanceFn distance_fn)
627{
628 using namespace blender::ed::greasepencil;
629
630 T default_value;
631 CPPType::get<T>().default_construct(&default_value);
632
634 grease_pencil);
635
636 blender::Set<T> selected_values;
637 for (const MutableDrawingInfo &info : drawings) {
638 insert_selected_values(info.drawing.strokes(), domain, attribute_id, selected_values);
639 }
640
641 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
643 info.drawing.strokes_for_write().attributes_for_write();
644 const int domain_size = attributes.domain_size(domain);
645 bke::SpanAttributeWriter<bool> selection_writer =
646 attributes.lookup_or_add_for_write_span<bool>(
647 ".selection",
648 domain,
650 const VArraySpan<T> values = *attributes.lookup_or_default<T>(
651 attribute_id, domain, default_value);
652
653 IndexMaskMemory memory;
654 const IndexMask mask = ed::greasepencil::retrieve_editable_points(
655 *object, info.drawing, info.layer_index, memory);
656
657 mask.foreach_index(GrainSize(1024), [&](const int index) {
658 if (selection_writer.span[index]) {
659 return;
660 }
661 for (const T &test_value : selected_values) {
662 if (distance_fn(values[index], test_value) <= threshold) {
663 selection_writer.span[index] = true;
664 }
665 }
666 });
667
668 selection_writer.finish();
669 });
670}
671
673 Object *object,
674 GreasePencil &grease_pencil,
675 bke::AttrDomain domain)
676{
678 grease_pencil);
679
680 blender::Set<int> selected_layers;
681 /* Layer is selected if any point is selected. */
682 for (const MutableDrawingInfo &info : drawings) {
683 const VArraySpan<bool> selection =
684 *info.drawing.strokes().attributes().lookup_or_default<bool>(".selection", domain, true);
685 for (const int i : selection.index_range()) {
686 if (selection[i]) {
687 selected_layers.add(info.layer_index);
688 break;
689 }
690 }
691 }
692
693 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
694 if (!selected_layers.contains(info.layer_index)) {
695 return;
696 }
697
698 IndexMaskMemory memory;
699 const IndexMask editable_elements = retrieve_editable_elements(*object, info, domain, memory);
700 if (editable_elements.is_empty()) {
701 return;
702 }
703 ed::curves::select_all(
704 info.drawing.strokes_for_write(), editable_elements, domain, SEL_SELECT);
705 });
706}
707
709{
710 const SelectSimilarMode mode = SelectSimilarMode(RNA_enum_get(op->ptr, "mode"));
711 const float threshold = RNA_float_get(op->ptr, "threshold");
712 Scene *scene = CTX_data_scene(C);
713 Object *object = CTX_data_active_object(C);
714 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
715 bke::AttrDomain selection_domain = ED_grease_pencil_selection_domain_get(scene->toolsettings,
716 object);
717
718 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
719
720 switch (mode) {
721 case SelectSimilarMode::LAYER:
722 select_similar_by_layer(scene, object, grease_pencil, selection_domain);
723 break;
724 case SelectSimilarMode::MATERIAL:
725 select_similar_by_value<int>(
726 scene,
727 object,
728 grease_pencil,
729 selection_domain,
730 "material_index",
731 threshold,
732 [](const int a, const int b) -> float { return float(math::distance(a, b)); });
733 break;
734 case SelectSimilarMode::VERTEX_COLOR:
735 select_similar_by_value<ColorGeometry4f>(
736 scene,
737 object,
738 grease_pencil,
739 selection_domain,
740 "vertex_color",
741 threshold,
742 [](const ColorGeometry4f &a, const ColorGeometry4f &b) -> float {
743 return math::distance(float4(a), float4(b));
744 });
745 break;
746 case SelectSimilarMode::RADIUS:
747 select_similar_by_value<float>(
748 scene,
749 object,
750 grease_pencil,
751 selection_domain,
752 "radius",
753 threshold,
754 [](const float a, const float b) -> float { return math::distance(a, b); });
755 break;
756 case SelectSimilarMode::OPACITY:
757 select_similar_by_value<float>(
758 scene,
759 object,
760 grease_pencil,
761 selection_domain,
762 "opacity",
763 threshold,
764 [](const float a, const float b) -> float { return math::distance(a, b); });
765 break;
766 }
767
768 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
769 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
770
771 return OPERATOR_FINISHED;
772}
773
775{
776 ot->name = "Select Similar";
777 ot->idname = "GREASE_PENCIL_OT_select_similar";
778 ot->description = "Select all strokes with similar characteristics";
779
783
785
787 ot->srna, "mode", select_similar_mode_items, int(SelectSimilarMode::LAYER), "Mode", "");
788
789 RNA_def_float(ot->srna, "threshold", 0.1f, 0.0f, FLT_MAX, "Threshold", "", 0.0f, 10.0f);
790}
791
793{
794 const int amount_start = RNA_int_get(op->ptr, "amount_start");
795 const int amount_end = RNA_int_get(op->ptr, "amount_end");
796 Object *object = CTX_data_active_object(C);
797 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
799
800 ed::greasepencil::selection_update(
801 &vc,
804 const IndexMask & /*universe*/,
805 StringRef /*attribute_name*/,
806 IndexMaskMemory &memory) {
807 const IndexMask selectable_strokes = ed::greasepencil::retrieve_editable_strokes(
808 *object, info.drawing, info.layer_index, memory);
809 return ed::curves::end_points(
810 info.drawing.strokes(), selectable_strokes, amount_start, amount_end, false, memory);
811 });
812
813 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
814 * attribute for now. */
815 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
816 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
817
818 return OPERATOR_FINISHED;
819}
820
822{
823 ot->name = "Select Ends";
824 ot->idname = "GREASE_PENCIL_OT_select_ends";
825 ot->description = "Select end points of strokes";
826
829
831
833 "amount_start",
834 0,
835 0,
836 INT32_MAX,
837 "Amount Start",
838 "Number of points to select from the start",
839 0,
840 INT32_MAX);
842 "amount_end",
843 1,
844 0,
845 INT32_MAX,
846 "Amount End",
847 "Number of points to select from the end",
848 0,
849 INT32_MAX);
850}
851
853{
854 using namespace blender::bke::greasepencil;
855
856 /* Set new selection mode. */
857 const int mode_new = RNA_enum_get(op->ptr, "mode");
859
860 bool changed = (mode_new != ts->gpencil_selectmode_edit);
861 ts->gpencil_selectmode_edit = mode_new;
862
863 /* Convert all drawings of the active GP to the new selection domain. */
864 Object *object = CTX_data_active_object(C);
866 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
867 Span<GreasePencilDrawingBase *> drawings = grease_pencil.drawings();
868
869 for (const int index : drawings.index_range()) {
870 GreasePencilDrawingBase *drawing_base = drawings[index];
871 if (drawing_base->type != GP_DRAWING) {
872 continue;
873 }
874
875 GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
876 bke::CurvesGeometry &curves = drawing->wrap().strokes_for_write();
877 if (curves.points_num() == 0) {
878 continue;
879 }
880
881 /* Skip curve when the selection domain already matches, or when there is no selection
882 * at all. */
883 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
884 const std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(
885 ".selection");
886 if ((!meta_data) || (meta_data->domain == domain)) {
887 continue;
888 }
889
890 /* When the new selection domain is 'curve', ensure all curves with a point selection
891 * are selected. */
892 if (domain == bke::AttrDomain::Curve) {
894 }
895
896 /* Convert selection domain. */
897 const GVArray src = *attributes.lookup(".selection", domain);
898 if (src) {
899 const CPPType &type = src.type();
900 void *dst = MEM_malloc_arrayN(attributes.domain_size(domain), type.size(), __func__);
901 src.materialize(dst);
902
903 attributes.remove(".selection");
904 if (!attributes.add(".selection",
905 domain,
906 bke::cpp_type_to_custom_data_type(type),
908 {
909 MEM_freeN(dst);
910 }
911
912 changed = true;
913
914 /* TODO: expand point selection to segments when in 'segment' mode. */
915 }
916 }
917
918 if (changed) {
919 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
920 * attribute for now. */
921 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
922 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
923
925 }
926
927 return OPERATOR_FINISHED;
928}
929
931{
932 PropertyRNA *prop;
933
934 ot->name = "Select Mode";
935 ot->idname = __func__;
936 ot->description = "Change the selection mode for Grease Pencil strokes";
937
940
942
943 ot->prop = prop = RNA_def_enum(
944 ot->srna, "mode", rna_enum_grease_pencil_selectmode_items, 0, "Mode", "");
946}
947
949{
950 const Scene *scene = CTX_data_scene(C);
951 Object *object = CTX_data_active_object(C);
952 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
953 const bool select = !RNA_boolean_get(op->ptr, "deselect");
954 const int material_index = object->actcol - 1;
955
956 if (material_index == -1) {
957 return OPERATOR_CANCELLED;
958 }
959
960 const Vector<MutableDrawingInfo> drawings = retrieve_editable_drawings(*scene, grease_pencil);
961 threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
963
964 IndexMaskMemory memory;
966 *object, info.drawing, material_index, memory);
967 if (strokes.is_empty()) {
968 return;
969 }
970 bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
971 curves, bke::AttrDomain::Curve, CD_PROP_BOOL);
972 index_mask::masked_fill(selection.span.typed<bool>(), select, strokes);
973 selection.finish();
974 });
975
976 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
977 WM_event_add_notifier(C, NC_GEOM | ND_DATA | NA_EDITED, &grease_pencil);
978
979 return OPERATOR_FINISHED;
980}
981
983{
984 /* identifiers */
985 ot->name = "Select Material";
986 ot->idname = "GREASE_PENCIL_OT_material_select";
987 ot->description = "Select/Deselect all Grease Pencil strokes using current material";
988
989 /* callbacks. */
992
993 /* flags. */
995
996 /* props */
997 ot->prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Unselect strokes");
999}
1000
1001} // namespace blender::ed::greasepencil
1002
1016
1018 const ToolSettings *tool_settings)
1019{
1020 const int selectmode = tool_settings->gpencil_selectmode_sculpt;
1023 }
1024 if (selectmode & (GP_SCULPT_MASK_SELECTMODE_STROKE)) {
1026 }
1028}
1029
1031 const ToolSettings *tool_settings)
1032{
1033 const int selectmode = tool_settings->gpencil_selectmode_vertex;
1036 }
1037 if (selectmode & (GP_VERTEX_MASK_SELECTMODE_STROKE)) {
1039 }
1041}
1042
1044 const Object *object)
1045{
1046 if (object->mode & OB_MODE_EDIT) {
1047 return ED_grease_pencil_edit_selection_domain_get(tool_settings);
1048 }
1049 if (object->mode & OB_MODE_SCULPT_GREASE_PENCIL) {
1051 }
1052 if (object->mode & OB_MODE_VERTEX_GREASE_PENCIL) {
1054 }
1056}
1057
1059{
1060 return tool_settings->gpencil_selectmode_edit == GP_SELECTMODE_SEGMENT;
1061}
1062
1067
1072
1074 const Object *object)
1075{
1076 if (object->mode & OB_MODE_EDIT) {
1078 }
1079 if (object->mode & OB_MODE_SCULPT_GREASE_PENCIL) {
1081 }
1082 if (object->mode & OB_MODE_VERTEX_GREASE_PENCIL) {
1084 }
1085 return false;
1086}
1087
1089{
1090 using namespace blender::ed::greasepencil;
1091 WM_operatortype_append(GREASE_PENCIL_OT_select_all);
1092 WM_operatortype_append(GREASE_PENCIL_OT_select_more);
1093 WM_operatortype_append(GREASE_PENCIL_OT_select_less);
1094 WM_operatortype_append(GREASE_PENCIL_OT_select_linked);
1095 WM_operatortype_append(GREASE_PENCIL_OT_select_random);
1096 WM_operatortype_append(GREASE_PENCIL_OT_select_alternate);
1097 WM_operatortype_append(GREASE_PENCIL_OT_select_similar);
1098 WM_operatortype_append(GREASE_PENCIL_OT_select_ends);
1099 WM_operatortype_append(GREASE_PENCIL_OT_set_selection_mode);
1100 WM_operatortype_append(GREASE_PENCIL_OT_material_select);
1101}
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)
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 or normal between and object coordinate space Combine Create a color from its and value channels VERTEX_COLOR
PropertyFlag
Definition RNA_types.hh:201
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
#define NA_EDITED
Definition WM_types.hh:550
#define ND_SPACE_VIEW3D
Definition WM_types.hh:494
#define NC_SPACE
Definition WM_types.hh:359
static unsigned long seed
Definition btSoftBody.h:39
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
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
std::optional< AttributeMetaData > lookup_meta_data(const StringRef attribute_id) const
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
IndexRange range
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 select(const int4 mask, const float4 a, const float4 b)
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)
IndexMask select_adjacent_mask(const bke::CurvesGeometry &curves, const IndexMask &curves_mask, const StringRef attribute_name, const bool deselect, IndexMaskMemory &memory)
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, eCustomDataType create_type, StringRef attribute_name)
static void GREASE_PENCIL_OT_select_linked(wmOperatorType *ot)
static void GREASE_PENCIL_OT_select_more(wmOperatorType *ot)
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 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)
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 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)
Vector< MutableDrawingInfo > retrieve_editable_drawings(const Scene &scene, GreasePencil &grease_pencil)
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)
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)
T distance(const T &a, const T &b)
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
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 bContext::@80 data
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(* 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
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct PointerRNA * ptr
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 *)