Blender V5.0
curves_selection.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_array_utils.hh"
10#include "BLI_assert.h"
11#include "BLI_index_mask.hh"
12#include "BLI_lasso_2d.hh"
13#include "BLI_math_geom.h"
14#include "BLI_rect.h"
15
16#include "BKE_attribute.hh"
17#include "BKE_crazyspace.hh"
18#include "BKE_curves.hh"
19#include "BKE_curves_utils.hh"
20
21#include "ED_curves.hh"
22#include "ED_select_utils.hh"
23#include "ED_view3d.hh"
24#include <optional>
25
26namespace blender::ed::curves {
27
29{
30 const IndexRange curves_range = curves.curves_range();
31 const VArray<int8_t> curve_types = curves.curve_types();
32 const bke::AttributeAccessor attributes = curves.attributes();
33
34 /* Interpolate from points to curves manually as a performance improvement, since we are only
35 * interested in whether any point in each curve is selected. Retrieve meta data since
36 * #lookup_or_default from the attribute API doesn't give the domain of the attribute. */
37 std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(".selection");
38 if (meta_data && meta_data->domain == bke::AttrDomain::Point) {
39 /* Avoid the interpolation from interpolating the attribute to the
40 * curve domain by retrieving the point domain values directly. */
41 const VArray<bool> selection = *attributes.lookup_or_default<bool>(
42 ".selection", bke::AttrDomain::Point, true);
43 const VArray<bool> selection_left = *attributes.lookup_or_default<bool>(
44 ".selection_handle_left", bke::AttrDomain::Point, true);
45 const VArray<bool> selection_right = *attributes.lookup_or_default<bool>(
46 ".selection_handle_right", bke::AttrDomain::Point, true);
47
48 if (selection.is_single() && curves.is_single_type(CURVE_TYPE_POLY)) {
49 return selection.get_internal_single() ? IndexMask(curves_range) : IndexMask();
50 }
51
52 const OffsetIndices points_by_curve = curves.points_by_curve();
54 curves_range, GrainSize(512), memory, [&](const int64_t curve) {
55 const IndexRange points = points_by_curve[curve];
56 /* The curve is selected if any of its points are selected. */
57 Array<bool, 32> point_selection(points.size());
58 selection.materialize_compressed(points, point_selection);
59 bool is_selected = point_selection.as_span().contains(true);
60 if (curve_types[curve] == CURVE_TYPE_BEZIER) {
61 selection_left.materialize_compressed(points, point_selection);
62 is_selected |= point_selection.as_span().contains(true);
63 selection_right.materialize_compressed(points, point_selection);
64 is_selected |= point_selection.as_span().contains(true);
65 }
66 return is_selected;
67 });
68 }
69 const VArray<bool> selection = *attributes.lookup_or_default<bool>(
70 ".selection", bke::AttrDomain::Curve, true);
71 return IndexMask::from_bools(curves_range, selection, memory);
72}
73
75{
76 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
77 return retrieve_selected_curves(curves, memory);
78}
79
81{
83 *curves.attributes().lookup_or_default<bool>(".selection", bke::AttrDomain::Point, true),
84 memory);
85}
86
88 const int handle_display,
89 IndexMaskMemory &memory)
90{
92 curves, CURVE_TYPE_BEZIER, memory);
93
94 Vector<IndexMask> selection_by_attribute;
96 if (selection_name != ".selection" && handle_display == CURVE_HANDLE_NONE) {
97 continue;
98 }
99
100 selection_by_attribute.append(
101 ed::curves::retrieve_selected_points(curves, selection_name, bezier_points, memory));
102 }
103 return IndexMask::from_union(selection_by_attribute, memory);
104}
105
107 StringRef attribute_name,
108 const IndexMask &bezier_points,
109 IndexMaskMemory &memory)
110{
111 const VArray<bool> selected = *curves.attributes().lookup_or_default<bool>(
112 attribute_name, bke::AttrDomain::Point, true);
113
114 if (attribute_name == ".selection") {
115 return IndexMask::from_bools(selected, memory);
116 }
117
118 return IndexMask::from_bools(bezier_points, selected, memory);
119}
120
122{
123 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
124 return retrieve_selected_points(curves, memory);
125}
126
128{
129 static const std::array<StringRef, 1> selection_attribute_names{".selection"};
130 return curves.has_curve_with_type(CURVE_TYPE_BEZIER) ?
132 selection_attribute_names;
133}
134
136{
137 static const std::array<StringRef, 3> selection_attribute_names{
138 ".selection", ".selection_handle_left", ".selection_handle_right"};
139 return selection_attribute_names;
140}
141
143{
144 static const std::array<StringRef, 2> selection_attribute_names{".selection_handle_left",
145 ".selection_handle_right"};
146 const bke::AttributeAccessor attributes = curves.attributes();
147 return (attributes.contains("handle_type_left") && attributes.contains("handle_type_right")) ?
148 selection_attribute_names :
150}
151
153 Span<StringRef> selection_attribute_names)
154{
155 for (const StringRef selection_name : selection_attribute_names) {
156 attributes.remove(selection_name);
157 }
158}
159
160std::optional<Span<float3>> get_selection_attribute_positions(
162 const bke::crazyspace::GeometryDeformation &deformation,
163 const StringRef attribute_name)
164{
165 if (attribute_name == ".selection") {
166 return deformation.positions;
167 }
168 if (attribute_name == ".selection_handle_left") {
169 return curves.handle_positions_left();
170 }
171 if (attribute_name == ".selection_handle_right") {
172 return curves.handle_positions_right();
173 }
175 return {};
176}
177
179 bke::AttrDomain selection_domain)
180{
181 const bke::AttrType create_type = bke::AttrType::Bool;
184 for (const int i : selection_attribute_names.index_range()) {
186 curves, selection_domain, create_type, selection_attribute_names[i]));
187 };
188 return writers;
189}
190
192{
193 for (auto &attribute_writer : attribute_writers) {
194 attribute_writer.finish();
195 }
196}
197
199 MutableSpan<bke::GSpanAttributeWriter> selections, StringRef attribute_name)
200{
201 Span<StringRef> selection_attribute_names = get_curves_all_selection_attribute_names();
202
203 BLI_assert(selection_attribute_names.contains(attribute_name));
204
205 for (const int index : selections.index_range()) {
206 if (attribute_name.size() == selection_attribute_names[index].size()) {
207 return selections[index];
208 }
209 }
211 return selections[0];
212}
213
216 bke::AttrDomain selection_domain,
218{
220 selection_domain);
221 for (bke::GSpanAttributeWriter &selection_writer : selection_writers) {
222 fn(selection_writer);
223 }
224 finish_attribute_writers(selection_writers);
225}
226
229 const bke::crazyspace::GeometryDeformation &deformation,
230 eHandleDisplay handle_display,
231 Span<StringRef> &r_bezier_attribute_names,
232 Span<float3> &r_positions,
233 std::optional<std::array<Span<float3>, 2>> &r_bezier_handle_positions,
234 IndexMaskMemory &r_memory,
235 IndexMask &r_bezier_curves)
236{
237 r_bezier_attribute_names = get_curves_bezier_selection_attribute_names(curves);
238 r_positions = deformation.positions;
239 if (handle_display != eHandleDisplay::CURVE_HANDLE_NONE && r_bezier_attribute_names.size() > 0) {
240 r_bezier_handle_positions = {*curves.handle_positions_left(),
241 *curves.handle_positions_right()};
242 r_bezier_curves = curves.indices_for_curve_type(CURVE_TYPE_BEZIER, r_memory);
243 }
244 else {
245 r_bezier_handle_positions = std::nullopt;
246 }
247}
248
250 const bke::crazyspace::GeometryDeformation &deformation,
251 eHandleDisplay handle_display,
252 SelectionRangeFn range_consumer)
253{
254 Span<StringRef> bezier_attribute_names;
255 Span<float3> positions;
256 std::optional<std::array<Span<float3>, 2>> bezier_handle_positions;
257 IndexMaskMemory memory;
258 IndexMask bezier_curves;
260 deformation,
261 handle_display,
262 bezier_attribute_names,
263 positions,
264 bezier_handle_positions,
265 memory,
266 bezier_curves);
267
268 range_consumer(curves.points_range(), positions, ".selection");
269
270 if (handle_display == eHandleDisplay::CURVE_HANDLE_NONE) {
271 return;
272 }
273
274 OffsetIndices<int> points_by_curve = curves.points_by_curve();
275 for (const int attribute_i : bezier_attribute_names.index_range()) {
276 bezier_curves.foreach_index(GrainSize(512), [&](const int64_t curve) {
277 range_consumer(points_by_curve[curve],
278 (*bezier_handle_positions)[attribute_i],
279 bezier_attribute_names[attribute_i]);
280 });
281 }
282}
283
285 const bke::crazyspace::GeometryDeformation &deformation,
286 eHandleDisplay handle_display,
287 SelectionRangeFn range_consumer)
288{
289 Span<StringRef> bezier_attribute_names;
290 Span<float3> positions;
291 std::optional<std::array<Span<float3>, 2>> bezier_handle_positions;
292 IndexMaskMemory memory;
293 IndexMask bezier_curves;
295 deformation,
296 handle_display,
297 bezier_attribute_names,
298 positions,
299 bezier_handle_positions,
300 memory,
301 bezier_curves);
302
303 range_consumer(curves.curves_range(), positions, ".selection");
304 if (handle_display == eHandleDisplay::CURVE_HANDLE_NONE) {
305 return;
306 }
307
308 for (const int attribute_i : bezier_attribute_names.index_range()) {
309 bezier_curves.foreach_range([&](const IndexRange curves_range) {
310 range_consumer(curves_range,
311 (*bezier_handle_positions)[attribute_i],
312 bezier_attribute_names[attribute_i]);
313 });
314 }
315}
316
318 bke::AttrDomain selection_domain,
319 bke::AttrType create_type,
320 StringRef attribute_name)
321{
322 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
323 if (attributes.contains(attribute_name)) {
324 bke::GSpanAttributeWriter selection_attr = attributes.lookup_for_write_span(attribute_name);
325 /* Check domain type. */
326 if (selection_attr.domain == selection_domain) {
327 return selection_attr;
328 }
329 selection_attr.finish();
330 attributes.remove(attribute_name);
331 }
332 const int domain_size = attributes.domain_size(selection_domain);
333 switch (create_type) {
335 attributes.add(attribute_name,
336 selection_domain,
339 break;
341 attributes.add(attribute_name,
342 selection_domain,
345 break;
346 default:
348 }
349 return attributes.lookup_for_write_span(attribute_name);
350}
351
353{
354 if (selection.type().is<bool>()) {
355 selection.typed<bool>().fill(false);
356 }
357 else if (selection.type().is<float>()) {
358 selection.typed<float>().fill(0.0f);
359 }
360}
361
363{
364 if (selection.type().is<bool>()) {
365 selection.typed<bool>().fill(true);
366 }
367 else if (selection.type().is<float>()) {
368 selection.typed<float>().fill(1.0f);
369 }
370}
371
372void fill_selection(GMutableSpan selection, bool value)
373{
374 if (selection.type().is<bool>()) {
375 selection.typed<bool>().fill(value);
376 }
377 else if (selection.type().is<float>()) {
378 selection.typed<float>().fill(value ? 1.0f : 0.0f);
379 }
380}
381
383{
384 if (selection.type().is<bool>()) {
385 index_mask::masked_fill(selection.typed<bool>(), false, mask);
386 }
387 else if (selection.type().is<float>()) {
388 index_mask::masked_fill(selection.typed<float>(), 0.0f, mask);
389 }
390}
391
393{
394 if (selection.type().is<bool>()) {
395 index_mask::masked_fill(selection.typed<bool>(), true, mask);
396 }
397 else if (selection.type().is<float>()) {
398 index_mask::masked_fill(selection.typed<float>(), 1.0f, mask);
399 }
400}
401
402bool has_anything_selected(const VArray<bool> &varray, const IndexRange range_to_check)
403{
404 return array_utils::contains(varray, range_to_check, true);
405}
406
407bool has_anything_selected(const VArray<bool> &varray, const IndexMask &indices_to_check)
408{
409 return array_utils::contains(varray, indices_to_check, true);
410}
411
413{
414 const VArray<bool> selection = *curves.attributes().lookup<bool>(".selection");
415 return !selection || array_utils::contains(selection, selection.index_range(), true);
416}
417
419{
421 curves, selection_domain, IndexRange(curves.attributes().domain_size(selection_domain)));
422}
423
425 bke::AttrDomain selection_domain,
426 const IndexMask &mask)
427{
428 for (const StringRef selection_name : get_curves_selection_attribute_names(curves)) {
429 const VArray<bool> selection = *curves.attributes().lookup<bool>(selection_name,
430 selection_domain);
431 if (!selection || array_utils::contains(selection, mask, true)) {
432 return true;
433 }
434 }
435 return false;
436}
437
438bool has_anything_selected(const GSpan selection)
439{
440 if (selection.type().is<bool>()) {
441 return selection.typed<bool>().contains(true);
442 }
443 if (selection.type().is<float>()) {
444 for (const float elem : selection.typed<float>()) {
445 if (elem > 0.0f) {
446 return true;
447 }
448 }
449 }
450 return false;
451}
452
453static void invert_selection(MutableSpan<float> selection, const IndexMask &mask)
454{
455 mask.foreach_index_optimized<int64_t>(
456 GrainSize(2048), [&](const int64_t i) { selection[i] = 1.0f - selection[i]; });
457}
458
459static void invert_selection(GMutableSpan selection, const IndexMask &mask)
460{
461 if (selection.type().is<bool>()) {
462 array_utils::invert_booleans(selection.typed<bool>(), mask);
463 }
464 else if (selection.type().is<float>()) {
465 invert_selection(selection.typed<float>(), mask);
466 }
467}
468
469static void invert_selection(GMutableSpan selection)
470{
471 invert_selection(selection, IndexRange(selection.size()));
472}
473
475 const IndexMask &mask,
476 const bke::AttrDomain selection_domain,
477 int action)
478{
479 if (action == SEL_SELECT) {
480 std::optional<IndexRange> range = mask.to_range();
481 if (range.has_value() &&
482 (*range == IndexRange(curves.attributes().domain_size(selection_domain))))
483 {
484 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
485 /* As an optimization, just remove the selection attributes when everything is selected. */
486 remove_selection_attributes(attributes);
487 return;
488 }
489 }
491 curves, selection_domain, [&](bke::GSpanAttributeWriter &selection) {
492 if (action == SEL_SELECT) {
493 fill_selection_true(selection.span, mask);
494 }
495 else if (action == SEL_DESELECT) {
496 fill_selection_false(selection.span, mask);
497 }
498 else if (action == SEL_INVERT) {
499 invert_selection(selection.span, mask);
500 }
501 });
502}
503
504void select_all(bke::CurvesGeometry &curves, const bke::AttrDomain selection_domain, int action)
505{
506 const IndexRange selection(curves.attributes().domain_size(selection_domain));
507 select_all(curves, selection, selection_domain, action);
508}
509
511{
512 const OffsetIndices points_by_curve = curves.points_by_curve();
513 const VArray<int8_t> curve_types = curves.curve_types();
515 const IndexRange selection_writer = IndexRange(1);
516
519
520 curves_mask.foreach_index(GrainSize(256), [&](const int64_t curve) {
521 /* For Bezier curves check all three selection layers ".selection", ".selection_handle_left",
522 * ".selection_handle_right". For other curves only ".selection". */
523 const IndexRange curve_writers = curve_types[curve] == CURVE_TYPE_BEZIER ? all_writers :
524 selection_writer;
525 const IndexRange points = points_by_curve[curve];
526
527 for (const int i : curve_writers) {
528 bke::GSpanAttributeWriter &selection = selection_writers[i];
529 GMutableSpan selection_curve = selection.span.slice(points);
530 if (has_anything_selected(selection_curve)) {
531 fill_selection_true(selection_curve);
532 for (const int j : curve_writers) {
533 if (j == i) {
534 continue;
535 }
536 fill_selection_true(selection_writers[j].span.slice(points));
537 }
538 return;
539 }
540 }
541 });
542 finish_attribute_writers(selection_writers);
543}
544
546{
547 select_linked(curves, curves.curves_range());
548}
549
551 const IndexMask &curves_mask,
552 const bool deselect_ends)
553{
555 return;
556 }
557
558 const OffsetIndices points_by_curve = curves.points_by_curve();
559 const VArray<bool> cyclic = curves.cyclic();
562
563 curves_mask.foreach_index([&](const int64_t curve) {
564 const IndexRange points = points_by_curve[curve];
565
566 bool anything_selected = false;
567
568 for (bke::GSpanAttributeWriter &writer : selection_writers) {
569 const bool writer_has_anything_selected = has_anything_selected(writer.span.slice(points));
570 anything_selected = anything_selected || writer_has_anything_selected;
571 }
572 if (!anything_selected) {
573 return;
574 }
575
576 for (bke::GSpanAttributeWriter &writer : selection_writers) {
577 MutableSpan<bool> selection_typed = writer.span.typed<bool>();
578
579 const int half_of_size = points.size() / 2;
580 const IndexRange selected = points.shift(deselect_ends ? 1 : 0);
581 const IndexRange deselected = points.shift(deselect_ends ? 0 : 1);
582 for (const int i : IndexRange(half_of_size)) {
583 const int index = i * 2;
584 selection_typed[selected[index]] = true;
585 selection_typed[deselected[index]] = false;
586 }
587
588 selection_typed[points.first()] = !deselect_ends;
589 const bool end_parity_to_selected = bool(points.size() % 2);
590 const bool selected_end = cyclic[curve] || end_parity_to_selected;
591 selection_typed[points.last()] = !deselect_ends && selected_end;
592 /* Selected last one require to deselect pre-last one point which is not first. */
593 const IndexRange curve_body = points.drop_front(1).drop_back(1);
594 if (!deselect_ends && cyclic[curve] && !curve_body.is_empty()) {
595 selection_typed[curve_body.last()] = false;
596 }
597 }
598 });
599
600 finish_attribute_writers(selection_writers);
601}
602
603void select_alternate(bke::CurvesGeometry &curves, const bool deselect_ends)
604{
605 select_alternate(curves, curves.curves_range(), deselect_ends);
606}
607
609 const IndexMask &curves_mask,
610 const bool deselect)
611{
612 const OffsetIndices points_by_curve = curves.points_by_curve();
615 const VArray<bool> cyclic = curves.cyclic();
616
617 if (deselect) {
618 invert_selection(selection.span);
619 }
620
621 if (selection.span.type().is<bool>()) {
622 MutableSpan<bool> selection_typed = selection.span.typed<bool>();
623 curves_mask.foreach_index([&](const int64_t curve) {
624 const IndexRange points = points_by_curve[curve];
625 const int first_point = points.first();
626 const int last_point = points.last();
627
628 /* Handle all cases in the forward direction. */
629 for (int point = first_point; point < last_point; point++) {
630 if (!selection_typed[point] && selection_typed[point + 1]) {
631 selection_typed[point] = true;
632 }
633 }
634
635 /* Handle all cases in the backwards direction. */
636 for (int point = last_point; point > first_point; point--) {
637 if (!selection_typed[point] && selection_typed[point - 1]) {
638 selection_typed[point] = true;
639 }
640 }
641 if (deselect) {
642 if (!selection_typed[first_point]) {
643 selection_typed[first_point] = true;
644 }
645 if (!selection_typed[last_point]) {
646 selection_typed[last_point] = true;
647 }
648 }
649 /* Handle cyclic curve case. */
650 if (cyclic[curve]) {
651 if (selection_typed[first_point] != selection_typed[last_point]) {
652 selection_typed[first_point] = true;
653 selection_typed[last_point] = true;
654 }
655 }
656 });
657 }
658 else if (selection.span.type().is<float>()) {
659 MutableSpan<float> selection_typed = selection.span.typed<float>();
660 curves_mask.foreach_index([&](const int64_t curve) {
661 const IndexRange points = points_by_curve[curve];
662 const int first_point = points.first();
663 const int last_point = points.last();
664
665 /* Handle all cases in the forward direction. */
666 for (int point_i = first_point; point_i < last_point; point_i++) {
667 if ((selection_typed[point_i] == 0.0f) && (selection_typed[point_i + 1] > 0.0f)) {
668 selection_typed[point_i] = 1.0f;
669 }
670 }
671
672 /* Handle all cases in the backwards direction. */
673 for (int point_i = last_point; point_i > first_point; point_i--) {
674 if ((selection_typed[point_i] == 0.0f) && (selection_typed[point_i - 1] > 0.0f)) {
675 selection_typed[point_i] = 1.0f;
676 }
677 }
678
679 /* Handle cyclic curve case. */
680 if (cyclic[curve]) {
681 if (selection_typed[first_point] != selection_typed[last_point]) {
682 selection_typed[first_point] = 1.0f;
683 selection_typed[last_point] = 1.0f;
684 }
685 }
686 });
687 }
688
689 if (deselect) {
690 invert_selection(selection.span);
691 }
692
693 selection.finish();
694}
695
696void select_adjacent(bke::CurvesGeometry &curves, const bool deselect)
697{
698 select_adjacent(curves, curves.curves_range(), deselect);
699}
700
702 const int index,
703 const eSelectOp sel_op)
704{
705 if (selection.type().is<bool>()) {
706 MutableSpan<bool> selection_typed = selection.typed<bool>();
707 switch (sel_op) {
708 case SEL_OP_ADD:
709 case SEL_OP_SET:
710 selection_typed[index] = true;
711 break;
712 case SEL_OP_SUB:
713 selection_typed[index] = false;
714 break;
715 case SEL_OP_XOR:
716 selection_typed[index] = !selection_typed[index];
717 break;
718 default:
719 break;
720 }
721 }
722 else if (selection.type().is<float>()) {
723 MutableSpan<float> selection_typed = selection.typed<float>();
724 switch (sel_op) {
725 case SEL_OP_ADD:
726 case SEL_OP_SET:
727 selection_typed[index] = 1.0f;
728 break;
729 case SEL_OP_SUB:
730 selection_typed[index] = 0.0f;
731 break;
732 case SEL_OP_XOR:
733 selection_typed[index] = 1.0f - selection_typed[index];
734 break;
735 default:
736 break;
737 }
738 }
739}
740
742{
743 if (a.distance_sq < b.distance_sq) {
744 return a;
745 }
746 return b;
747}
748
749static std::optional<FindClosestData> find_closest_point_to_screen_co(
750 const ARegion *region,
751 const Span<float3> positions,
752 const float4x4 &projection,
753 const IndexMask &points_mask,
754 const float2 mouse_pos,
755 const float radius,
756 const FindClosestData &initial_closest)
757{
758 const float radius_sq = pow2f(radius);
759 const FindClosestData new_closest_data = threading::parallel_reduce(
760 points_mask.index_range(),
761 1024,
762 initial_closest,
763 [&](const IndexRange range, const FindClosestData &init) {
764 FindClosestData best_match = init;
765 points_mask.slice(range).foreach_index([&](const int point) {
766 const float3 &pos = positions[point];
767 const float2 pos_proj = ED_view3d_project_float_v2_m4(region, pos, projection);
768
769 const float distance_proj_sq = math::distance_squared(pos_proj, mouse_pos);
770 if (distance_proj_sq > radius_sq || distance_proj_sq > best_match.distance_sq) {
771 return;
772 }
773
774 best_match = {point, distance_proj_sq};
775 });
776 return best_match;
777 },
779
780 if (new_closest_data.distance_sq < initial_closest.distance_sq) {
781 return new_closest_data;
782 }
783
784 return {};
785}
786
787static std::optional<FindClosestData> find_closest_curve_to_screen_co(
788 const ARegion *region,
789 const OffsetIndices<int> points_by_curve,
790 const Span<float3> positions,
791 const VArray<bool> &cyclic,
792 const float4x4 &projection,
793 const IndexMask &curves_mask,
794 const float2 mouse_pos,
795 float radius,
796 const FindClosestData &initial_closest)
797{
798 const float radius_sq = pow2f(radius);
799
800 const FindClosestData new_closest_data = threading::parallel_reduce(
801 curves_mask.index_range(),
802 256,
803 initial_closest,
804 [&](const IndexRange range, const FindClosestData &init) {
805 FindClosestData best_match = init;
806 curves_mask.slice(range).foreach_index([&](const int curve) {
807 const IndexRange points = points_by_curve[curve];
808
809 if (points.size() == 1) {
810 const float3 &pos = positions[points.first()];
811 const float2 pos_proj = ED_view3d_project_float_v2_m4(region, pos, projection);
812
813 const float distance_proj_sq = math::distance_squared(pos_proj, mouse_pos);
814 if (distance_proj_sq > radius_sq || distance_proj_sq > best_match.distance_sq) {
815 return;
816 }
817
818 best_match = {curve, distance_proj_sq};
819 return;
820 }
821
822 auto process_segment = [&](const int segment_i, const int next_i) {
823 const float3 &pos1 = positions[segment_i];
824 const float3 &pos2 = positions[next_i];
825 const float2 pos1_proj = ED_view3d_project_float_v2_m4(region, pos1, projection);
826 const float2 pos2_proj = ED_view3d_project_float_v2_m4(region, pos2, projection);
827
828 const float distance_proj_sq = dist_squared_to_line_segment_v2(
829 mouse_pos, pos1_proj, pos2_proj);
830 if (distance_proj_sq > radius_sq || distance_proj_sq > best_match.distance_sq) {
831 return;
832 }
833
834 best_match = {curve, distance_proj_sq};
835 };
836 for (const int segment_i : points.drop_back(1)) {
837 process_segment(segment_i, segment_i + 1);
838 }
839 if (cyclic[curve]) {
840 process_segment(points.last(), points.first());
841 }
842 });
843 return best_match;
844 },
845 closer_elem);
846
847 if (new_closest_data.distance_sq < initial_closest.distance_sq) {
848 return new_closest_data;
849 }
850
851 return {};
852}
853
854std::optional<FindClosestData> closest_elem_find_screen_space(
855 const ViewContext &vc,
856 const OffsetIndices<int> points_by_curve,
857 const Span<float3> positions,
858 const VArray<bool> &cyclic,
859 const float4x4 &projection,
860 const IndexMask &mask,
861 const bke::AttrDomain domain,
862 const int2 coord,
863 const FindClosestData &initial_closest)
864{
865 switch (domain) {
868 positions,
869 projection,
870 mask,
871 float2(coord),
873 initial_closest);
876 points_by_curve,
877 positions,
878 cyclic,
879 projection,
880 mask,
881 float2(coord),
883 initial_closest);
884 default:
886 return {};
887 }
888}
889
890bool select_box(const ViewContext &vc,
892 const bke::crazyspace::GeometryDeformation &deformation,
893 const float4x4 &projection,
894 const IndexMask &selection_mask,
895 const IndexMask &bezier_mask,
896 const bke::AttrDomain selection_domain,
897 const rcti &rect,
898 const eSelectOp sel_op)
899{
901 selection_domain);
902
903 bool changed = false;
904 if (sel_op == SEL_OP_SET) {
905 for (bke::GSpanAttributeWriter &selection : selection_writers) {
906 fill_selection_false(selection.span, selection_mask);
907 };
908 changed = true;
909 }
910
911 if (selection_domain == bke::AttrDomain::Point) {
913 curves,
914 deformation,
916 [&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
917 const IndexMask &mask = (selection_attribute_name == ".selection") ? selection_mask :
918 bezier_mask;
919 mask.slice_content(range).foreach_index(GrainSize(1024), [&](const int point) {
920 const float2 pos_proj = ED_view3d_project_float_v2_m4(
921 vc.region, positions[point], projection);
922 if (BLI_rcti_isect_pt_v(&rect, int2(pos_proj))) {
923 apply_selection_operation_at_index(
924 selection_attribute_writer_by_name(selection_writers, selection_attribute_name)
925 .span,
926 point,
927 sel_op);
928 changed = true;
929 }
930 });
931 });
932 }
933 else if (selection_domain == bke::AttrDomain::Curve) {
934 const OffsetIndices points_by_curve = curves.points_by_curve();
935 const VArray<bool> cyclic = curves.cyclic();
936 foreach_selectable_curve_range(
937 curves,
938 deformation,
939 eHandleDisplay(vc.v3d->overlay.handle_display),
940 [&](const IndexRange range,
941 const Span<float3> positions,
942 StringRef /* selection_attribute_name */) {
943 const IndexMask &mask = selection_mask;
944 mask.slice_content(range).foreach_index(GrainSize(512), [&](const int curve) {
945 const IndexRange points = points_by_curve[curve];
946 if (points.size() == 1) {
947 const float2 pos_proj = ED_view3d_project_float_v2_m4(
948 vc.region, positions[points.first()], projection);
949 if (BLI_rcti_isect_pt_v(&rect, int2(pos_proj))) {
950 for (bke::GSpanAttributeWriter &selection : selection_writers) {
951 apply_selection_operation_at_index(selection.span, curve, sel_op);
952 };
953 changed = true;
954 }
955 return;
956 }
957 auto process_segment = [&](const int segment_i, const int next_i) {
958 const float3 &pos1 = positions[segment_i];
959 const float3 &pos2 = positions[next_i];
960 const float2 pos1_proj = ED_view3d_project_float_v2_m4(vc.region, pos1, projection);
961 const float2 pos2_proj = ED_view3d_project_float_v2_m4(vc.region, pos2, projection);
962
963 if (BLI_rcti_isect_segment(&rect, int2(pos1_proj), int2(pos2_proj))) {
964 for (bke::GSpanAttributeWriter &selection : selection_writers) {
965 apply_selection_operation_at_index(selection.span, curve, sel_op);
966 };
967 changed = true;
968 return true;
969 }
970 return false;
971 };
972 bool segment_selected = false;
973 for (const int segment_i : points.drop_back(1)) {
974 if (process_segment(segment_i, segment_i + 1)) {
975 segment_selected = true;
976 break;
977 }
978 }
979 if (!segment_selected && cyclic[curve]) {
980 process_segment(points.last(), points.first());
981 }
982 });
983 });
984 }
985 finish_attribute_writers(selection_writers);
986 return changed;
987}
988
991 const bke::crazyspace::GeometryDeformation &deformation,
992 const float4x4 &projection,
993 const IndexMask &selection_mask,
994 const IndexMask &bezier_mask,
995 const bke::AttrDomain selection_domain,
996 const Span<int2> lasso_coords,
997 const eSelectOp sel_op)
998{
999 rcti bbox;
1000 BLI_lasso_boundbox(&bbox, lasso_coords);
1002 selection_domain);
1003 bool changed = false;
1004 if (sel_op == SEL_OP_SET) {
1005 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1006 fill_selection_false(selection.span, selection_mask);
1007 };
1008 changed = true;
1009 }
1010
1011 if (selection_domain == bke::AttrDomain::Point) {
1013 curves,
1014 deformation,
1016 [&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
1017 const IndexMask &mask = (selection_attribute_name == ".selection") ? selection_mask :
1018 bezier_mask;
1019 mask.slice_content(range).foreach_index(GrainSize(1024), [&](const int point) {
1020 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1021 vc.region, positions[point], projection);
1022 /* Check the lasso bounding box first as an optimization. */
1023 if (BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
1024 BLI_lasso_is_point_inside(
1025 lasso_coords, int(pos_proj.x), int(pos_proj.y), IS_CLIPPED))
1026 {
1027 apply_selection_operation_at_index(
1028 selection_attribute_writer_by_name(selection_writers, selection_attribute_name)
1029 .span,
1030 point,
1031 sel_op);
1032 changed = true;
1033 }
1034 });
1035 });
1036 }
1037 else if (selection_domain == bke::AttrDomain::Curve) {
1038 const OffsetIndices points_by_curve = curves.points_by_curve();
1039 const VArray<bool> cyclic = curves.cyclic();
1040 foreach_selectable_curve_range(
1041 curves,
1042 deformation,
1043 eHandleDisplay(vc.v3d->overlay.handle_display),
1044 [&](const IndexRange range,
1045 const Span<float3> positions,
1046 StringRef /* selection_attribute_name */) {
1047 const IndexMask &mask = selection_mask;
1048 mask.slice_content(range).foreach_index(GrainSize(512), [&](const int curve) {
1049 const IndexRange points = points_by_curve[curve];
1050 if (points.size() == 1) {
1051 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1052 vc.region, positions[points.first()], projection);
1053 /* Check the lasso bounding box first as an optimization. */
1054 if (BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
1055 BLI_lasso_is_point_inside(
1056 lasso_coords, int(pos_proj.x), int(pos_proj.y), IS_CLIPPED))
1057 {
1058 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1059 apply_selection_operation_at_index(selection.span, curve, sel_op);
1060 }
1061 changed = true;
1062 }
1063 return;
1064 }
1065 auto process_segment = [&](const int segment_i, const int next_i) {
1066 const float3 &pos1 = positions[segment_i];
1067 const float3 &pos2 = positions[next_i];
1068 const float2 pos1_proj = ED_view3d_project_float_v2_m4(vc.region, pos1, projection);
1069 const float2 pos2_proj = ED_view3d_project_float_v2_m4(vc.region, pos2, projection);
1070
1071 /* Check the lasso bounding box first as an optimization. */
1072 if (BLI_rcti_isect_segment(&bbox, int2(pos1_proj), int2(pos2_proj)) &&
1073 BLI_lasso_is_edge_inside(lasso_coords,
1074 int(pos1_proj.x),
1075 int(pos1_proj.y),
1076 int(pos2_proj.x),
1077 int(pos2_proj.y),
1078 IS_CLIPPED))
1079 {
1080 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1081 apply_selection_operation_at_index(selection.span, curve, sel_op);
1082 }
1083 changed = true;
1084 return true;
1085 }
1086 return false;
1087 };
1088 bool segment_selected = false;
1089 for (const int segment_i : points.drop_back(cyclic[curve] ? 0 : 1)) {
1090 if (process_segment(segment_i, segment_i + 1)) {
1091 segment_selected = true;
1092 break;
1093 }
1094 }
1095 if (!segment_selected && cyclic[curve]) {
1096 process_segment(points.last(), points.first());
1097 }
1098 });
1099 });
1100 }
1101 finish_attribute_writers(selection_writers);
1102 return changed;
1103}
1104
1107 const bke::crazyspace::GeometryDeformation &deformation,
1108 const float4x4 &projection,
1109 const IndexMask &selection_mask,
1110 const IndexMask &bezier_mask,
1111 const bke::AttrDomain selection_domain,
1112 const int2 coord,
1113 const float radius,
1114 const eSelectOp sel_op)
1115{
1116 const float radius_sq = pow2f(radius);
1118 selection_domain);
1119 bool changed = false;
1120 if (sel_op == SEL_OP_SET) {
1121 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1122 fill_selection_false(selection.span, selection_mask);
1123 };
1124 changed = true;
1125 }
1126
1127 if (selection_domain == bke::AttrDomain::Point) {
1129 curves,
1130 deformation,
1132 [&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
1133 const IndexMask &mask = (selection_attribute_name == ".selection") ? selection_mask :
1134 bezier_mask;
1135 mask.slice_content(range).foreach_index(GrainSize(1024), [&](const int point) {
1136 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1137 vc.region, positions[point], projection);
1138 if (math::distance_squared(pos_proj, float2(coord)) <= radius_sq) {
1139 apply_selection_operation_at_index(
1140 selection_attribute_writer_by_name(selection_writers, selection_attribute_name)
1141 .span,
1142 point,
1143 sel_op);
1144 changed = true;
1145 }
1146 });
1147 });
1148 }
1149 else if (selection_domain == bke::AttrDomain::Curve) {
1150 const OffsetIndices points_by_curve = curves.points_by_curve();
1151 const VArray<bool> cyclic = curves.cyclic();
1152 foreach_selectable_curve_range(
1153 curves,
1154 deformation,
1155 eHandleDisplay(vc.v3d->overlay.handle_display),
1156 [&](const IndexRange range,
1157 const Span<float3> positions,
1158 StringRef /* selection_attribute_name */) {
1159 const IndexMask &mask = selection_mask;
1160 mask.slice_content(range).foreach_index(GrainSize(512), [&](const int curve) {
1161 const IndexRange points = points_by_curve[curve];
1162 if (points.size() == 1) {
1163 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1164 vc.region, positions[points.first()], projection);
1165 if (math::distance_squared(pos_proj, float2(coord)) <= radius_sq) {
1166 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1167 apply_selection_operation_at_index(selection.span, curve, sel_op);
1168 }
1169 changed = true;
1170 }
1171 return;
1172 }
1173 auto process_segments = [&](const int segment_i, const int next_i) {
1174 const float3 &pos1 = positions[segment_i];
1175 const float3 &pos2 = positions[next_i];
1176 const float2 pos1_proj = ED_view3d_project_float_v2_m4(vc.region, pos1, projection);
1177 const float2 pos2_proj = ED_view3d_project_float_v2_m4(vc.region, pos2, projection);
1178
1179 const float distance_proj_sq = dist_squared_to_line_segment_v2(
1180 float2(coord), pos1_proj, pos2_proj);
1181 if (distance_proj_sq <= radius_sq) {
1182 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1183 apply_selection_operation_at_index(selection.span, curve, sel_op);
1184 }
1185 changed = true;
1186 return true;
1187 }
1188 return false;
1189 };
1190 bool segment_selected = false;
1191 for (const int segment_i : points.drop_back(1)) {
1192 if (process_segments(segment_i, segment_i + 1)) {
1193 segment_selected = true;
1194 break;
1195 }
1196 }
1197 if (!segment_selected && cyclic[curve]) {
1198 process_segments(points.last(), points.first());
1199 }
1200 });
1201 });
1202 }
1203 finish_attribute_writers(selection_writers);
1204 return changed;
1205}
1206
1207template<typename PointSelectFn, typename LineSelectFn>
1209 const IndexMask &mask,
1210 const bke::AttrDomain selection_domain,
1211 IndexMaskMemory &memory,
1212 PointSelectFn &&point_predicate,
1213 LineSelectFn &&line_predicate)
1214{
1215 const OffsetIndices points_by_curve = curves.points_by_curve();
1216 const VArraySpan<bool> cyclic = curves.cyclic();
1217
1218 if (selection_domain == bke::AttrDomain::Point) {
1220 mask.slice_content(curves.points_range()), GrainSize(1024), memory, point_predicate);
1221 }
1222 if (selection_domain == bke::AttrDomain::Curve) {
1223 return IndexMask::from_predicate(mask.slice_content(curves.curves_range()),
1224 GrainSize(512),
1225 memory,
1226 [&](const int curve) -> bool {
1227 const IndexRange points = points_by_curve[curve];
1228 const bool is_cyclic = cyclic[curve];
1229
1230 /* Single-point curve can still be selected in curve mode.
1231 */
1232 if (points.size() == 1) {
1233 return point_predicate(points.first());
1234 }
1235
1236 for (const int point : points.drop_back(1)) {
1237 if (line_predicate(curve, point, point + 1)) {
1238 return true;
1239 }
1240 }
1241 if (is_cyclic) {
1242 if (line_predicate(curve, points.last(), points.first()))
1243 {
1244 return true;
1245 }
1246 }
1247 return false;
1248 });
1249 }
1250 return {};
1251}
1252
1254 const IndexMask &curves_mask,
1255 const StringRef attribute_name,
1256 const bool deselect,
1257 IndexMaskMemory &memory)
1258{
1259 const OffsetIndices points_by_curve = curves.points_by_curve();
1260 const VArray<bool> cyclic = curves.cyclic();
1261
1262 VArraySpan<bool> selection = *curves.attributes().lookup_or_default<bool>(
1263 attribute_name, bke::AttrDomain::Point, true);
1264
1265 /* Mask of points that are not selected yet but adjacent. */
1266 Array<bool> changed_points(curves.points_num());
1267
1268 auto is_point_changed1 = [&](const int point, const int neighbor) {
1269 return deselect ? (selection[point] && !selection[neighbor]) :
1270 (!selection[point] && selection[neighbor]);
1271 };
1272 auto is_point_changed2 = [&](const int point, const int neighbor1, const int neighbor2) {
1273 return deselect ? (selection[point] && (!selection[neighbor1] || !selection[neighbor2])) :
1274 (!selection[point] && (selection[neighbor1] || selection[neighbor2]));
1275 };
1276
1277 curves_mask.foreach_index([&](const int64_t curve) {
1278 const IndexRange points = points_by_curve[curve];
1279 if (points.size() == 1) {
1280 /* Single point curve does not add anything to the mask. */
1281 return;
1282 }
1283
1284 if (cyclic[curve]) {
1285 changed_points[points.first()] = is_point_changed2(
1286 points.first(), points.last(), points.first() + 1);
1287 for (const int point : points.drop_front(1).drop_back(1)) {
1288 changed_points[point] = is_point_changed2(point, point - 1, point + 1);
1289 }
1290 changed_points[points.last()] = is_point_changed2(
1291 points.last(), points.last() - 1, points.first());
1292 }
1293 else {
1294 changed_points[points.first()] = is_point_changed1(points.first(), points.first() + 1);
1295 for (const int point : points.drop_front(1).drop_back(1)) {
1296 changed_points[point] = is_point_changed2(point, point - 1, point + 1);
1297 }
1298 changed_points[points.last()] = is_point_changed1(points.last(), points.last() - 1);
1299 }
1300 });
1301
1302 return IndexMask::from_bools(changed_points, memory);
1303}
1304
1306 const StringRef attribute_name,
1307 const bool deselect,
1308 IndexMaskMemory &memory)
1309{
1310 return select_adjacent_mask(curves, curves.curves_range(), attribute_name, deselect, memory);
1311}
1312
1315 const bke::crazyspace::GeometryDeformation &deformation,
1316 const float4x4 &projection,
1317 const IndexMask &selection_mask,
1318 const IndexMask &bezier_mask,
1319 const bke::AttrDomain selection_domain,
1320 const StringRef attribute_name,
1321 const rcti &rect,
1322 IndexMaskMemory &memory)
1323{
1324 const std::optional<Span<float3>> positions_opt = get_selection_attribute_positions(
1325 curves, deformation, attribute_name);
1326 if (!positions_opt) {
1327 return {};
1328 }
1329 const Span<float3> positions = *positions_opt;
1330
1331 auto point_predicate = [&](const int point) {
1332 const float2 pos_proj = ED_view3d_project_float_v2_m4(vc.region, positions[point], projection);
1333 /* Check the lasso bounding box first as an optimization. */
1334 return BLI_rcti_isect_pt_v(&rect, int2(pos_proj));
1335 };
1336 auto line_predicate = [&](const int /*curve*/, const int point, const int next_point_i) {
1337 const float2 pos_proj = ED_view3d_project_float_v2_m4(vc.region, positions[point], projection);
1338 const float2 next_pos_proj = ED_view3d_project_float_v2_m4(
1339 vc.region, positions[next_point_i], projection);
1340 return BLI_rcti_isect_segment(&rect, int2(pos_proj), int2(next_pos_proj));
1341 };
1342
1343 const IndexMask &mask = (selection_domain != bke::AttrDomain::Point ||
1344 attribute_name == ".selection") ?
1345 selection_mask :
1346 bezier_mask;
1348 curves, mask, selection_domain, memory, point_predicate, line_predicate);
1349}
1350
1353 const bke::crazyspace::GeometryDeformation &deformation,
1354 const float4x4 &projection,
1355 const IndexMask &selection_mask,
1356 const IndexMask &bezier_mask,
1357 const bke::AttrDomain selection_domain,
1358 const StringRef attribute_name,
1359 const Span<int2> lasso_coords,
1360 IndexMaskMemory &memory)
1361{
1362 rcti bbox;
1363 BLI_lasso_boundbox(&bbox, lasso_coords);
1364 const std::optional<Span<float3>> positions_opt = get_selection_attribute_positions(
1365 curves, deformation, attribute_name);
1366 if (!positions_opt) {
1367 return {};
1368 }
1369 const Span<float3> positions = *positions_opt;
1370
1371 auto point_predicate = [&](const int point) {
1372 const float2 pos_proj = ED_view3d_project_float_v2_m4(vc.region, positions[point], projection);
1373 /* Check the lasso bounding box first as an optimization. */
1374 return BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
1375 BLI_lasso_is_point_inside(lasso_coords, int(pos_proj.x), int(pos_proj.y), IS_CLIPPED);
1376 };
1377 auto line_predicate = [&](const int /*curve*/, const int point, const int next_point_i) {
1378 const float2 pos_proj = ED_view3d_project_float_v2_m4(vc.region, positions[point], projection);
1379 const float2 next_pos_proj = ED_view3d_project_float_v2_m4(
1380 vc.region, positions[next_point_i], projection);
1381 return BLI_rcti_isect_segment(&bbox, int2(pos_proj), int2(next_pos_proj)) &&
1382 BLI_lasso_is_edge_inside(lasso_coords,
1383 int(pos_proj.x),
1384 int(pos_proj.y),
1385 int(next_pos_proj.x),
1386 int(next_pos_proj.y),
1387 IS_CLIPPED);
1388 };
1389
1390 const IndexMask &mask = (selection_domain != bke::AttrDomain::Point ||
1391 attribute_name == ".selection") ?
1392 selection_mask :
1393 bezier_mask;
1395 curves, mask, selection_domain, memory, point_predicate, line_predicate);
1396}
1397
1400 const bke::crazyspace::GeometryDeformation &deformation,
1401 const float4x4 &projection,
1402 const IndexMask &selection_mask,
1403 const IndexMask &bezier_mask,
1404 const bke::AttrDomain selection_domain,
1405 const StringRef attribute_name,
1406 const int2 coord,
1407 const float radius,
1408 IndexMaskMemory &memory)
1409{
1410 const float radius_sq = pow2f(radius);
1411 const std::optional<Span<float3>> positions_opt = get_selection_attribute_positions(
1412 curves, deformation, attribute_name);
1413 if (!positions_opt) {
1414 return {};
1415 }
1416 const Span<float3> positions = *positions_opt;
1417
1418 auto point_predicate = [&](const int point) {
1419 const float2 pos_proj = ED_view3d_project_float_v2_m4(vc.region, positions[point], projection);
1420 const float distance_proj_sq = math::distance_squared(pos_proj, float2(coord));
1421 return distance_proj_sq <= radius_sq;
1422 };
1423 auto line_predicate = [&](const int /*curve*/, const int point, const int next_point_i) {
1424 const float2 pos_proj = ED_view3d_project_float_v2_m4(vc.region, positions[point], projection);
1425 const float2 next_pos_proj = ED_view3d_project_float_v2_m4(
1426 vc.region, positions[next_point_i], projection);
1427 const float distance_proj_sq = dist_squared_to_line_segment_v2(
1428 float2(coord), pos_proj, next_pos_proj);
1429 return distance_proj_sq <= radius_sq;
1430 };
1431
1432 const IndexMask &mask = (selection_domain != bke::AttrDomain::Point ||
1433 attribute_name == ".selection") ?
1434 selection_mask :
1435 bezier_mask;
1437 curves, mask, selection_domain, memory, point_predicate, line_predicate);
1438}
1439
1440} // namespace blender::ed::curves
Low-level operations for curves.
Low-level operations for curves.
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
bool BLI_lasso_is_point_inside(blender::Span< blender::int2 > mcoords, int sx, int sy, int error_value)
void BLI_lasso_boundbox(rcti *rect, blender::Span< blender::int2 > mcoords)
bool BLI_lasso_is_edge_inside(blender::Span< blender::int2 > mcoords, int x0, int y0, int x1, int y1, int error_value)
MINLINE float pow2f(float x)
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:291
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
bool BLI_rcti_isect_segment(const struct rcti *rect, const int s1[2], const int s2[2])
eHandleDisplay
@ CURVE_HANDLE_NONE
eSelectOp
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_XOR
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
blender::float2 ED_view3d_project_float_v2_m4(const ARegion *region, const float co[3], const blender::float4x4 &mat)
float ED_view3d_select_dist_px()
#define IS_CLIPPED
Definition ED_view3d.hh:252
long long int int64_t
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
Span< T > as_span() const
Definition BLI_array.hh:243
bool is() const
GMutableSpan slice(const int64_t start, int64_t size) const
const CPPType & type() const
MutableSpan< T > typed() const
Span< T > typed() const
const CPPType & type() const
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_union(const IndexMask &mask_a, const IndexMask &mask_b, IndexMaskMemory &memory)
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
constexpr int64_t first() const
constexpr IndexRange shift(int64_t n) const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr bool is_empty() const
constexpr IndexRange index_range() const
constexpr IndexRange drop_front(int64_t n) const
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool contains(const T &value) const
Definition BLI_span.hh:277
constexpr int64_t size() const
IndexRange index_range() const
void materialize_compressed(const IndexMask &mask, MutableSpan< T > r_span) const
static VArray from_single(T value, const int64_t size)
void append(const T &value)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
bool contains(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)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
void foreach_index(Fn &&fn) const
static bool is_cyclic(const Nurb *nu)
VecBase< float, 2 > float2
VecBase< int, 2 > int2
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void invert_booleans(MutableSpan< bool > span)
bool contains(const VArray< bool > &varray, const IndexMask &indices_to_check, bool value)
IndexMask curve_type_point_selection(const bke::CurvesGeometry &curves, CurveType curve_type, IndexMaskMemory &memory)
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)
void foreach_selectable_point_range(const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, eHandleDisplay handle_display, SelectionRangeFn range_consumer)
IndexMask select_circle_mask(const ViewContext &vc, const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const StringRef attribute_name, const int2 coord, const float radius, IndexMaskMemory &memory)
static bool has_anything_selected(const Span< Curves * > curves_ids)
IndexMask retrieve_all_selected_points(const bke::CurvesGeometry &curves, const int handle_display, IndexMaskMemory &memory)
IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
void remove_selection_attributes(bke::MutableAttributeAccessor &attributes, Span< StringRef > selection_attribute_names)
static std::optional< FindClosestData > find_closest_point_to_screen_co(const ARegion *region, const Span< float3 > positions, const float4x4 &projection, const IndexMask &points_mask, const float2 mouse_pos, const float radius, const FindClosestData &initial_closest)
static Vector< bke::GSpanAttributeWriter > init_selection_writers(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain)
void select_all(bke::CurvesGeometry &curves, const IndexMask &mask, const bke::AttrDomain selection_domain, int action)
IndexMask select_mask_from_predicates(const bke::CurvesGeometry &curves, const IndexMask &mask, const bke::AttrDomain selection_domain, IndexMaskMemory &memory, PointSelectFn &&point_predicate, LineSelectFn &&line_predicate)
void select_alternate(bke::CurvesGeometry &curves, const IndexMask &curves_mask, const bool deselect_ends)
void foreach_selectable_curve_range(const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, eHandleDisplay handle_display, SelectionRangeFn range_consumer)
void select_adjacent(bke::CurvesGeometry &curves, const IndexMask &curves_mask, const bool deselect)
void foreach_selection_attribute_writer(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, blender::FunctionRef< void(bke::GSpanAttributeWriter &selection)> fn)
static bke::GSpanAttributeWriter & selection_attribute_writer_by_name(MutableSpan< bke::GSpanAttributeWriter > selections, StringRef attribute_name)
static void invert_selection(MutableSpan< float > selection, const IndexMask &mask)
Span< StringRef > get_curves_bezier_selection_attribute_names(const bke::CurvesGeometry &curves)
void fill_selection_false(GMutableSpan selection)
IndexMask select_lasso_mask(const ViewContext &vc, const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const StringRef attribute_name, const Span< int2 > lasso_coords, IndexMaskMemory &memory)
bool select_circle(const ViewContext &vc, bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const int2 coord, const float radius, const eSelectOp sel_op)
static void finish_attribute_writers(MutableSpan< bke::GSpanAttributeWriter > attribute_writers)
IndexMask select_adjacent_mask(const bke::CurvesGeometry &curves, const IndexMask &curves_mask, const StringRef attribute_name, const bool deselect, IndexMaskMemory &memory)
void fill_selection_true(GMutableSpan selection)
Span< StringRef > get_curves_selection_attribute_names(const bke::CurvesGeometry &curves)
void fill_selection(GMutableSpan selection, bool value)
std::optional< FindClosestData > closest_elem_find_screen_space(const ViewContext &vc, const OffsetIndices< int > points_by_curve, const Span< float3 > positions, const VArray< bool > &cyclic, const float4x4 &projection, const IndexMask &mask, const bke::AttrDomain domain, const int2 coord, const FindClosestData &initial_closest)
Span< StringRef > get_curves_all_selection_attribute_names()
static void init_selectable_foreach(const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, eHandleDisplay handle_display, Span< StringRef > &r_bezier_attribute_names, Span< float3 > &r_positions, std::optional< std::array< Span< float3 >, 2 > > &r_bezier_handle_positions, IndexMaskMemory &r_memory, IndexMask &r_bezier_curves)
static std::optional< FindClosestData > find_closest_curve_to_screen_co(const ARegion *region, const OffsetIndices< int > points_by_curve, const Span< float3 > positions, const VArray< bool > &cyclic, const float4x4 &projection, const IndexMask &curves_mask, const float2 mouse_pos, float radius, const FindClosestData &initial_closest)
IndexMask select_box_mask(const ViewContext &vc, const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const StringRef attribute_name, const rcti &rect, IndexMaskMemory &memory)
bool select_lasso(const ViewContext &vc, bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const Span< int2 > lasso_coords, const eSelectOp sel_op)
std::optional< Span< float3 > > get_selection_attribute_positions(const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const StringRef attribute_name)
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, bke::AttrType create_type, StringRef attribute_name)
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
static FindClosestData closer_elem(const FindClosestData &a, const FindClosestData &b)
FunctionRef< void( IndexRange range, Span< float3 > positions, StringRef selection_attribute_name)> SelectionRangeFn
Definition ED_curves.hh:194
bool select_box(const ViewContext &vc, bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const rcti &rect, const eSelectOp sel_op)
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:151
MatBase< float, 4, 4 > float4x4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
static void init(bNodeTree *, bNode *node)
CurvesGeometry geometry
View3DOverlay overlay
ARegion * region
Definition ED_view3d.hh:77
View3D * v3d
Definition ED_view3d.hh:78
i
Definition text_draw.cc:230