Blender V5.0
selection.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_array_utils.hh"
10#include "BLI_index_mask.hh"
11#include "BLI_lasso_2d.hh"
12#include "BLI_math_vector.hh"
13#include "BLI_rect.h"
14
15#include "BKE_attribute.hh"
16
17#include "ED_pointcloud.hh"
18#include "ED_select_utils.hh"
19#include "ED_view3d.hh"
20
22
24
26{
27 const VArray<bool> selection = *pointcloud.attributes().lookup<bool>(".selection");
28 return !selection || array_utils::contains(selection, selection.index_range(), true);
29}
30
32 bke::AttrType create_type)
33{
34 const bke::AttrDomain selection_domain = bke::AttrDomain::Point;
35 const StringRef attribute_name = ".selection";
36
37 bke::MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
38 if (attributes.contains(attribute_name)) {
39 return attributes.lookup_for_write_span(attribute_name);
40 }
41 const int domain_size = pointcloud.totpoint;
42 switch (create_type) {
44 attributes.add(attribute_name,
45 selection_domain,
48 break;
50 attributes.add(attribute_name,
51 selection_domain,
54 break;
55 default:
57 }
58 return attributes.lookup_for_write_span(attribute_name);
59}
60
62{
63 if (selection.type().is<bool>()) {
64 index_mask::masked_fill(selection.typed<bool>(), false, mask);
65 }
66 else if (selection.type().is<float>()) {
67 index_mask::masked_fill(selection.typed<float>(), 0.0f, mask);
68 }
69}
70
72{
73 fill_selection_true(selection, IndexMask(selection.size()));
74}
75
77{
78 if (selection.type().is<bool>()) {
79 index_mask::masked_fill(selection.typed<bool>(), true, mask);
80 }
81 else if (selection.type().is<float>()) {
82 index_mask::masked_fill(selection.typed<float>(), 1.0f, mask);
83 }
84}
85
86static void invert_selection(MutableSpan<float> selection, const IndexMask &mask)
87{
88 mask.foreach_index_optimized<int64_t>(
89 GrainSize(2048), [&](const int64_t i) { selection[i] = 1.0f - selection[i]; });
90}
91
92static void invert_selection(GMutableSpan selection, const IndexMask &mask)
93{
94 if (selection.type().is<bool>()) {
95 array_utils::invert_booleans(selection.typed<bool>(), mask);
96 }
97 else if (selection.type().is<float>()) {
98 invert_selection(selection.typed<float>(), mask);
99 }
100}
101
102static void select_all(PointCloud &pointcloud, const IndexMask &mask, int action)
103{
104 if (action == SEL_SELECT) {
105 std::optional<IndexRange> range = mask.to_range();
106 if (range.has_value() && (*range == IndexRange(pointcloud.totpoint))) {
107 bke::MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
108 /* As an optimization, just remove the selection attributes when everything is selected. */
109 attributes.remove(".selection");
110 return;
111 }
112 }
113
116 if (action == SEL_SELECT) {
117 fill_selection_true(selection.span, mask);
118 }
119 else if (action == SEL_DESELECT) {
120 fill_selection_false(selection.span, mask);
121 }
122 else if (action == SEL_INVERT) {
123 invert_selection(selection.span, mask);
124 }
125 selection.finish();
126}
127
129{
130 select_all(pointcloud, IndexRange(pointcloud.totpoint), action);
131}
132
134 const IndexMask &mask,
135 eSelectOp sel_op)
136{
137 bool changed = false;
140 if (sel_op == SEL_OP_SET) {
141 fill_selection_false(selection.span, IndexRange(selection.span.size()));
142 changed = true;
143 }
144 switch (sel_op) {
145 case SEL_OP_ADD:
146 case SEL_OP_SET:
147 fill_selection_true(selection.span, mask);
148 break;
149 case SEL_OP_SUB:
150 fill_selection_false(selection.span, mask);
151 break;
152 case SEL_OP_XOR:
153 invert_selection(selection.span, mask);
154 break;
155 default:
156 break;
157 }
158 changed |= !mask.is_empty();
159 selection.finish();
160 return changed;
161}
162
164 const ARegion &region,
165 const float4x4 &projection,
166 const rcti &rect,
167 const eSelectOp sel_op)
168{
169 const Span<float3> positions = pointcloud.positions();
170
171 IndexMaskMemory memory;
173 positions.index_range(), GrainSize(1024), memory, [&](const int point) {
174 const float2 pos_proj = ED_view3d_project_float_v2_m4(
175 &region, positions[point], projection);
176 return BLI_rcti_isect_pt_v(&rect, int2(pos_proj));
177 });
178
180}
181
183 const ARegion &region,
184 const float4x4 &projection,
185 const Span<int2> lasso_coords,
186 const eSelectOp sel_op)
187{
188 rcti bbox;
189 BLI_lasso_boundbox(&bbox, lasso_coords);
190
191 const Span<float3> positions = pointcloud.positions();
192
193 IndexMaskMemory memory;
195 positions.index_range(), GrainSize(1024), memory, [&](const int point) {
196 const float2 pos_proj = ED_view3d_project_float_v2_m4(
197 &region, positions[point], projection);
198 if (!BLI_rcti_isect_pt_v(&bbox, int2(pos_proj))) {
199 return false;
200 }
201 if (!BLI_lasso_is_point_inside(lasso_coords, int(pos_proj.x), int(pos_proj.y), IS_CLIPPED))
202 {
203 return false;
204 }
205 return true;
206 });
207
209}
210
212 const ARegion &region,
213 const float4x4 &projection,
214 const int2 coord,
215 const float radius,
216 const eSelectOp sel_op)
217{
218 const float radius_sq = radius * radius;
219
220 const Span<float3> positions = pointcloud.positions();
221
222 IndexMaskMemory memory;
224 positions.index_range(), GrainSize(1024), memory, [&](const int point) {
225 const float2 pos_proj = ED_view3d_project_float_v2_m4(
226 &region, positions[point], projection);
227 return math::distance_squared(pos_proj, float2(coord)) <= radius_sq;
228 });
229
231}
232
234{
235 if (a.distance_sq < b.distance_sq) {
236 return a;
237 }
238 return b;
239}
240
241std::optional<FindClosestData> find_closest_point_to_screen_co(
242 const ARegion &region,
243 const Span<float3> positions,
244 const float4x4 &projection,
245 const IndexMask &points_mask,
246 const float2 mouse_pos,
247 const float radius,
248 const FindClosestData &initial_closest)
249{
250 const float radius_sq = radius * radius;
251 const FindClosestData new_closest_data = threading::parallel_reduce(
252 points_mask.index_range(),
253 1024,
254 initial_closest,
255 [&](const IndexRange range, const FindClosestData &init) {
256 FindClosestData best_match = init;
257 points_mask.slice(range).foreach_index([&](const int point) {
258 const float3 &pos = positions[point];
259 const float2 pos_proj = ED_view3d_project_float_v2_m4(&region, pos, projection);
260
261 const float distance_proj_sq = math::distance_squared(pos_proj, mouse_pos);
262 if (distance_proj_sq > radius_sq || distance_proj_sq > best_match.distance_sq) {
263 return;
264 }
265
266 best_match = {point, distance_proj_sq};
267 });
268 return best_match;
269 },
271
272 if (new_closest_data.distance_sq < initial_closest.distance_sq) {
273 return new_closest_data;
274 }
275
276 return {};
277}
278
280{
281 const VArray selection = *pointcloud.attributes().lookup_or_default<bool>(
282 ".selection", bke::AttrDomain::Point, true);
283 return IndexMask::from_bools(selection, memory);
284}
285
286} // namespace blender::ed::pointcloud
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
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_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
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)
#define IS_CLIPPED
Definition ED_view3d.hh:252
long long int int64_t
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
bool is() const
const CPPType & type() const
MutableSpan< T > typed() const
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
IndexRange index_range() const
static VArray from_single(T value, const int64_t size)
bool contains(StringRef attribute_id) const
bool add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
bool remove(const StringRef attribute_id)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void invert_booleans(MutableSpan< bool > span)
bool contains(const VArray< bool > &varray, const IndexMask &indices_to_check, bool value)
bool select_box(PointCloud &pointcloud, const ARegion &region, const float4x4 &projection, const rcti &rect, const eSelectOp sel_op)
Definition selection.cc:163
bool select_lasso(PointCloud &pointcloud, const ARegion &region, const float4x4 &projection, const Span< int2 > lasso_coords, const eSelectOp sel_op)
Definition selection.cc:182
bool select_circle(PointCloud &pointcloud, const ARegion &region, const float4x4 &projection, const int2 coord, const float radius, const eSelectOp sel_op)
Definition selection.cc:211
void fill_selection_true(GMutableSpan span)
Definition selection.cc:71
static void invert_selection(MutableSpan< float > selection, const IndexMask &mask)
Definition selection.cc:86
static bool apply_selection_operation(PointCloud &pointcloud, const IndexMask &mask, eSelectOp sel_op)
Definition selection.cc:133
static FindClosestData closer_elem(const FindClosestData &a, const FindClosestData &b)
Definition selection.cc:233
IndexMask retrieve_selected_points(const PointCloud &pointcloud, IndexMaskMemory &memory)
Definition selection.cc:279
bke::GSpanAttributeWriter ensure_selection_attribute(PointCloud &pointcloud, bke::AttrType create_type)
Definition selection.cc:31
void fill_selection_false(GMutableSpan selection, const IndexMask &mask)
Definition selection.cc:61
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)
Definition selection.cc:241
bool has_anything_selected(const PointCloud &pointcloud)
Definition selection.cc:25
void select_all(PointCloud &pointcloud, int action)
Definition selection.cc:128
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)
i
Definition text_draw.cc:230