Blender V4.3
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
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_rand.hh"
15#include "BLI_rect.h"
16
17#include "BKE_attribute.hh"
18#include "BKE_crazyspace.hh"
19#include "BKE_curves.hh"
20
21#include "ED_curves.hh"
22#include "ED_object.hh"
23#include "ED_select_utils.hh"
24#include "ED_view3d.hh"
25
26namespace blender::ed::curves {
27
29{
30 const IndexRange curves_range = curves.curves_range();
31 const bke::AttributeAccessor attributes = curves.attributes();
32
33 /* Interpolate from points to curves manually as a performance improvement, since we are only
34 * interested in whether any point in each curve is selected. Retrieve meta data since
35 * #lookup_or_default from the attribute API doesn't give the domain of the attribute. */
36 std::optional<bke::AttributeMetaData> meta_data = attributes.lookup_meta_data(".selection");
37 if (meta_data && meta_data->domain == bke::AttrDomain::Point) {
38 /* Avoid the interpolation from interpolating the attribute to the
39 * curve domain by retrieving the point domain values directly. */
40 const VArray<bool> selection = *attributes.lookup_or_default<bool>(
41 ".selection", bke::AttrDomain::Point, true);
42 if (selection.is_single()) {
43 return selection.get_internal_single() ? IndexMask(curves_range) : IndexMask();
44 }
45 const OffsetIndices points_by_curve = curves.points_by_curve();
47 curves_range, GrainSize(512), memory, [&](const int64_t curve_i) {
48 const IndexRange points = points_by_curve[curve_i];
49 /* The curve is selected if any of its points are selected. */
50 Array<bool, 32> point_selection(points.size());
51 selection.materialize_compressed(points, point_selection);
52 return point_selection.as_span().contains(true);
53 });
54 }
55 const VArray<bool> selection = *attributes.lookup_or_default<bool>(
56 ".selection", bke::AttrDomain::Curve, true);
57 return IndexMask::from_bools(curves_range, selection, memory);
58}
59
61{
62 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
63 return retrieve_selected_curves(curves, memory);
64}
65
67{
68 return retrieve_selected_points(curves, ".selection", memory);
69}
70
72 StringRef attribute_name,
73 IndexMaskMemory &memory)
74{
76 *curves.attributes().lookup_or_default<bool>(attribute_name, bke::AttrDomain::Point, true),
77 memory);
78}
79
81{
82 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
83 return retrieve_selected_points(curves, memory);
84}
85
87{
88 static const std::array<StringRef, 1> selection_attribute_names{".selection"};
89 const bke::AttributeAccessor attributes = curves.attributes();
90 return (attributes.contains("handle_type_left") && attributes.contains("handle_type_right")) ?
92 selection_attribute_names;
93}
94
96{
97 static const std::array<StringRef, 3> selection_attribute_names{
98 ".selection", ".selection_handle_left", ".selection_handle_right"};
99 return selection_attribute_names;
100}
101
103{
104 static const std::array<StringRef, 2> selection_attribute_names{".selection_handle_left",
105 ".selection_handle_right"};
106 const bke::AttributeAccessor attributes = curves.attributes();
107 return (attributes.contains("handle_type_left") && attributes.contains("handle_type_right")) ?
108 selection_attribute_names :
110}
111
113 Span<StringRef> selection_attribute_names)
114{
115 for (const StringRef selection_name : selection_attribute_names) {
116 attributes.remove(selection_name);
117 }
118}
119
121 const bke::CurvesGeometry &curves,
122 const bke::crazyspace::GeometryDeformation &deformation,
123 const StringRef attribute_name)
124{
125 if (attribute_name == ".selection") {
126 return deformation.positions;
127 }
128 if (attribute_name == ".selection_handle_left") {
129 return curves.handle_positions_left();
130 }
131 if (attribute_name == ".selection_handle_right") {
132 return curves.handle_positions_right();
133 }
135 return {};
136}
137
139 bke::AttrDomain selection_domain)
140{
141 const eCustomDataType create_type = CD_PROP_BOOL;
142 Span<StringRef> selection_attribute_names = get_curves_selection_attribute_names(curves);
144 for (const int i : selection_attribute_names.index_range()) {
146 curves, selection_domain, create_type, selection_attribute_names[i]));
147 };
148 return writers;
149}
150
152{
153 for (auto &attribute_writer : attribute_writers) {
154 attribute_writer.finish();
155 }
156}
157
159 MutableSpan<bke::GSpanAttributeWriter> selections, StringRef attribute_name)
160{
161 Span<StringRef> selection_attribute_names = get_curves_all_selection_attribute_names();
162
163 BLI_assert(selection_attribute_names.contains(attribute_name));
164
165 for (const int index : selections.index_range()) {
166 if (attribute_name.size() == selection_attribute_names[index].size()) {
167 return selections[index];
168 }
169 }
171 return selections[0];
172}
173
175 bke::CurvesGeometry &curves,
176 bke::AttrDomain selection_domain,
178{
180 selection_domain);
181 for (bke::GSpanAttributeWriter &selection_writer : selection_writers) {
182 fn(selection_writer);
183 }
184 finish_attribute_writers(selection_writers);
185}
186
188 const bke::crazyspace::GeometryDeformation &deformation,
189 Span<StringRef> &r_bezier_attribute_names,
190 Span<float3> &r_positions,
191 std::array<Span<float3>, 2> &r_bezier_handle_positions,
192 IndexMaskMemory &r_memory,
193 IndexMask &r_bezier_curves)
194{
195 r_bezier_attribute_names = get_curves_bezier_selection_attribute_names(curves);
196 r_positions = deformation.positions;
197 if (r_bezier_attribute_names.size() > 0) {
198 r_bezier_handle_positions[0] = curves.handle_positions_left();
199 r_bezier_handle_positions[1] = curves.handle_positions_right();
200 r_bezier_curves = curves.indices_for_curve_type(CURVE_TYPE_BEZIER, r_memory);
201 }
202}
203
205 const bke::crazyspace::GeometryDeformation &deformation,
206 SelectionRangeFn range_consumer)
207{
208 Span<StringRef> bezier_attribute_names;
209 Span<float3> positions;
210 std::array<Span<float3>, 2> bezier_handle_positions;
211 IndexMaskMemory memory;
212 IndexMask bezier_curves;
214 deformation,
215 bezier_attribute_names,
216 positions,
217 bezier_handle_positions,
218 memory,
219 bezier_curves);
220
221 range_consumer(curves.points_range(), positions, ".selection");
222
223 OffsetIndices<int> points_by_curve = curves.points_by_curve();
224 for (const int attribute_i : bezier_attribute_names.index_range()) {
225 bezier_curves.foreach_index(GrainSize(512), [&](const int64_t curve_i) {
226 range_consumer(points_by_curve[curve_i],
227 bezier_handle_positions[attribute_i],
228 bezier_attribute_names[attribute_i]);
229 });
230 }
231}
232
234 const bke::crazyspace::GeometryDeformation &deformation,
235 SelectionRangeFn range_consumer)
236{
237 Span<StringRef> bezier_attribute_names;
238 Span<float3> positions;
239 std::array<Span<float3>, 2> bezier_handle_positions;
240 IndexMaskMemory memory;
241 IndexMask bezier_curves;
243 deformation,
244 bezier_attribute_names,
245 positions,
246 bezier_handle_positions,
247 memory,
248 bezier_curves);
249
250 range_consumer(curves.curves_range(), positions, ".selection");
251
252 for (const int attribute_i : bezier_attribute_names.index_range()) {
253 bezier_curves.foreach_range([&](const IndexRange curves_range) {
254 range_consumer(
255 curves_range, bezier_handle_positions[attribute_i], bezier_attribute_names[attribute_i]);
256 });
257 }
258}
259
261 bke::AttrDomain selection_domain,
262 eCustomDataType create_type,
263 StringRef attribute_name)
264{
265 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
266 if (attributes.contains(attribute_name)) {
267 bke::GSpanAttributeWriter selection_attr = attributes.lookup_for_write_span(attribute_name);
268 /* Check domain type. */
269 if (selection_attr.domain == selection_domain) {
270 return selection_attr;
271 }
272 selection_attr.finish();
273 attributes.remove(attribute_name);
274 }
275 const int domain_size = attributes.domain_size(selection_domain);
276 switch (create_type) {
277 case CD_PROP_BOOL:
278 attributes.add(attribute_name,
279 selection_domain,
282 break;
283 case CD_PROP_FLOAT:
284 attributes.add(attribute_name,
285 selection_domain,
288 break;
289 default:
291 }
292 return attributes.lookup_for_write_span(attribute_name);
293}
294
296{
297 if (selection.type().is<bool>()) {
298 selection.typed<bool>().fill(false);
299 }
300 else if (selection.type().is<float>()) {
301 selection.typed<float>().fill(0.0f);
302 }
303}
304
306{
307 if (selection.type().is<bool>()) {
308 selection.typed<bool>().fill(true);
309 }
310 else if (selection.type().is<float>()) {
311 selection.typed<float>().fill(1.0f);
312 }
313}
314
315void fill_selection(GMutableSpan selection, bool value)
316{
317 if (selection.type().is<bool>()) {
318 selection.typed<bool>().fill(value);
319 }
320 else if (selection.type().is<float>()) {
321 selection.typed<float>().fill(value ? 1.0f : 0.0f);
322 }
323}
324
325void fill_selection_false(GMutableSpan selection, const IndexMask &mask)
326{
327 if (selection.type().is<bool>()) {
328 index_mask::masked_fill(selection.typed<bool>(), false, mask);
329 }
330 else if (selection.type().is<float>()) {
331 index_mask::masked_fill(selection.typed<float>(), 0.0f, mask);
332 }
333}
334
335void fill_selection_true(GMutableSpan selection, const IndexMask &mask)
336{
337 if (selection.type().is<bool>()) {
338 index_mask::masked_fill(selection.typed<bool>(), true, mask);
339 }
340 else if (selection.type().is<float>()) {
341 index_mask::masked_fill(selection.typed<float>(), 1.0f, mask);
342 }
343}
344
345static bool contains(const VArray<bool> &varray,
346 const IndexMask &indices_to_check,
347 const bool value)
348{
349 const CommonVArrayInfo info = varray.common_info();
351 return *static_cast<const bool *>(info.data) == value;
352 }
354 const Span<bool> span(static_cast<const bool *>(info.data), varray.size());
356 indices_to_check.index_range(),
357 4096,
358 false,
359 [&](const IndexRange range, const bool init) {
360 if (init) {
361 return init;
362 }
363 const IndexMask sliced_mask = indices_to_check.slice(range);
364 if (std::optional<IndexRange> range = sliced_mask.to_range()) {
365 return span.slice(*range).contains(value);
366 }
367 for (const int64_t segment_i : IndexRange(sliced_mask.segments_num())) {
368 const IndexMaskSegment segment = sliced_mask.segment(segment_i);
369 for (const int i : segment) {
370 if (span[i] == value) {
371 return true;
372 }
373 }
374 }
375 return false;
376 },
377 std::logical_or());
378 }
380 indices_to_check.index_range(),
381 2048,
382 false,
383 [&](const IndexRange range, const bool init) {
384 if (init) {
385 return init;
386 }
387 constexpr int64_t MaxChunkSize = 512;
388 const int64_t slice_end = range.one_after_last();
389 for (int64_t start = range.start(); start < slice_end; start += MaxChunkSize) {
390 const int64_t end = std::min<int64_t>(start + MaxChunkSize, slice_end);
391 const int64_t size = end - start;
392 const IndexMask sliced_mask = indices_to_check.slice(start, size);
393 std::array<bool, MaxChunkSize> values;
394 auto values_end = values.begin() + size;
395 varray.materialize_compressed(sliced_mask, values);
396 if (std::find(values.begin(), values_end, value) != values_end) {
397 return true;
398 }
399 }
400 return false;
401 },
402 std::logical_or());
403}
404
405static bool contains(const VArray<bool> &varray, const IndexRange range_to_check, const bool value)
406{
407 return contains(varray, IndexMask(range_to_check), value);
408}
409
410bool has_anything_selected(const VArray<bool> &varray, const IndexRange range_to_check)
411{
412 return contains(varray, range_to_check, true);
413}
414
415bool has_anything_selected(const VArray<bool> &varray, const IndexMask &indices_to_check)
416{
417 return contains(varray, indices_to_check, true);
418}
419
421{
422 const VArray<bool> selection = *curves.attributes().lookup<bool>(".selection");
423 return !selection || contains(selection, selection.index_range(), true);
424}
425
426bool has_anything_selected(const bke::CurvesGeometry &curves, bke::AttrDomain selection_domain)
427{
429 curves, selection_domain, IndexRange(curves.attributes().domain_size(selection_domain)));
430}
431
433 bke::AttrDomain selection_domain,
434 const IndexMask &mask)
435{
436 for (const StringRef selection_name : get_curves_selection_attribute_names(curves)) {
437 const VArray<bool> selection = *curves.attributes().lookup<bool>(selection_name,
438 selection_domain);
439 if (!selection || contains(selection, mask, true)) {
440 return true;
441 }
442 }
443 return false;
444}
445
446bool has_anything_selected(const GSpan selection)
447{
448 if (selection.type().is<bool>()) {
449 return selection.typed<bool>().contains(true);
450 }
451 else if (selection.type().is<float>()) {
452 for (const float elem : selection.typed<float>()) {
453 if (elem > 0.0f) {
454 return true;
455 }
456 }
457 }
458 return false;
459}
460
462{
463 threading::parallel_for(selection.index_range(), 2048, [&](IndexRange range) {
464 for (const int i : range) {
465 selection[i] = 1.0f - selection[i];
466 }
467 });
468}
469
470static void invert_selection(GMutableSpan selection)
471{
472 if (selection.type().is<bool>()) {
473 array_utils::invert_booleans(selection.typed<bool>());
474 }
475 else if (selection.type().is<float>()) {
476 invert_selection(selection.typed<float>());
477 }
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
497 const IndexMask &mask,
498 const bke::AttrDomain selection_domain,
499 int action)
500{
501 if (action == SEL_SELECT) {
502 std::optional<IndexRange> range = mask.to_range();
503 if (range.has_value() &&
504 (*range == IndexRange(curves.attributes().domain_size(selection_domain))))
505 {
506 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
507 /* As an optimization, just remove the selection attributes when everything is selected. */
508 remove_selection_attributes(attributes);
509 return;
510 }
511 }
513 curves, selection_domain, [&](bke::GSpanAttributeWriter &selection) {
514 if (action == SEL_SELECT) {
515 fill_selection_true(selection.span, mask);
516 }
517 else if (action == SEL_DESELECT) {
518 fill_selection_false(selection.span, mask);
519 }
520 else if (action == SEL_INVERT) {
521 invert_selection(selection.span, mask);
522 }
523 });
524}
525
526void select_all(bke::CurvesGeometry &curves, const bke::AttrDomain selection_domain, int action)
527{
528 const IndexRange selection(curves.attributes().domain_size(selection_domain));
529 select_all(curves, selection, selection_domain, action);
530}
531
532void select_linked(bke::CurvesGeometry &curves, const IndexMask &curves_mask)
533{
534 const OffsetIndices points_by_curve = curves.points_by_curve();
535 const VArray<int8_t> curve_types = curves.curve_types();
536
538 curves, bke::AttrDomain::Point);
539
540 curves_mask.foreach_index(GrainSize(256), [&](const int64_t curve_i) {
541 for (const int i : selection_writers.index_range()) {
542 bke::GSpanAttributeWriter &selection = selection_writers[i];
543 GMutableSpan selection_curve = selection.span.slice(points_by_curve[curve_i]);
544 if (has_anything_selected(selection_curve)) {
545 fill_selection_true(selection_curve);
546 for (const int j : selection_writers.index_range()) {
547 if (j == i) {
548 continue;
549 }
550 fill_selection_true(selection_writers[j].span.slice(points_by_curve[curve_i]));
551 }
552 return;
553 }
554 if (curve_types[curve_i] != CURVE_TYPE_BEZIER) {
555 return;
556 }
557 }
558 });
559 finish_attribute_writers(selection_writers);
560}
561
563{
564 select_linked(curves, curves.curves_range());
565}
566
568 const IndexMask &curves_mask,
569 const bool deselect_ends)
570{
571 if (!has_anything_selected(curves)) {
572 return;
573 }
574
575 const OffsetIndices points_by_curve = curves.points_by_curve();
577 curves, bke::AttrDomain::Point, CD_PROP_BOOL);
578 const VArray<bool> cyclic = curves.cyclic();
579
580 MutableSpan<bool> selection_typed = selection.span.typed<bool>();
581 curves_mask.foreach_index([&](const int64_t curve_i) {
582 const IndexRange points = points_by_curve[curve_i];
583 if (!has_anything_selected(selection.span.slice(points))) {
584 return;
585 }
586
587 const int half_of_size = points.size() / 2;
588 const IndexRange selected = points.shift(deselect_ends ? 1 : 0);
589 const IndexRange deselected = points.shift(deselect_ends ? 0 : 1);
590 for (const int i : IndexRange(half_of_size)) {
591 const int index = i * 2;
592 selection_typed[selected[index]] = true;
593 selection_typed[deselected[index]] = false;
594 }
595
596 selection_typed[points.first()] = !deselect_ends;
597 const bool end_parity_to_selected = bool(points.size() % 2);
598 const bool selected_end = cyclic[curve_i] || end_parity_to_selected;
599 selection_typed[points.last()] = !deselect_ends && selected_end;
600
601 /* Selected last one require to deselect pre-last one point which is not first. */
602 const IndexRange curve_body = points.drop_front(1).drop_back(1);
603 if (!deselect_ends && cyclic[curve_i] && !curve_body.is_empty()) {
604 selection_typed[curve_body.last()] = false;
605 }
606 });
607
608 selection.finish();
609}
610
611void select_alternate(bke::CurvesGeometry &curves, const bool deselect_ends)
612{
613 select_alternate(curves, curves.curves_range(), deselect_ends);
614}
615
617 const IndexMask &curves_mask,
618 const bool deselect)
619{
620 const OffsetIndices points_by_curve = curves.points_by_curve();
622 curves, bke::AttrDomain::Point, CD_PROP_BOOL);
623 const VArray<bool> cyclic = curves.cyclic();
624
625 if (deselect) {
626 invert_selection(selection.span);
627 }
628
629 if (selection.span.type().is<bool>()) {
630 MutableSpan<bool> selection_typed = selection.span.typed<bool>();
631 curves_mask.foreach_index([&](const int64_t curve_i) {
632 const IndexRange points = points_by_curve[curve_i];
633 const int first_point = points.first();
634 const int last_point = points.last();
635
636 /* Handle all cases in the forward direction. */
637 for (int point_i = first_point; point_i < last_point; point_i++) {
638 if (!selection_typed[point_i] && selection_typed[point_i + 1]) {
639 selection_typed[point_i] = true;
640 }
641 }
642
643 /* Handle all cases in the backwards direction. */
644 for (int point_i = last_point; point_i > first_point; point_i--) {
645 if (!selection_typed[point_i] && selection_typed[point_i - 1]) {
646 selection_typed[point_i] = true;
647 }
648 }
649 if (deselect) {
650 if (!selection_typed[first_point]) {
651 selection_typed[first_point] = true;
652 }
653 if (!selection_typed[last_point]) {
654 selection_typed[last_point] = true;
655 }
656 }
657 /* Handle cyclic curve case. */
658 if (cyclic[curve_i]) {
659 if (selection_typed[first_point] != selection_typed[last_point]) {
660 selection_typed[first_point] = true;
661 selection_typed[last_point] = true;
662 }
663 }
664 });
665 }
666 else if (selection.span.type().is<float>()) {
667 MutableSpan<float> selection_typed = selection.span.typed<float>();
668 curves_mask.foreach_index([&](const int64_t curve_i) {
669 const IndexRange points = points_by_curve[curve_i];
670 const int first_point = points.first();
671 const int last_point = points.last();
672
673 /* Handle all cases in the forward direction. */
674 for (int point_i = first_point; point_i < last_point; point_i++) {
675 if ((selection_typed[point_i] == 0.0f) && (selection_typed[point_i + 1] > 0.0f)) {
676 selection_typed[point_i] = 1.0f;
677 }
678 }
679
680 /* Handle all cases in the backwards direction. */
681 for (int point_i = last_point; point_i > first_point; point_i--) {
682 if ((selection_typed[point_i] == 0.0f) && (selection_typed[point_i - 1] > 0.0f)) {
683 selection_typed[point_i] = 1.0f;
684 }
685 }
686
687 /* Handle cyclic curve case. */
688 if (cyclic[curve_i]) {
689 if (selection_typed[first_point] != selection_typed[last_point]) {
690 selection_typed[first_point] = 1.0f;
691 selection_typed[last_point] = 1.0f;
692 }
693 }
694 });
695 }
696
697 if (deselect) {
698 invert_selection(selection.span);
699 }
700
701 selection.finish();
702}
703
704void select_adjacent(bke::CurvesGeometry &curves, const bool deselect)
705{
706 select_adjacent(curves, curves.curves_range(), deselect);
707}
708
710 const int index,
711 const eSelectOp sel_op)
712{
713 if (selection.type().is<bool>()) {
714 MutableSpan<bool> selection_typed = selection.typed<bool>();
715 switch (sel_op) {
716 case SEL_OP_ADD:
717 case SEL_OP_SET:
718 selection_typed[index] = true;
719 break;
720 case SEL_OP_SUB:
721 selection_typed[index] = false;
722 break;
723 case SEL_OP_XOR:
724 selection_typed[index] = !selection_typed[index];
725 break;
726 default:
727 break;
728 }
729 }
730 else if (selection.type().is<float>()) {
731 MutableSpan<float> selection_typed = selection.typed<float>();
732 switch (sel_op) {
733 case SEL_OP_ADD:
734 case SEL_OP_SET:
735 selection_typed[index] = 1.0f;
736 break;
737 case SEL_OP_SUB:
738 selection_typed[index] = 0.0f;
739 break;
740 case SEL_OP_XOR:
741 selection_typed[index] = 1.0f - selection_typed[index];
742 break;
743 default:
744 break;
745 }
746 }
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 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 point_indicies_range, const FindClosestData &init) {
764 FindClosestData best_match = init;
765 for (const int index : point_indicies_range) {
766 const int point_i = points_mask[index];
767 const float3 pos = positions[point_i];
768
769 /* Find the position of the point in screen space. */
770 const float2 pos_proj = ED_view3d_project_float_v2_m4(region, pos, projection);
771
772 const float distance_proj_sq = math::distance_squared(pos_proj, mouse_pos);
773 if (distance_proj_sq > radius_sq ||
774 distance_proj_sq > best_match.distance * best_match.distance)
775 {
776 /* Ignore the point because it's too far away or there is already a better point. */
777 continue;
778 }
779
780 FindClosestData better_candidate;
781 better_candidate.index = point_i;
782 better_candidate.distance = std::sqrt(distance_proj_sq);
783
784 best_match = better_candidate;
785 }
786 return best_match;
787 },
788 [](const FindClosestData &a, const FindClosestData &b) {
789 if (a.distance < b.distance) {
790 return a;
791 }
792 return b;
793 });
794
795 if (new_closest_data.distance < initial_closest.distance) {
796 return new_closest_data;
797 }
798
799 return {};
800}
801
802static std::optional<FindClosestData> find_closest_curve_to_screen_co(
803 const ARegion *region,
804 const OffsetIndices<int> points_by_curve,
805 const Span<float3> positions,
806 const VArray<bool> &cyclic,
807 const float4x4 &projection,
808 const IndexMask &curves_mask,
809 const float2 mouse_pos,
810 float radius,
811 const FindClosestData &initial_closest)
812{
813 const float radius_sq = pow2f(radius);
814
815 const FindClosestData new_closest_data = threading::parallel_reduce(
816 curves_mask.index_range(),
817 256,
818 initial_closest,
819 [&](const IndexRange curves_indices_range, const FindClosestData &init) {
820 FindClosestData best_match = init;
821 for (const int index : curves_indices_range) {
822 const int curve_i = curves_mask[index];
823 const IndexRange points = points_by_curve[curve_i];
824 if (points.size() == 1) {
825 const float3 pos = positions[points.first()];
826
827 /* Find the position of the point in screen space. */
828 const float2 pos_proj = ED_view3d_project_float_v2_m4(region, pos, projection);
829
830 const float distance_proj_sq = math::distance_squared(pos_proj, mouse_pos);
831 if (distance_proj_sq > radius_sq ||
832 distance_proj_sq > best_match.distance * best_match.distance)
833 {
834 /* Ignore the point because it's too far away or there is already a better point.
835 */
836 continue;
837 }
838
839 FindClosestData better_candidate;
840 better_candidate.index = curve_i;
841 better_candidate.distance = std::sqrt(distance_proj_sq);
842
843 best_match = better_candidate;
844 continue;
845 }
846
847 auto process_segment = [&](const int segment_i, const int next_i) {
848 const float3 pos1 = positions[segment_i];
849 const float3 pos2 = positions[next_i];
850 const float2 pos1_proj = ED_view3d_project_float_v2_m4(region, pos1, projection);
851 const float2 pos2_proj = ED_view3d_project_float_v2_m4(region, pos2, projection);
852
853 const float distance_proj_sq = dist_squared_to_line_segment_v2(
854 mouse_pos, pos1_proj, pos2_proj);
855 if (distance_proj_sq > radius_sq ||
856 distance_proj_sq > best_match.distance * best_match.distance)
857 {
858 /* Ignore the segment because it's too far away or there is already a better point.
859 */
860 return;
861 }
862
863 FindClosestData better_candidate;
864 better_candidate.index = curve_i;
865 better_candidate.distance = std::sqrt(distance_proj_sq);
866
867 best_match = better_candidate;
868 };
869 for (const int segment_i : points.drop_back(1)) {
870 process_segment(segment_i, segment_i + 1);
871 }
872 if (cyclic[curve_i]) {
873 process_segment(points.last(), points.first());
874 }
875 }
876 return best_match;
877 },
878 [](const FindClosestData &a, const FindClosestData &b) {
879 if (a.distance < b.distance) {
880 return a;
881 }
882 return b;
883 });
884
885 if (new_closest_data.distance < initial_closest.distance) {
886 return new_closest_data;
887 }
888
889 return {};
890}
891
892std::optional<FindClosestData> closest_elem_find_screen_space(
893 const ViewContext &vc,
894 const OffsetIndices<int> points_by_curve,
895 const Span<float3> positions,
896 const VArray<bool> &cyclic,
897 const float4x4 &projection,
898 const IndexMask &mask,
899 const bke::AttrDomain domain,
900 const int2 coord,
901 const FindClosestData &initial_closest)
902{
903 switch (domain) {
904 case bke::AttrDomain::Point:
906 positions,
907 projection,
908 mask,
909 float2(coord),
911 initial_closest);
912 case bke::AttrDomain::Curve:
914 points_by_curve,
915 positions,
916 cyclic,
917 projection,
918 mask,
919 float2(coord),
921 initial_closest);
922 default:
924 return {};
925 }
926}
927
928bool select_box(const ViewContext &vc,
929 bke::CurvesGeometry &curves,
930 const bke::crazyspace::GeometryDeformation &deformation,
931 const float4x4 &projection,
932 const IndexMask &selection_mask,
933 const IndexMask &bezier_mask,
934 const bke::AttrDomain selection_domain,
935 const rcti &rect,
936 const eSelectOp sel_op)
937{
939 selection_domain);
940
941 bool changed = false;
942 if (sel_op == SEL_OP_SET) {
943 for (bke::GSpanAttributeWriter &selection : selection_writers) {
944 fill_selection_false(selection.span, selection_mask);
945 };
946 changed = true;
947 }
948
949 if (selection_domain == bke::AttrDomain::Point) {
951 curves,
952 deformation,
953 [&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
954 const IndexMask &mask = (selection_attribute_name == ".selection") ? selection_mask :
955 bezier_mask;
956 mask.slice_content(range).foreach_index(GrainSize(1024), [&](const int point_i) {
957 const float2 pos_proj = ED_view3d_project_float_v2_m4(
958 vc.region, positions[point_i], projection);
959 if (BLI_rcti_isect_pt_v(&rect, int2(pos_proj))) {
961 selection_attribute_writer_by_name(selection_writers, selection_attribute_name)
962 .span,
963 point_i,
964 sel_op);
965 changed = true;
966 }
967 });
968 });
969 }
970 else if (selection_domain == bke::AttrDomain::Curve) {
971 const OffsetIndices points_by_curve = curves.points_by_curve();
972 const VArray<bool> cyclic = curves.cyclic();
974 curves,
975 deformation,
976 [&](const IndexRange range,
977 const Span<float3> positions,
978 StringRef /* selection_attribute_name */) {
979 const IndexMask &mask = selection_mask;
980 mask.slice_content(range).foreach_index(GrainSize(512), [&](const int curve_i) {
981 const IndexRange points = points_by_curve[curve_i];
982 if (points.size() == 1) {
983 const float2 pos_proj = ED_view3d_project_float_v2_m4(
984 vc.region, positions[points.first()], projection);
985 if (BLI_rcti_isect_pt_v(&rect, int2(pos_proj))) {
986 for (bke::GSpanAttributeWriter &selection : selection_writers) {
987 apply_selection_operation_at_index(selection.span, curve_i, sel_op);
988 };
989 changed = true;
990 }
991 return;
992 }
993 auto process_segment = [&](const int segment_i, const int next_i) {
994 const float3 pos1 = positions[segment_i];
995 const float3 pos2 = positions[next_i];
996
997 const float2 pos1_proj = ED_view3d_project_float_v2_m4(vc.region, pos1, projection);
998 const float2 pos2_proj = ED_view3d_project_float_v2_m4(vc.region, pos2, projection);
999
1000 if (BLI_rcti_isect_segment(&rect, int2(pos1_proj), int2(pos2_proj))) {
1001 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1002 apply_selection_operation_at_index(selection.span, curve_i, sel_op);
1003 };
1004 changed = true;
1005 return true;
1006 }
1007 return false;
1008 };
1009 bool segment_selected = false;
1010 for (const int segment_i : points.drop_back(1)) {
1011 if (process_segment(segment_i, segment_i + 1)) {
1012 segment_selected = true;
1013 break;
1014 }
1015 }
1016 if (!segment_selected && cyclic[curve_i]) {
1017 process_segment(points.last(), points.first());
1018 }
1019 });
1020 });
1021 }
1022 finish_attribute_writers(selection_writers);
1023 return changed;
1024}
1025
1027 bke::CurvesGeometry &curves,
1028 const bke::crazyspace::GeometryDeformation &deformation,
1029 const float4x4 &projection_matrix,
1030 const IndexMask &selection_mask,
1031 const IndexMask &bezier_mask,
1032 const bke::AttrDomain selection_domain,
1033 const Span<int2> lasso_coords,
1034 const eSelectOp sel_op)
1035{
1036 rcti bbox;
1037 BLI_lasso_boundbox(&bbox, lasso_coords);
1039 selection_domain);
1040 bool changed = false;
1041 if (sel_op == SEL_OP_SET) {
1042 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1043 fill_selection_false(selection.span, selection_mask);
1044 };
1045 changed = true;
1046 }
1047
1048 if (selection_domain == bke::AttrDomain::Point) {
1050 curves,
1051 deformation,
1052 [&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
1053 const IndexMask &mask = (selection_attribute_name == ".selection") ? selection_mask :
1054 bezier_mask;
1055 mask.slice_content(range).foreach_index(GrainSize(1024), [&](const int point_i) {
1056 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1057 vc.region, positions[point_i], projection_matrix);
1058 /* Check the lasso bounding box first as an optimization. */
1059 if (BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
1061 lasso_coords, int(pos_proj.x), int(pos_proj.y), IS_CLIPPED))
1062 {
1064 selection_attribute_writer_by_name(selection_writers, selection_attribute_name)
1065 .span,
1066 point_i,
1067 sel_op);
1068 changed = true;
1069 }
1070 });
1071 });
1072 }
1073 else if (selection_domain == bke::AttrDomain::Curve) {
1074 const OffsetIndices points_by_curve = curves.points_by_curve();
1075 const VArray<bool> cyclic = curves.cyclic();
1077 curves,
1078 deformation,
1079 [&](const IndexRange range,
1080 const Span<float3> positions,
1081 StringRef /* selection_attribute_name */) {
1082 const IndexMask &mask = selection_mask;
1083 mask.slice_content(range).foreach_index(GrainSize(512), [&](const int curve_i) {
1084 const IndexRange points = points_by_curve[curve_i];
1085 if (points.size() == 1) {
1086 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1087 vc.region, positions[points.first()], projection_matrix);
1088 /* Check the lasso bounding box first as an optimization. */
1089 if (BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
1090 BLI_lasso_is_point_inside(
1091 lasso_coords, int(pos_proj.x), int(pos_proj.y), IS_CLIPPED))
1092 {
1093 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1094 apply_selection_operation_at_index(selection.span, curve_i, sel_op);
1095 }
1096 changed = true;
1097 }
1098 return;
1099 }
1100 auto process_segment = [&](const int segment_i, const int next_i) {
1101 const float3 pos1 = positions[segment_i];
1102 const float3 pos2 = positions[next_i];
1103
1104 const float2 pos1_proj = ED_view3d_project_float_v2_m4(
1105 vc.region, pos1, projection_matrix);
1106 const float2 pos2_proj = ED_view3d_project_float_v2_m4(
1107 vc.region, pos2, projection_matrix);
1108
1109 /* Check the lasso bounding box first as an optimization. */
1110 if (BLI_rcti_isect_segment(&bbox, int2(pos1_proj), int2(pos2_proj)) &&
1111 BLI_lasso_is_edge_inside(lasso_coords,
1112 int(pos1_proj.x),
1113 int(pos1_proj.y),
1114 int(pos2_proj.x),
1115 int(pos2_proj.y),
1116 IS_CLIPPED))
1117 {
1118 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1119 apply_selection_operation_at_index(selection.span, curve_i, sel_op);
1120 }
1121 changed = true;
1122 return true;
1123 }
1124 return false;
1125 };
1126 bool segment_selected = false;
1127 for (const int segment_i : points.drop_back(cyclic[curve_i] ? 0 : 1)) {
1128 if (process_segment(segment_i, segment_i + 1)) {
1129 segment_selected = true;
1130 break;
1131 }
1132 }
1133 if (!segment_selected && cyclic[curve_i]) {
1134 process_segment(points.last(), points.first());
1135 }
1136 });
1137 });
1138 }
1139 finish_attribute_writers(selection_writers);
1140 return changed;
1141}
1142
1144 bke::CurvesGeometry &curves,
1145 const bke::crazyspace::GeometryDeformation &deformation,
1146 const float4x4 &projection,
1147 const IndexMask &selection_mask,
1148 const IndexMask &bezier_mask,
1149 const bke::AttrDomain selection_domain,
1150 const int2 coord,
1151 const float radius,
1152 const eSelectOp sel_op)
1153{
1154 const float radius_sq = pow2f(radius);
1156 selection_domain);
1157 bool changed = false;
1158 if (sel_op == SEL_OP_SET) {
1159 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1160 fill_selection_false(selection.span, selection_mask);
1161 };
1162 changed = true;
1163 }
1164
1165 if (selection_domain == bke::AttrDomain::Point) {
1167 curves,
1168 deformation,
1169 [&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
1170 const IndexMask &mask = (selection_attribute_name == ".selection") ? selection_mask :
1171 bezier_mask;
1172 mask.slice_content(range).foreach_index(GrainSize(1024), [&](const int point_i) {
1173 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1174 vc.region, positions[point_i], projection);
1175 if (math::distance_squared(pos_proj, float2(coord)) <= radius_sq) {
1177 selection_attribute_writer_by_name(selection_writers, selection_attribute_name)
1178 .span,
1179 point_i,
1180 sel_op);
1181 changed = true;
1182 }
1183 });
1184 });
1185 }
1186 else if (selection_domain == bke::AttrDomain::Curve) {
1187 const OffsetIndices points_by_curve = curves.points_by_curve();
1188 const VArray<bool> cyclic = curves.cyclic();
1190 curves,
1191 deformation,
1192 [&](const IndexRange range,
1193 const Span<float3> positions,
1194 StringRef /* selection_attribute_name */) {
1195 const IndexMask &mask = selection_mask;
1196 mask.slice_content(range).foreach_index(GrainSize(512), [&](const int curve_i) {
1197 const IndexRange points = points_by_curve[curve_i];
1198 if (points.size() == 1) {
1199 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1200 vc.region, positions[points.first()], projection);
1201 if (math::distance_squared(pos_proj, float2(coord)) <= radius_sq) {
1202 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1203 apply_selection_operation_at_index(selection.span, curve_i, sel_op);
1204 }
1205 changed = true;
1206 }
1207 return;
1208 }
1209 auto process_segments = [&](const int segment_i, const int next_i) {
1210 const float3 pos1 = positions[segment_i];
1211 const float3 pos2 = positions[next_i];
1212
1213 const float2 pos1_proj = ED_view3d_project_float_v2_m4(vc.region, pos1, projection);
1214 const float2 pos2_proj = ED_view3d_project_float_v2_m4(vc.region, pos2, projection);
1215
1216 const float distance_proj_sq = dist_squared_to_line_segment_v2(
1217 float2(coord), pos1_proj, pos2_proj);
1218 if (distance_proj_sq <= radius_sq) {
1219 for (bke::GSpanAttributeWriter &selection : selection_writers) {
1220 apply_selection_operation_at_index(selection.span, curve_i, sel_op);
1221 }
1222 changed = true;
1223 return true;
1224 }
1225 return false;
1226 };
1227 bool segment_selected = false;
1228 for (const int segment_i : points.drop_back(1)) {
1229 if (process_segments(segment_i, segment_i + 1)) {
1230 segment_selected = true;
1231 break;
1232 }
1233 }
1234 if (!segment_selected && cyclic[curve_i]) {
1235 process_segments(points.last(), points.first());
1236 }
1237 });
1238 });
1239 }
1240 finish_attribute_writers(selection_writers);
1241 return changed;
1242}
1243
1244template<typename PointSelectFn, typename LineSelectFn>
1246 const IndexMask &mask,
1247 const bke::AttrDomain selection_domain,
1248 IndexMaskMemory &memory,
1249 PointSelectFn &&point_predicate,
1250 LineSelectFn &&line_predicate)
1251{
1252 const OffsetIndices points_by_curve = curves.points_by_curve();
1253 const VArraySpan<bool> cyclic = curves.cyclic();
1254
1255 if (selection_domain == bke::AttrDomain::Point) {
1256 return IndexMask::from_predicate(
1257 mask.slice_content(curves.points_range()), GrainSize(1024), memory, point_predicate);
1258 }
1259 if (selection_domain == bke::AttrDomain::Curve) {
1260 return IndexMask::from_predicate(
1261 mask.slice_content(curves.curves_range()),
1262 GrainSize(512),
1263 memory,
1264 [&](const int curve_i) -> bool {
1265 const IndexRange points = points_by_curve[curve_i];
1266 const bool is_cyclic = cyclic[curve_i];
1267
1268 /* Single-point curve can still be selected in curve mode. */
1269 if (points.size() == 1) {
1270 return point_predicate(points.first());
1271 }
1272
1273 for (const int point_i : points.drop_back(1)) {
1274 if (line_predicate(curve_i, point_i, point_i + 1)) {
1275 return true;
1276 }
1277 }
1278 if (is_cyclic) {
1279 if (line_predicate(curve_i, points.last(), points.first())) {
1280 return true;
1281 }
1282 }
1283 return false;
1284 });
1285 }
1286 return {};
1287}
1288
1290 const IndexMask &curves_mask,
1291 const StringRef attribute_name,
1292 const bool deselect,
1293 IndexMaskMemory &memory)
1294{
1295 const OffsetIndices points_by_curve = curves.points_by_curve();
1296 const VArray<bool> cyclic = curves.cyclic();
1297
1298 VArraySpan<bool> selection = *curves.attributes().lookup_or_default<bool>(
1299 attribute_name, bke::AttrDomain::Point, true);
1300
1301 /* Mask of points that are not selected yet but adjacent. */
1302 Array<bool> changed_points(curves.points_num());
1303
1304 auto is_point_changed1 = [&](const int point, const int neighbor) {
1305 return deselect ? (selection[point] && !selection[neighbor]) :
1306 (!selection[point] && selection[neighbor]);
1307 };
1308 auto is_point_changed2 = [&](const int point, const int neighbor1, const int neighbor2) {
1309 return deselect ? (selection[point] && (!selection[neighbor1] || !selection[neighbor2])) :
1310 (!selection[point] && (selection[neighbor1] || selection[neighbor2]));
1311 };
1312
1313 curves_mask.foreach_index([&](const int64_t curve_i) {
1314 const IndexRange points = points_by_curve[curve_i];
1315 if (points.size() == 1) {
1316 /* Single point curve does not add anything to the mask. */
1317 return;
1318 }
1319
1320 if (cyclic[curve_i]) {
1321 changed_points[points.first()] = is_point_changed2(
1322 points.first(), points.last(), points.first() + 1);
1323 for (const int point_i : points.drop_front(1).drop_back(1)) {
1324 changed_points[point_i] = is_point_changed2(point_i, point_i - 1, point_i + 1);
1325 }
1326 changed_points[points.last()] = is_point_changed2(
1327 points.last(), points.last() - 1, points.first());
1328 }
1329 else {
1330 changed_points[points.first()] = is_point_changed1(points.first(), points.first() + 1);
1331 for (const int point_i : points.drop_front(1).drop_back(1)) {
1332 changed_points[point_i] = is_point_changed2(point_i, point_i - 1, point_i + 1);
1333 }
1334 changed_points[points.last()] = is_point_changed1(points.last(), points.last() - 1);
1335 }
1336 });
1337
1338 return IndexMask::from_bools(changed_points, memory);
1339}
1340
1342 const StringRef attribute_name,
1343 const bool deselect,
1344 IndexMaskMemory &memory)
1345{
1346 return select_adjacent_mask(curves, curves.curves_range(), attribute_name, deselect, memory);
1347}
1348
1350 const bke::CurvesGeometry &curves,
1351 const bke::crazyspace::GeometryDeformation &deformation,
1352 const float4x4 &projection,
1353 const IndexMask &selection_mask,
1354 const IndexMask &bezier_mask,
1355 const bke::AttrDomain selection_domain,
1356 const StringRef attribute_name,
1357 const rcti &rect,
1358 IndexMaskMemory &memory)
1359{
1361 curves, deformation, attribute_name);
1362
1363 auto point_predicate = [&](const int point_i) {
1364 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1365 vc.region, positions[point_i], projection);
1366 /* Check the lasso bounding box first as an optimization. */
1367 return BLI_rcti_isect_pt_v(&rect, int2(pos_proj));
1368 };
1369 auto line_predicate = [&](const int /*curve_i*/, const int point_i, const int next_point_i) {
1370 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1371 vc.region, positions[point_i], projection);
1372 const float2 next_pos_proj = ED_view3d_project_float_v2_m4(
1373 vc.region, positions[next_point_i], projection);
1374 return BLI_rcti_isect_segment(&rect, int2(pos_proj), int2(next_pos_proj));
1375 };
1376
1377 const IndexMask &mask = (selection_domain != bke::AttrDomain::Point ||
1378 attribute_name == ".selection") ?
1379 selection_mask :
1380 bezier_mask;
1382 curves, mask, selection_domain, memory, point_predicate, line_predicate);
1383}
1384
1386 const bke::CurvesGeometry &curves,
1387 const bke::crazyspace::GeometryDeformation &deformation,
1388 const float4x4 &projection,
1389 const IndexMask &selection_mask,
1390 const IndexMask &bezier_mask,
1391 const bke::AttrDomain selection_domain,
1392 const StringRef attribute_name,
1393 const Span<int2> lasso_coords,
1394 IndexMaskMemory &memory)
1395{
1396 rcti bbox;
1397 BLI_lasso_boundbox(&bbox, lasso_coords);
1399 curves, deformation, attribute_name);
1400
1401 auto point_predicate = [&](const int point_i) {
1402 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1403 vc.region, positions[point_i], projection);
1404 /* Check the lasso bounding box first as an optimization. */
1405 return BLI_rcti_isect_pt_v(&bbox, int2(pos_proj)) &&
1406 BLI_lasso_is_point_inside(lasso_coords, int(pos_proj.x), int(pos_proj.y), IS_CLIPPED);
1407 };
1408 auto line_predicate = [&](const int /*curve_i*/, const int point_i, const int next_point_i) {
1409 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1410 vc.region, positions[point_i], projection);
1411 const float2 next_pos_proj = ED_view3d_project_float_v2_m4(
1412 vc.region, positions[next_point_i], projection);
1413 return BLI_rcti_isect_segment(&bbox, int2(pos_proj), int2(next_pos_proj)) &&
1414 BLI_lasso_is_edge_inside(lasso_coords,
1415 int(pos_proj.x),
1416 int(pos_proj.y),
1417 int(next_pos_proj.x),
1418 int(next_pos_proj.y),
1419 IS_CLIPPED);
1420 };
1421
1422 const IndexMask &mask = (selection_domain != bke::AttrDomain::Point ||
1423 attribute_name == ".selection") ?
1424 selection_mask :
1425 bezier_mask;
1427 curves, mask, selection_domain, memory, point_predicate, line_predicate);
1428}
1429
1431 const bke::CurvesGeometry &curves,
1432 const bke::crazyspace::GeometryDeformation &deformation,
1433 const float4x4 &projection,
1434 const IndexMask &selection_mask,
1435 const IndexMask &bezier_mask,
1436 const bke::AttrDomain selection_domain,
1437 const StringRef attribute_name,
1438 const int2 coord,
1439 const float radius,
1440 IndexMaskMemory &memory)
1441{
1442 const float radius_sq = pow2f(radius);
1444 curves, deformation, attribute_name);
1445
1446 auto point_predicate = [&](const int point_i) {
1447 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1448 vc.region, positions[point_i], projection);
1449 const float distance_proj_sq = math::distance_squared(pos_proj, float2(coord));
1450 return distance_proj_sq <= radius_sq;
1451 };
1452 auto line_predicate = [&](const int /*curve_i*/, const int point_i, const int next_point_i) {
1453 const float2 pos_proj = ED_view3d_project_float_v2_m4(
1454 vc.region, positions[point_i], projection);
1455 const float2 next_pos_proj = ED_view3d_project_float_v2_m4(
1456 vc.region, positions[next_point_i], projection);
1457 const float distance_proj_sq = dist_squared_to_line_segment_v2(
1458 float2(coord), pos_proj, next_pos_proj);
1459 return distance_proj_sq <= radius_sq;
1460 };
1461
1462 const IndexMask &mask = (selection_domain != bke::AttrDomain::Point ||
1463 attribute_name == ".selection") ?
1464 selection_mask :
1465 bezier_mask;
1467 curves, mask, selection_domain, memory, point_predicate, line_predicate);
1468}
1469
1470} // namespace blender::ed::curves
Low-level operations for curves.
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
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:289
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
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
eSelectOp
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_XOR
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:239
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
void init()
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
Span< T > as_span() const
Definition BLI_array.hh:232
GMutableSpan slice(const int64_t start, int64_t size) 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 bool is_empty() const
constexpr IndexRange drop_front(int64_t n) const
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool contains(const T &value) const
Definition BLI_span.hh:278
constexpr int64_t size() const
CommonVArrayInfo common_info() const
void append(const T &value)
IndexRange index_range() const
std::optional< AttributeMetaData > lookup_meta_data(const StringRef attribute_id) const
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
IndexMask slice_content(IndexRange range) const
IndexMask slice(IndexRange range) const
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
void foreach_range(Fn &&fn) const
void foreach_index(Fn &&fn) const
local_group_size(16, 16) .push_constant(Type b
static bool is_cyclic(const Nurb *nu)
bool has_anything_selected(const VArray< bool > &varray, const IndexRange range_to_check)
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)
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 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, float radius, const FindClosestData &initial_closest)
IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
void remove_selection_attributes(bke::MutableAttributeAccessor &attributes, Span< StringRef > selection_attribute_names)
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)
Span< float3 > get_selection_attribute_positions(const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const StringRef attribute_name)
static void invert_selection(MutableSpan< float > selection)
static void init_selectable_foreach(const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, 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 foreach_selectable_point_range(const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, 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)
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 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)
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)
void foreach_selectable_curve_range(const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, SelectionRangeFn range_consumer)
bool select_lasso(const ViewContext &vc, bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection_matrix, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const Span< int2 > lasso_coords, const eSelectOp sel_op)
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:153
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
__int64 int64_t
Definition stdint.h:89
CurvesGeometry geometry
ARegion * region
Definition ED_view3d.hh:73