Blender V4.3
node_geo_index_of_nearest.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
5#include "BLI_array.hh"
6#include "BLI_kdtree.h"
7#include "BLI_map.hh"
8#include "BLI_task.hh"
9
10#include "node_geometry_util.hh"
11
13
15{
16 b.add_input<decl::Vector>("Position").implicit_field(implicit_field_inputs::position);
17 b.add_input<decl::Int>("Group ID").supports_field().hide_value();
18
19 b.add_output<decl::Int>("Index").field_source_reference_all().description(
20 "Index of nearest element");
21 b.add_output<decl::Bool>("Has Neighbor").field_source_reference_all();
22}
23
24static KDTree_3d *build_kdtree(const Span<float3> positions, const IndexMask &mask)
25{
26 KDTree_3d *tree = BLI_kdtree_3d_new(mask.size());
27 mask.foreach_index(
28 [&](const int index) { BLI_kdtree_3d_insert(tree, index, positions[index]); });
29 BLI_kdtree_3d_balance(tree);
30 return tree;
31}
32
33static int find_nearest_non_self(const KDTree_3d &tree, const float3 &position, const int index)
34{
35 return BLI_kdtree_3d_find_nearest_cb_cpp(
36 &tree,
37 position,
38 nullptr,
39 [index](const int other, const float * /*co*/, const float /*dist_sq*/) {
40 return index == other ? 0 : 1;
41 });
42}
43
44static void find_neighbors(const KDTree_3d &tree,
45 const Span<float3> positions,
46 const IndexMask &mask,
47 MutableSpan<int> r_indices)
48{
49 mask.foreach_index(GrainSize(1024), [&](const int index) {
50 r_indices[index] = find_nearest_non_self(tree, positions[index], index);
51 });
52}
53
55 private:
56 const Field<float3> positions_field_;
57 const Field<int> group_field_;
58
59 public:
61 : bke::GeometryFieldInput(CPPType::get<int>(), "Index of Nearest"),
62 positions_field_(std::move(positions_field)),
63 group_field_(std::move(group_field))
64 {
65 }
66
68 const IndexMask &mask) const final
69 {
70 if (!context.attributes()) {
71 return {};
72 }
73 const int domain_size = context.attributes()->domain_size(context.domain());
74 fn::FieldEvaluator evaluator{context, domain_size};
75 evaluator.add(positions_field_);
76 evaluator.add(group_field_);
77 evaluator.evaluate();
78 const VArraySpan<float3> positions = evaluator.get_evaluated<float3>(0);
79 const VArray<int> group_ids = evaluator.get_evaluated<int>(1);
80
82
83 if (group_ids.is_single()) {
84 result.reinitialize(mask.min_array_size());
85 KDTree_3d *tree = build_kdtree(positions, IndexRange(domain_size));
86 find_neighbors(*tree, positions, mask, result);
87 BLI_kdtree_3d_free(tree);
88 return VArray<int>::ForContainer(std::move(result));
89 }
90 const VArraySpan<int> group_ids_span(group_ids);
91
92 const VectorSet<int> group_indexing(group_ids_span);
93 const int groups_num = group_indexing.size();
94
95 IndexMaskMemory mask_memory;
96 Array<IndexMask> all_indices_by_group_id(groups_num);
97 Array<IndexMask> lookup_indices_by_group_id(groups_num);
98
99 const auto get_group_index = [&](const int i) {
100 const int group_id = group_ids_span[i];
101 return group_indexing.index_of(group_id);
102 };
103
105 IndexMask(domain_size), mask_memory, get_group_index, all_indices_by_group_id);
106
107 if (mask.size() == domain_size) {
108 lookup_indices_by_group_id = all_indices_by_group_id;
109 result.reinitialize(domain_size);
110 }
111 else {
112 IndexMask::from_groups<int>(mask, mask_memory, get_group_index, lookup_indices_by_group_id);
113 result.reinitialize(mask.min_array_size());
114 }
115
116 /* The grain size should be larger as each tree gets smaller. */
117 const int avg_tree_size = domain_size / group_indexing.size();
118 const int grain_size = std::max(8192 / avg_tree_size, 1);
119 threading::parallel_for(IndexRange(groups_num), grain_size, [&](const IndexRange range) {
120 for (const int group_index : range) {
121 const IndexMask &tree_mask = all_indices_by_group_id[group_index];
122 const IndexMask &lookup_mask = lookup_indices_by_group_id[group_index];
123 KDTree_3d *tree = build_kdtree(positions, tree_mask);
124 find_neighbors(*tree, positions, lookup_mask, result);
125 BLI_kdtree_3d_free(tree);
126 }
127 });
128
129 return VArray<int>::ForContainer(std::move(result));
130 }
131
132 public:
134 {
135 positions_field_.node().for_each_field_input_recursive(fn);
136 group_field_.node().for_each_field_input_recursive(fn);
137 }
138
139 uint64_t hash() const final
140 {
141 return get_default_hash(positions_field_, group_field_);
142 }
143
144 bool is_equal_to(const fn::FieldNode &other) const final
145 {
146 if (const auto *other_field = dynamic_cast<const IndexOfNearestFieldInput *>(&other)) {
147 return positions_field_ == other_field->positions_field_ &&
148 group_field_ == other_field->group_field_;
149 }
150 return false;
151 }
152
153 std::optional<AttrDomain> preferred_domain(const GeometryComponent &component) const final
154 {
155 return bke::try_detect_field_domain(component, positions_field_);
156 }
157};
158
160 private:
161 const Field<int> group_field_;
162
163 public:
165 : bke::GeometryFieldInput(CPPType::get<bool>(), "Has Neighbor"),
166 group_field_(std::move(group_field))
167 {
168 }
169
171 const IndexMask &mask) const final
172 {
173 if (!context.attributes()) {
174 return {};
175 }
176 const int domain_size = context.attributes()->domain_size(context.domain());
177 if (domain_size == 1) {
178 return VArray<bool>::ForSingle(false, mask.min_array_size());
179 }
180
181 fn::FieldEvaluator evaluator{context, domain_size};
182 evaluator.add(group_field_);
183 evaluator.evaluate();
184 const VArray<int> group = evaluator.get_evaluated<int>(0);
185
186 if (group.is_single()) {
187 return VArray<bool>::ForSingle(true, mask.min_array_size());
188 }
189
190 Map<int, int> counts;
191 const VArraySpan<int> group_span(group);
192 mask.foreach_index([&](const int i) {
193 counts.add_or_modify(
194 group_span[i], [](int *count) { *count = 1; }, [](int *count) { (*count)++; });
195 });
196 Array<bool> result(mask.min_array_size());
197 mask.foreach_index([&](const int i) { result[i] = counts.lookup(group_span[i]) > 1; });
198 return VArray<bool>::ForContainer(std::move(result));
199 }
200
201 public:
203 {
204 group_field_.node().for_each_field_input_recursive(fn);
205 }
206
207 uint64_t hash() const final
208 {
209 return get_default_hash(39847876, group_field_);
210 }
211
212 bool is_equal_to(const fn::FieldNode &other) const final
213 {
214 if (const auto *other_field = dynamic_cast<const HasNeighborFieldInput *>(&other)) {
215 return group_field_ == other_field->group_field_;
216 }
217 return false;
218 }
219
220 std::optional<AttrDomain> preferred_domain(const GeometryComponent &component) const final
221 {
222 return bke::try_detect_field_domain(component, group_field_);
223 }
224};
225
227{
228 Field<float3> position_field = params.extract_input<Field<float3>>("Position");
229 Field<int> group_field = params.extract_input<Field<int>>("Group ID");
230
231 if (params.output_is_required("Index")) {
232 params.set_output("Index",
233 Field<int>(std::make_shared<IndexOfNearestFieldInput>(
234 std::move(position_field), group_field)));
235 }
236
237 if (params.output_is_required("Has Neighbor")) {
238 params.set_output(
239 "Has Neighbor",
240 Field<bool>(std::make_shared<HasNeighborFieldInput>(std::move(group_field))));
241 }
242}
243
244static void node_register()
245{
246 static blender::bke::bNodeType ntype;
247
248 geo_node_type_base(&ntype, GEO_NODE_INDEX_OF_NEAREST, "Index of Nearest", NODE_CLASS_CONVERTER);
250 ntype.declare = node_declare;
252}
254
255} // namespace blender::nodes::node_geo_index_of_nearest_cc
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:410
A KD-tree for nearest neighbor search.
#define NOD_REGISTER_NODE(REGISTER_FUNC)
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
auto add_or_modify(const Key &key, const CreateValueF &create_value, const ModifyValueF &modify_value) -> decltype(create_value(nullptr))
Definition BLI_map.hh:457
static VArray ForContainer(ContainerT container)
static VArray ForSingle(T value, const int64_t size)
int64_t index_of(const Key &key) const
int64_t size() const
int add(GField field, GVArray *varray_ptr)
Definition field.cc:756
virtual void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const
Definition field.cc:587
const FieldNode & node() const
Definition FN_field.hh:137
static void from_groups(const IndexMask &universe, IndexMaskMemory &memory, Fn &&get_group_index, MutableSpan< IndexMask > r_masks)
GVArray get_varray_for_context(const bke::GeometryFieldContext &context, const IndexMask &mask) const final
void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const
std::optional< AttrDomain > preferred_domain(const GeometryComponent &component) const final
IndexOfNearestFieldInput(Field< float3 > positions_field, Field< int > group_field)
GVArray get_varray_for_context(const bke::GeometryFieldContext &context, const IndexMask &mask) const final
std::optional< AttrDomain > preferred_domain(const GeometryComponent &component) const final
void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const
local_group_size(16, 16) .push_constant(Type b
KDTree_3d * tree
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
IndexRange range
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
std::optional< AttrDomain > try_detect_field_domain(const GeometryComponent &component, const fn::GField &field)
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
void position(const bNode &, void *r_value)
static void find_neighbors(const KDTree_3d &tree, const Span< float3 > positions, const IndexMask &mask, MutableSpan< int > r_indices)
static void node_geo_exec(GeoNodeExecParams params)
static void node_declare(NodeDeclarationBuilder &b)
static int find_nearest_non_self(const KDTree_3d &tree, const float3 &position, const int index)
static KDTree_3d * build_kdtree(const Span< float3 > positions, const IndexMask &mask)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
uint64_t get_default_hash(const T &v)
Definition BLI_hash.hh:219
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
unsigned __int64 uint64_t
Definition stdint.h:90
Defines a node type.
Definition BKE_node.hh:218
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
NodeDeclareFunction declare
Definition BKE_node.hh:347