26 .description(
"Base curves that new curves are interpolated between");
30 .
description(
"Optional up vector that is typically a surface normal");
35 "Splits guides into separate groups. New curves interpolate existing curves "
36 "from a single group");
38 "First control point positions for new interpolated curves");
42 .
description(
"Optional up vector that is typically a surface normal");
51 "Maximum amount of close guide curves that are taken into account for interpolation");
55 .
description(
"Index of the closest guide curve for each generated curve");
58 .
description(
"Weight of the closest guide curve for each generated curve");
68 for (
const int curve_i : guide_group_ids.
index_range()) {
69 const int group = guide_group_ids[curve_i];
70 guides_by_group.
add(group, curve_i);
72 return guides_by_group;
84 for (
const auto &[group, guide_curve_indices] : guides_by_group.
items()) {
85 int group_control_points = points_by_curve[guide_curve_indices[0]].
size();
86 for (
const int guide_curve_i : guide_curve_indices.as_span().drop_front(1)) {
87 const int control_points = points_by_curve[guide_curve_i].
size();
88 if (group_control_points != control_points) {
89 group_control_points = -1;
93 if (group_control_points != -1) {
94 points_per_curve_by_group.
add(group, group_control_points);
97 return points_per_curve_by_group;
110 for (
const auto item : guides_by_group.
items()) {
111 const int group = item.key;
112 const Span<int> guide_indices = item.value;
114 KDTree_3d *kdtree = BLI_kdtree_3d_new(guide_indices.
size());
115 kdtrees.
add_new(group, kdtree);
117 for (
const int curve_i : guide_indices) {
118 const int first_point_i = offsets[curve_i];
119 const float3 &root_pos = positions[first_point_i];
120 BLI_kdtree_3d_insert(kdtree, curve_i, root_pos);
124 [](KDTree_3d *kdtree) { BLI_kdtree_3d_balance(kdtree); });
136 const int max_neighbor_count,
142 for (const int child_curve_i : range) {
143 const float3 &position = positions[child_curve_i];
144 const int group = point_group_ids[child_curve_i];
145 const KDTree_3d *kdtree = kdtrees.lookup_default(group, nullptr);
146 if (kdtree == nullptr) {
147 r_all_neighbor_counts[child_curve_i] = 0;
151 const int num_guides_in_group = guides_by_group.lookup(group).size();
155 const bool use_extra_neighbor = num_guides_in_group > max_neighbor_count;
156 const int neighbors_to_find = max_neighbor_count + use_extra_neighbor;
158 Vector<KDTreeNearest_3d, 16> nearest_n(neighbors_to_find);
159 const int num_neighbors = BLI_kdtree_3d_find_nearest_n(
160 kdtree, position, nearest_n.data(), neighbors_to_find);
161 if (num_neighbors == 0) {
162 r_all_neighbor_counts[child_curve_i] = 0;
166 const IndexRange neighbors_range{child_curve_i * max_neighbor_count, max_neighbor_count};
167 MutableSpan<int> neighbor_indices = r_all_neighbor_indices.slice(neighbors_range);
168 MutableSpan<float> neighbor_weights = r_all_neighbor_weights.slice(neighbors_range);
170 float tot_weight = 0.0f;
172 if (use_extra_neighbor) {
175 const float max_distance = std::max_element(
177 nearest_n.begin() + num_neighbors,
178 [](const KDTreeNearest_3d &a, const KDTreeNearest_3d &b) {
179 return a.dist < b.dist;
182 if (max_distance == 0.0f) {
183 r_all_neighbor_counts[child_curve_i] = 1;
184 neighbor_indices[0] = nearest_n[0].index;
185 neighbor_weights[0] = 1.0f;
189 int neighbor_counter = 0;
190 for (const int neighbor_i : IndexRange(num_neighbors)) {
191 const KDTreeNearest_3d &nearest = nearest_n[neighbor_i];
195 const float weight = (max_distance - nearest.dist) / std::max(nearest.dist, 0.000001f);
197 tot_weight += weight;
198 neighbor_indices[neighbor_counter] = nearest.index;
199 neighbor_weights[neighbor_counter] = weight;
203 r_all_neighbor_counts[child_curve_i] = neighbor_counter;
206 int neighbor_counter = 0;
207 for (const int neighbor_i : IndexRange(num_neighbors)) {
208 const KDTreeNearest_3d &nearest = nearest_n[neighbor_i];
212 const float weight = 1.0f / std::max(nearest.dist, 0.000001f);
214 tot_weight += weight;
215 neighbor_indices[neighbor_counter] = nearest.index;
216 neighbor_weights[neighbor_counter] = weight;
220 r_all_neighbor_counts[child_curve_i] = neighbor_counter;
222 if (tot_weight > 0.0f) {
224 const float weight_factor = 1.0f / tot_weight;
225 for (float &weight : neighbor_weights.take_front(r_all_neighbor_counts[child_curve_i])) {
226 weight *= weight_factor;
243 const int max_neighbors,
249 for (const int child_curve_i : range) {
250 const int neighbor_count = all_neighbor_counts[child_curve_i];
251 if (neighbor_count == 0) {
252 r_points_per_child[child_curve_i] = 1;
253 r_use_direct_interpolation[child_curve_i] = false;
256 const int group = point_group_ids[child_curve_i];
257 const int points_per_curve_in_group = points_per_curve_by_group.lookup_default(group, -1);
258 if (points_per_curve_in_group != -1) {
259 r_points_per_child[child_curve_i] = points_per_curve_in_group;
260 r_use_direct_interpolation[child_curve_i] = true;
263 const IndexRange neighbors_range{child_curve_i * max_neighbors, neighbor_count};
264 const Span<float> neighbor_weights = all_neighbor_weights.slice(neighbors_range);
265 const Span<int> neighbor_indices = all_neighbor_indices.slice(neighbors_range);
267 float neighbor_points_weighted_sum = 0.0f;
268 for (const int neighbor_i : IndexRange(neighbor_count)) {
269 const int neighbor_index = neighbor_indices[neighbor_i];
270 const float neighbor_weight = neighbor_weights[neighbor_i];
271 const int neighbor_points = guide_points_by_curve[neighbor_index].size();
272 neighbor_points_weighted_sum += neighbor_weight * float(neighbor_points);
274 const int points_in_child = std::max<int>(1, roundf(neighbor_points_weighted_sum));
275 r_points_per_child[child_curve_i] = points_in_child;
276 r_use_direct_interpolation[child_curve_i] = false;
291 for (const int guide_curve_i : range) {
292 r_parameterized_guide_offsets[guide_curve_i] = length_parameterize::segments_num(
293 guide_points_by_curve[guide_curve_i].size(), false);
299 r_parameterized_guide_lengths.reinitialize(r_parameterized_guide_offsets.last());
300 const Span<float3> guide_positions = guide_curves.positions();
302 for (const int guide_curve_i : range) {
303 const IndexRange points = guide_points_by_curve[guide_curve_i];
304 const IndexRange lengths_range = parameterize_offsets[guide_curve_i];
305 length_parameterize::accumulate_lengths<float3>(
306 guide_positions.slice(points),
308 r_parameterized_guide_lengths.as_mutable_span().slice(lengths_range));
318 const int max_neighbors,
327 const Span<bool> use_direct_interpolation_per_child)
335 Vector<float, 16> sample_lengths;
336 Vector<int, 16> sample_segments;
337 Vector<float, 16> sample_factors;
339 for (const int child_curve_i : range) {
340 const IndexRange points = child_points_by_curve[child_curve_i];
341 const int neighbor_count = all_neighbor_counts[child_curve_i];
342 const float3 child_up = points_up[child_curve_i];
343 BLI_assert(math::is_unit_scale(child_up));
344 const float3 &child_root_position = point_positions[child_curve_i];
345 MutableSpan<float3> child_positions = children_positions.slice(points);
347 child_positions.fill(child_root_position);
348 if (neighbor_count == 0) {
353 const IndexRange neighbors_range{child_curve_i * max_neighbors, neighbor_count};
354 const Span<float> neighbor_weights = all_neighbor_weights.slice(neighbors_range);
355 const Span<int> neighbor_indices = all_neighbor_indices.slice(neighbors_range);
357 const bool use_direct_interpolation = use_direct_interpolation_per_child[child_curve_i];
359 for (const int neighbor_i : IndexRange(neighbor_count)) {
360 const int neighbor_index = neighbor_indices[neighbor_i];
361 const float neighbor_weight = neighbor_weights[neighbor_i];
362 const IndexRange guide_points = guide_points_by_curve[neighbor_index];
363 const Span<float3> neighbor_positions = guide_positions.slice(guide_points);
364 const float3 &neighbor_root = neighbor_positions.first();
365 const float3 neighbor_up = guides_up[neighbor_index];
366 BLI_assert(math::is_unit_scale(neighbor_up));
368 const bool is_same_up_vector = neighbor_up == child_up;
370 float3x3 normal_rotation;
371 if (!is_same_up_vector) {
372 rotation_between_vecs_to_mat3(normal_rotation.ptr(), neighbor_up, child_up);
375 if (use_direct_interpolation) {
379 for (const int i : IndexRange(points.size())) {
380 const float3 &neighbor_pos = neighbor_positions[i];
381 const float3 relative_to_root = neighbor_pos - neighbor_root;
382 float3 rotated_relative = relative_to_root;
383 if (!is_same_up_vector) {
384 rotated_relative = normal_rotation * rotated_relative;
386 child_positions[i] += neighbor_weight * rotated_relative;
393 const IndexRange guide_offsets = parameterized_guide_offsets[neighbor_index];
395 if (guide_offsets.is_empty()) {
397 float3 rotated_relative = neighbor_root;
398 if (!is_same_up_vector) {
399 rotated_relative = normal_rotation * rotated_relative;
401 const float3 global_pos = rotated_relative * neighbor_weight;
402 for (float3 &position : child_positions) {
403 position += global_pos;
408 const Span<float> lengths = parameterized_guide_lengths.slice(guide_offsets);
409 const float neighbor_length = lengths.last();
411 sample_lengths.reinitialize(points.size());
412 const float sample_length_factor = math::safe_divide(neighbor_length,
413 float(points.size() - 1));
414 for (const int i : sample_lengths.index_range()) {
415 sample_lengths[i] = i * sample_length_factor;
418 sample_segments.reinitialize(points.size());
419 sample_factors.reinitialize(points.size());
420 length_parameterize::sample_at_lengths(
421 lengths, sample_lengths, sample_segments, sample_factors);
423 for (const int i : IndexRange(points.size())) {
424 const int segment = sample_segments[i];
425 const float factor = sample_factors[i];
426 const float3 sample_pos = math::interpolate(
427 neighbor_positions[segment], neighbor_positions[segment + 1], factor);
428 const float3 relative_to_root = sample_pos - neighbor_root;
429 float3 rotated_relative = relative_to_root;
430 if (!is_same_up_vector) {
431 rotated_relative = normal_rotation * rotated_relative;
433 child_positions[i] += neighbor_weight * rotated_relative;
451 const int max_neighbors,
457 const Span<bool> use_direct_interpolation_per_child)
479 if (iter.
domain == AttrDomain::Curve) {
480 const GVArraySpan src_generic = *iter.
get(AttrDomain::Curve, type);
483 iter.
name, AttrDomain::Curve, type);
488 using T =
decltype(dummy);
494 for (const int child_curve_i : range) {
495 const int neighbor_count = all_neighbor_counts[child_curve_i];
496 const IndexRange neighbors_range{child_curve_i * max_neighbors, neighbor_count};
497 const Span<float> neighbor_weights = all_neighbor_weights.slice(neighbors_range);
498 const Span<int> neighbor_indices = all_neighbor_indices.slice(neighbors_range);
500 for (const int neighbor_i : IndexRange(neighbor_count)) {
501 const int neighbor_index = neighbor_indices[neighbor_i];
502 const float neighbor_weight = neighbor_weights[neighbor_i];
503 mixer.mix_in(child_curve_i, src[neighbor_index], neighbor_weight);
506 mixer.finalize(range);
510 dst_generic.finish();
514 const GVArraySpan src_generic = *iter.
get(AttrDomain::Point, type);
516 iter.
name, AttrDomain::Point, type);
522 using T =
decltype(dummy);
528 Vector<float, 16> sample_lengths;
529 Vector<int, 16> sample_segments;
530 Vector<float, 16> sample_factors;
531 for (const int child_curve_i : range) {
532 const IndexRange points = child_points_by_curve[child_curve_i];
533 const int neighbor_count = all_neighbor_counts[child_curve_i];
534 const IndexRange neighbors_range{child_curve_i * max_neighbors, neighbor_count};
535 const Span<float> neighbor_weights = all_neighbor_weights.slice(neighbors_range);
536 const Span<int> neighbor_indices = all_neighbor_indices.slice(neighbors_range);
537 const bool use_direct_interpolation =
538 use_direct_interpolation_per_child[child_curve_i];
540 for (const int neighbor_i : IndexRange(neighbor_count)) {
541 const int neighbor_index = neighbor_indices[neighbor_i];
542 const float neighbor_weight = neighbor_weights[neighbor_i];
543 const IndexRange guide_points = guide_points_by_curve[neighbor_index];
545 if (use_direct_interpolation) {
546 for (const int i : IndexRange(points.size())) {
547 mixer.mix_in(points[i], src[guide_points[i]], neighbor_weight);
551 const IndexRange guide_offsets = parameterized_guide_offsets[neighbor_index];
552 if (guide_offsets.is_empty()) {
554 const T &curve_value = src[guide_points.first()];
555 for (const int i : points) {
556 mixer.mix_in(i, curve_value, neighbor_weight);
561 const Span<float> lengths = parameterized_guide_lengths.slice(guide_offsets);
562 const float neighbor_length = lengths.last();
564 sample_lengths.reinitialize(points.size());
565 const float sample_length_factor = math::safe_divide(neighbor_length,
566 float(points.size() - 1));
567 for (const int i : sample_lengths.index_range()) {
568 sample_lengths[i] = i * sample_length_factor;
571 sample_segments.reinitialize(points.size());
572 sample_factors.reinitialize(points.size());
573 length_parameterize::sample_at_lengths(
574 lengths, sample_lengths, sample_segments, sample_factors);
576 for (const int i : IndexRange(points.size())) {
577 const int segment = sample_segments[i];
578 const float factor = sample_factors[i];
579 const T value = math::interpolate(
580 src[guide_points[segment]], src[guide_points[segment + 1]], factor);
581 mixer.mix_in(points[i], value, neighbor_weight);
586 mixer.finalize(child_points_by_curve[range]);
590 dst_generic.finish();
597 if (iter.
is_builtin && !children_attributes.is_builtin(iter.
name)) {
600 if (guide_curve_attributes.contains(iter.
name)) {
603 if (attribute_filter.allow_skip(iter.
name)) {
606 if (iter.
data_type == bke::AttrType::String) {
610 const GAttributeReader src = iter.
get();
611 if (src.sharing_info && src.varray.is_span()) {
612 const bke::AttributeInitShared
init(src.varray.get_internal_span().data(),
617 children_attributes.add(
618 iter.
name, AttrDomain::Curve, iter.
data_type, bke::AttributeInitVArray(src.varray));
624 const std::optional<StringRef> &weight_attribute_id,
625 const std::optional<StringRef> &index_attribute_id,
626 const int max_neighbors,
631 if (!weight_attribute_id && !index_attribute_id) {
635 if (weight_attribute_id) {
638 *weight_attribute_id, AttrDomain::Curve);
641 if (index_attribute_id) {
643 *index_attribute_id, AttrDomain::Curve);
646 for (const int child_curve_i : range) {
647 const int neighbor_count = all_neighbor_counts[child_curve_i];
650 float closest_weight;
651 if (neighbor_count == 0) {
653 closest_weight = 0.0f;
656 const IndexRange neighbors_range{child_curve_i * max_neighbors, neighbor_count};
657 const Span<float> neighbor_weights = all_neighbor_weights.slice(neighbors_range);
658 const Span<int> neighbor_indices = all_neighbor_indices.slice(neighbors_range);
659 const int max_index = std::max_element(neighbor_weights.begin(), neighbor_weights.end()) -
660 neighbor_weights.begin();
661 closest_index = neighbor_indices[max_index];
662 closest_weight = neighbor_weights[max_index];
664 if (index_attribute) {
665 index_attribute.span[child_curve_i] = closest_index;
667 if (weight_attribute) {
668 weight_attribute.span[child_curve_i] = closest_weight;
672 if (index_attribute) {
673 index_attribute.finish();
675 if (weight_attribute) {
676 weight_attribute.finish();
681 const Curves &guide_curves_id,
687 const int max_neighbors,
689 const std::optional<StringRef> &index_attribute_id,
690 const std::optional<StringRef> &weight_attribute_id)
696 guides_by_group, guide_curves);
700 for (KDTree_3d *kdtree : kdtrees.
values()) {
701 BLI_kdtree_3d_free(kdtree);
706 const int num_child_curves = point_attributes.
domain_size(AttrDomain::Point);
710 Array<int> all_neighbor_indices(num_child_curves * max_neighbors);
711 Array<float> all_neighbor_weights(num_child_curves * max_neighbors);
712 Array<int> all_neighbor_counts(num_child_curves);
719 all_neighbor_indices,
720 all_neighbor_weights,
721 all_neighbor_counts);
727 Array<bool> use_direct_interpolation_per_child(num_child_curves);
730 points_per_curve_by_group,
731 all_neighbor_indices,
732 all_neighbor_weights,
736 use_direct_interpolation_per_child);
738 const int num_child_points = children_curve_offsets.
last();
739 child_curves.
resize(num_child_points, num_child_curves);
745 guide_curves, parameterized_guide_offsets, parameterized_guide_lengths);
750 all_neighbor_indices,
751 all_neighbor_weights,
757 parameterized_guide_lengths,
758 use_direct_interpolation_per_child);
764 all_neighbor_indices,
765 all_neighbor_weights,
768 parameterized_guide_lengths,
769 use_direct_interpolation_per_child);
776 all_neighbor_indices,
777 all_neighbor_weights);
779 if (guide_curves_id.
mat !=
nullptr) {
797 params.set_default_remaining_outputs();
801 if (points_component ==
nullptr) {
802 points_component = points_geometry.get_component<
MeshComponent>();
804 if (points_component ==
nullptr || points_geometry.
is_empty()) {
805 params.set_default_remaining_outputs();
809 const int max_neighbors = std::max<int>(1,
params.extract_input<
int>(
"Max Neighbors"));
811 static auto normalize_fn = mf::build::SI1_SO<float3, float3>(
814 mf::build::exec_presets::AllSpanOrSingle());
828 fn::FieldEvaluator curves_evaluator{curves_context, guide_curves_id.geometry.curve_num};
829 curves_evaluator.
add(guides_up_field);
830 curves_evaluator.
add(guide_group_field);
838 points_evaluator.
add(points_up_field);
839 points_evaluator.
add(point_group_field);
846 std::optional<std::string> index_attribute_id =
847 params.get_output_anonymous_attribute_id_if_needed(
"Closest Index");
848 std::optional<std::string> weight_attribute_id =
849 params.get_output_anonymous_attribute_id_if_needed(
"Closest Weight");
860 weight_attribute_id);
863 if (
const auto *curve_edit_data =
866 new_curves.
add(*curve_edit_data);
868 new_curves.
name = guide_curves_geometry.
name;
870 params.set_output(
"Curves", std::move(new_curves));
878 ntype.
ui_name =
"Interpolate Curves";
879 ntype.
ui_description =
"Generate new curves on points by interpolating between existing curves";
Low-level operations for curves.
#define NODE_CLASS_GEOMETRY
#define GEO_NODE_INTERPOLATE_CURVES
A KD-tree for nearest neighbor search.
#define BLI_SCOPED_DEFER(function_to_defer)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
ATTR_WARN_UNUSED_RESULT const BMVert * v
void reinitialize(const int64_t new_size)
MutableSpan< T > typed() const
ValueIterator values() const &
bool add(const Key &key, const Value &value)
void add_new(const Key &key, const Value &value)
MapType::ItemIterator items() const
void add(const Key &key, const Value &value)
constexpr MutableSpan drop_back(const int64_t n) const
constexpr IndexRange index_range() const
constexpr T & last(const int64_t n=0) const
constexpr int64_t size() const
constexpr IndexRange index_range() const
IndexRange index_range() const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
GAttributeReader lookup(const StringRef attribute_id) const
int domain_size(const AttrDomain domain) const
GAttributeReader get() const
MutableSpan< float3 > positions_for_write()
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
MutableAttributeAccessor attributes_for_write()
Span< int > offsets() const
Span< float3 > positions() const
void resize(int points_num, int curves_num)
AttributeAccessor attributes() const
MutableSpan< int > offsets_for_write()
int attribute_domain_size(AttrDomain domain) const
virtual std::optional< AttributeAccessor > attributes() const
virtual bool is_empty() const
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, AttrType data_type)
int add(GField field, GVArray *varray_ptr)
const GVArray & get_evaluated(const int field_index) const
static std::shared_ptr< FieldOperation > from(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
static void remember_deformed_positions_if_necessary(GeometrySet &geometry)
void * MEM_dupallocN(const void *vmemh)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
typename DefaultMixerStruct< T >::type DefaultMixer
void node_register_type(bNodeType &ntype)
Curves * curves_new_nomain(int points_num, int curves_num)
void debug_randomize_curve_order(bke::CurvesGeometry *curves)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
static MultiValueMap< int, int > separate_guides_by_group(const VArray< int > &guide_group_ids)
static void parameterize_guide_curves(const bke::CurvesGeometry &guide_curves, Array< int > &r_parameterized_guide_offsets, Array< float > &r_parameterized_guide_lengths)
static void node_declare(NodeDeclarationBuilder &b)
static void find_neighbor_guides(const Span< float3 > positions, const VArray< int > point_group_ids, const Map< int, KDTree_3d * > kdtrees, const MultiValueMap< int, int > &guides_by_group, const int max_neighbor_count, MutableSpan< int > r_all_neighbor_indices, MutableSpan< float > r_all_neighbor_weights, MutableSpan< int > r_all_neighbor_counts)
static void node_register()
static void interpolate_curve_attributes(bke::CurvesGeometry &child_curves, const bke::CurvesGeometry &guide_curves, const AttributeAccessor &point_attributes, const AttributeFilter &attribute_filter, const int max_neighbors, const Span< int > all_neighbor_indices, const Span< float > all_neighbor_weights, const Span< int > all_neighbor_counts, const OffsetIndices< int > parameterized_guide_offsets, const Span< float > parameterized_guide_lengths, const Span< bool > use_direct_interpolation_per_child)
static void interpolate_curve_shapes(bke::CurvesGeometry &child_curves, const bke::CurvesGeometry &guide_curves, const int max_neighbors, const Span< int > all_neighbor_indices, const Span< float > all_neighbor_weights, const Span< int > all_neighbor_counts, const VArray< float3 > &guides_up, const VArray< float3 > &points_up, const Span< float3 > point_positions, const OffsetIndices< int > parameterized_guide_offsets, const Span< float > parameterized_guide_lengths, const Span< bool > use_direct_interpolation_per_child)
static Map< int, int > compute_points_per_curve_by_group(const MultiValueMap< int, int > &guides_by_group, const bke::CurvesGeometry &guide_curves)
static GeometrySet generate_interpolated_curves(const Curves &guide_curves_id, const AttributeAccessor &point_attributes, const VArray< float3 > &guides_up, const VArray< float3 > &points_up, const VArray< int > &guide_group_ids, const VArray< int > &point_group_ids, const int max_neighbors, const AttributeFilter &attribute_filter, const std::optional< StringRef > &index_attribute_id, const std::optional< StringRef > &weight_attribute_id)
static void compute_point_counts_per_child(const bke::CurvesGeometry &guide_curves, const VArray< int > &point_group_ids, const Map< int, int > &points_per_curve_by_group, const Span< int > all_neighbor_indices, const Span< float > all_neighbor_weights, const Span< int > all_neighbor_counts, const int max_neighbors, MutableSpan< int > r_points_per_child, MutableSpan< bool > r_use_direct_interpolation)
static void node_geo_exec(GeoNodeExecParams params)
static void store_output_attributes(bke::CurvesGeometry &child_curves, const std::optional< StringRef > &weight_attribute_id, const std::optional< StringRef > &index_attribute_id, const int max_neighbors, const Span< int > all_neighbor_counts, const Span< int > all_neighbor_indices, const Span< float > all_neighbor_weights)
static Map< int, KDTree_3d * > build_kdtrees_for_root_positions(const MultiValueMap< int, int > &guides_by_group, const bke::CurvesGeometry &guide_curves)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void parallel_for_each(Range &&range, const Function &function)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
static void init(bNodeTree *, bNode *node)
static GeometrySet from_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
bool allow_skip(const StringRef name) const
const GeometryComponent * get_component(GeometryComponent::Type component_type) const
const Curves * get_curves() const
void add(const GeometryComponent &component)
std::string ui_description
NodeGeometryExecFunction geometry_node_execute
const char * enum_name_legacy
NodeDeclareFunction declare