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