23 .description(
"Base curves that new curves are interpolated between");
27 .
description(
"Optional up vector that is typically a surface normal");
32 "Splits guides into separate groups. New curves interpolate existing curves "
33 "from a single group");
35 "First control point positions for new interpolated curves");
39 .
description(
"Optional up vector that is typically a surface normal");
48 "Maximum amount of close guide curves that are taken into account for interpolation");
52 .
description(
"Index of the closest guide curve for each generated curve");
55 .
description(
"Weight of the closest guide curve for each generated curve");
65 for (
const int curve_i : guide_group_ids.
index_range()) {
66 const int group = guide_group_ids[curve_i];
67 guides_by_group.
add(group, curve_i);
69 return guides_by_group;
81 for (
const auto &[group, guide_curve_indices] : guides_by_group.
items()) {
82 int group_control_points = points_by_curve[guide_curve_indices[0]].
size();
83 for (
const int guide_curve_i : guide_curve_indices.as_span().drop_front(1)) {
84 const int control_points = points_by_curve[guide_curve_i].
size();
85 if (group_control_points != control_points) {
86 group_control_points = -1;
90 if (group_control_points != -1) {
91 points_per_curve_by_group.
add(group, group_control_points);
94 return points_per_curve_by_group;
107 for (
const auto item : guides_by_group.
items()) {
108 const int group = item.key;
109 const Span<int> guide_indices = item.value;
111 KDTree_3d *kdtree = BLI_kdtree_3d_new(guide_indices.
size());
112 kdtrees.
add_new(group, kdtree);
114 for (
const int curve_i : guide_indices) {
115 const int first_point_i = offsets[curve_i];
116 const float3 &root_pos = positions[first_point_i];
117 BLI_kdtree_3d_insert(kdtree, curve_i, root_pos);
121 [](KDTree_3d *kdtree) { BLI_kdtree_3d_balance(kdtree); });
133 const int max_neighbor_count,
139 for (const int child_curve_i : range) {
140 const float3 &position = positions[child_curve_i];
141 const int group = point_group_ids[child_curve_i];
142 const KDTree_3d *kdtree = kdtrees.lookup_default(group, nullptr);
143 if (kdtree == nullptr) {
144 r_all_neighbor_counts[child_curve_i] = 0;
148 const int num_guides_in_group = guides_by_group.lookup(group).size();
152 const bool use_extra_neighbor = num_guides_in_group > max_neighbor_count;
153 const int neighbors_to_find = max_neighbor_count + use_extra_neighbor;
155 Vector<KDTreeNearest_3d, 16> nearest_n(neighbors_to_find);
156 const int num_neighbors = BLI_kdtree_3d_find_nearest_n(
157 kdtree, position, nearest_n.data(), neighbors_to_find);
158 if (num_neighbors == 0) {
159 r_all_neighbor_counts[child_curve_i] = 0;
163 const IndexRange neighbors_range{child_curve_i * max_neighbor_count, max_neighbor_count};
164 MutableSpan<int> neighbor_indices = r_all_neighbor_indices.slice(neighbors_range);
165 MutableSpan<float> neighbor_weights = r_all_neighbor_weights.slice(neighbors_range);
167 float tot_weight = 0.0f;
169 if (use_extra_neighbor) {
172 const float max_distance = std::max_element(
174 nearest_n.begin() + num_neighbors,
175 [](const KDTreeNearest_3d &a, const KDTreeNearest_3d &b) {
176 return a.dist < b.dist;
179 if (max_distance == 0.0f) {
180 r_all_neighbor_counts[child_curve_i] = 1;
181 neighbor_indices[0] = nearest_n[0].index;
182 neighbor_weights[0] = 1.0f;
186 int neighbor_counter = 0;
187 for (const int neighbor_i : IndexRange(num_neighbors)) {
188 const KDTreeNearest_3d &nearest = nearest_n[neighbor_i];
192 const float weight = (max_distance - nearest.dist) / std::max(nearest.dist, 0.000001f);
194 tot_weight += weight;
195 neighbor_indices[neighbor_counter] = nearest.index;
196 neighbor_weights[neighbor_counter] = weight;
200 r_all_neighbor_counts[child_curve_i] = neighbor_counter;
203 int neighbor_counter = 0;
204 for (const int neighbor_i : IndexRange(num_neighbors)) {
205 const KDTreeNearest_3d &nearest = nearest_n[neighbor_i];
209 const float weight = 1.0f / std::max(nearest.dist, 0.000001f);
211 tot_weight += weight;
212 neighbor_indices[neighbor_counter] = nearest.index;
213 neighbor_weights[neighbor_counter] = weight;
217 r_all_neighbor_counts[child_curve_i] = neighbor_counter;
219 if (tot_weight > 0.0f) {
221 const float weight_factor = 1.0f / tot_weight;
222 for (float &weight : neighbor_weights.take_front(r_all_neighbor_counts[child_curve_i])) {
223 weight *= weight_factor;
240 const int max_neighbors,
246 for (const int child_curve_i : range) {
247 const int neighbor_count = all_neighbor_counts[child_curve_i];
248 if (neighbor_count == 0) {
249 r_points_per_child[child_curve_i] = 1;
250 r_use_direct_interpolation[child_curve_i] = false;
253 const int group = point_group_ids[child_curve_i];
254 const int points_per_curve_in_group = points_per_curve_by_group.lookup_default(group, -1);
255 if (points_per_curve_in_group != -1) {
256 r_points_per_child[child_curve_i] = points_per_curve_in_group;
257 r_use_direct_interpolation[child_curve_i] = true;
260 const IndexRange neighbors_range{child_curve_i * max_neighbors, neighbor_count};
261 const Span<float> neighbor_weights = all_neighbor_weights.slice(neighbors_range);
262 const Span<int> neighbor_indices = all_neighbor_indices.slice(neighbors_range);
264 float neighbor_points_weighted_sum = 0.0f;
265 for (const int neighbor_i : IndexRange(neighbor_count)) {
266 const int neighbor_index = neighbor_indices[neighbor_i];
267 const float neighbor_weight = neighbor_weights[neighbor_i];
268 const int neighbor_points = guide_points_by_curve[neighbor_index].size();
269 neighbor_points_weighted_sum += neighbor_weight * float(neighbor_points);
271 const int points_in_child = std::max<int>(1, roundf(neighbor_points_weighted_sum));
272 r_points_per_child[child_curve_i] = points_in_child;
273 r_use_direct_interpolation[child_curve_i] = false;
288 for (const int guide_curve_i : range) {
289 r_parameterized_guide_offsets[guide_curve_i] = length_parameterize::segments_num(
290 guide_points_by_curve[guide_curve_i].size(), false);
296 r_parameterized_guide_lengths.reinitialize(r_parameterized_guide_offsets.last());
297 const Span<float3> guide_positions = guide_curves.positions();
299 for (const int guide_curve_i : range) {
300 const IndexRange points = guide_points_by_curve[guide_curve_i];
301 const IndexRange lengths_range = parameterize_offsets[guide_curve_i];
302 length_parameterize::accumulate_lengths<float3>(
303 guide_positions.slice(points),
305 r_parameterized_guide_lengths.as_mutable_span().slice(lengths_range));
315 const int max_neighbors,
324 const Span<bool> use_direct_interpolation_per_child)
332 Vector<float, 16> sample_lengths;
333 Vector<int, 16> sample_segments;
334 Vector<float, 16> sample_factors;
336 for (const int child_curve_i : range) {
337 const IndexRange points = child_points_by_curve[child_curve_i];
338 const int neighbor_count = all_neighbor_counts[child_curve_i];
339 const float3 child_up = points_up[child_curve_i];
340 BLI_assert(math::is_unit_scale(child_up));
341 const float3 &child_root_position = point_positions[child_curve_i];
342 MutableSpan<float3> child_positions = children_positions.slice(points);
344 child_positions.fill(child_root_position);
345 if (neighbor_count == 0) {
350 const IndexRange neighbors_range{child_curve_i * max_neighbors, neighbor_count};
351 const Span<float> neighbor_weights = all_neighbor_weights.slice(neighbors_range);
352 const Span<int> neighbor_indices = all_neighbor_indices.slice(neighbors_range);
354 const bool use_direct_interpolation = use_direct_interpolation_per_child[child_curve_i];
356 for (const int neighbor_i : IndexRange(neighbor_count)) {
357 const int neighbor_index = neighbor_indices[neighbor_i];
358 const float neighbor_weight = neighbor_weights[neighbor_i];
359 const IndexRange guide_points = guide_points_by_curve[neighbor_index];
360 const Span<float3> neighbor_positions = guide_positions.slice(guide_points);
361 const float3 &neighbor_root = neighbor_positions.first();
362 const float3 neighbor_up = guides_up[neighbor_index];
363 BLI_assert(math::is_unit_scale(neighbor_up));
365 const bool is_same_up_vector = neighbor_up == child_up;
367 float3x3 normal_rotation;
368 if (!is_same_up_vector) {
369 rotation_between_vecs_to_mat3(normal_rotation.ptr(), neighbor_up, child_up);
372 if (use_direct_interpolation) {
376 for (const int i : IndexRange(points.size())) {
377 const float3 &neighbor_pos = neighbor_positions[i];
378 const float3 relative_to_root = neighbor_pos - neighbor_root;
379 float3 rotated_relative = relative_to_root;
380 if (!is_same_up_vector) {
381 rotated_relative = normal_rotation * rotated_relative;
383 child_positions[i] += neighbor_weight * rotated_relative;
390 const IndexRange guide_offsets = parameterized_guide_offsets[neighbor_index];
392 if (guide_offsets.is_empty()) {
394 float3 rotated_relative = neighbor_root;
395 if (!is_same_up_vector) {
396 rotated_relative = normal_rotation * rotated_relative;
398 const float3 global_pos = rotated_relative * neighbor_weight;
399 for (float3 &position : child_positions) {
400 position += global_pos;
405 const Span<float> lengths = parameterized_guide_lengths.slice(guide_offsets);
406 const float neighbor_length = lengths.last();
408 sample_lengths.reinitialize(points.size());
409 const float sample_length_factor = math::safe_divide(neighbor_length,
410 float(points.size() - 1));
411 for (const int i : sample_lengths.index_range()) {
412 sample_lengths[i] = i * sample_length_factor;
415 sample_segments.reinitialize(points.size());
416 sample_factors.reinitialize(points.size());
417 length_parameterize::sample_at_lengths(
418 lengths, sample_lengths, sample_segments, sample_factors);
420 for (const int i : IndexRange(points.size())) {
421 const int segment = sample_segments[i];
422 const float factor = sample_factors[i];
423 const float3 sample_pos = math::interpolate(
424 neighbor_positions[segment], neighbor_positions[segment + 1], factor);
425 const float3 relative_to_root = sample_pos - neighbor_root;
426 float3 rotated_relative = relative_to_root;
427 if (!is_same_up_vector) {
428 rotated_relative = normal_rotation * rotated_relative;
430 child_positions[i] += neighbor_weight * rotated_relative;
448 const int max_neighbors,
454 const Span<bool> use_direct_interpolation_per_child)
476 if (iter.
domain == AttrDomain::Curve) {
477 const GVArraySpan src_generic = *iter.
get(AttrDomain::Curve, type);
480 iter.
name, AttrDomain::Curve, type);
485 using T =
decltype(dummy);
491 for (const int child_curve_i : range) {
492 const int neighbor_count = all_neighbor_counts[child_curve_i];
493 const IndexRange neighbors_range{child_curve_i * max_neighbors, neighbor_count};
494 const Span<float> neighbor_weights = all_neighbor_weights.slice(neighbors_range);
495 const Span<int> neighbor_indices = all_neighbor_indices.slice(neighbors_range);
497 for (const int neighbor_i : IndexRange(neighbor_count)) {
498 const int neighbor_index = neighbor_indices[neighbor_i];
499 const float neighbor_weight = neighbor_weights[neighbor_i];
500 mixer.mix_in(child_curve_i, src[neighbor_index], neighbor_weight);
503 mixer.finalize(range);
507 dst_generic.finish();
511 const GVArraySpan src_generic = *iter.
get(AttrDomain::Point, type);
513 iter.
name, AttrDomain::Point, type);
519 using T =
decltype(dummy);
525 Vector<float, 16> sample_lengths;
526 Vector<int, 16> sample_segments;
527 Vector<float, 16> sample_factors;
528 for (const int child_curve_i : range) {
529 const IndexRange points = child_points_by_curve[child_curve_i];
530 const int neighbor_count = all_neighbor_counts[child_curve_i];
531 const IndexRange neighbors_range{child_curve_i * max_neighbors, neighbor_count};
532 const Span<float> neighbor_weights = all_neighbor_weights.slice(neighbors_range);
533 const Span<int> neighbor_indices = all_neighbor_indices.slice(neighbors_range);
534 const bool use_direct_interpolation =
535 use_direct_interpolation_per_child[child_curve_i];
537 for (const int neighbor_i : IndexRange(neighbor_count)) {
538 const int neighbor_index = neighbor_indices[neighbor_i];
539 const float neighbor_weight = neighbor_weights[neighbor_i];
540 const IndexRange guide_points = guide_points_by_curve[neighbor_index];
542 if (use_direct_interpolation) {
543 for (const int i : IndexRange(points.size())) {
544 mixer.mix_in(points[i], src[guide_points[i]], neighbor_weight);
548 const IndexRange guide_offsets = parameterized_guide_offsets[neighbor_index];
549 if (guide_offsets.is_empty()) {
551 const T &curve_value = src[guide_points.first()];
552 for (const int i : points) {
553 mixer.mix_in(i, curve_value, neighbor_weight);
558 const Span<float> lengths = parameterized_guide_lengths.slice(guide_offsets);
559 const float neighbor_length = lengths.last();
561 sample_lengths.reinitialize(points.size());
562 const float sample_length_factor = math::safe_divide(neighbor_length,
563 float(points.size() - 1));
564 for (const int i : sample_lengths.index_range()) {
565 sample_lengths[i] = i * sample_length_factor;
568 sample_segments.reinitialize(points.size());
569 sample_factors.reinitialize(points.size());
570 length_parameterize::sample_at_lengths(
571 lengths, sample_lengths, sample_segments, sample_factors);
573 for (const int i : IndexRange(points.size())) {
574 const int segment = sample_segments[i];
575 const float factor = sample_factors[i];
576 const T value = math::interpolate(
577 src[guide_points[segment]], src[guide_points[segment + 1]], factor);
578 mixer.mix_in(points[i], value, neighbor_weight);
583 mixer.finalize(child_points_by_curve[range]);
587 dst_generic.finish();
594 if (iter.
is_builtin && !children_attributes.is_builtin(iter.
name)) {
597 if (guide_curve_attributes.contains(iter.
name)) {
600 if (attribute_filter.allow_skip(iter.
name)) {
607 const GAttributeReader src = iter.
get();
608 if (src.sharing_info && src.varray.is_span()) {
609 const bke::AttributeInitShared
init(src.varray.get_internal_span().data(),
614 children_attributes.add(
615 iter.
name, AttrDomain::Curve, iter.
data_type, bke::AttributeInitVArray(src.varray));
621 const std::optional<StringRef> &weight_attribute_id,
622 const std::optional<StringRef> &index_attribute_id,
623 const int max_neighbors,
628 if (!weight_attribute_id && !index_attribute_id) {
632 if (weight_attribute_id) {
635 *weight_attribute_id, AttrDomain::Curve);
638 if (index_attribute_id) {
640 *index_attribute_id, AttrDomain::Curve);
643 for (const int child_curve_i : range) {
644 const int neighbor_count = all_neighbor_counts[child_curve_i];
647 float closest_weight;
648 if (neighbor_count == 0) {
650 closest_weight = 0.0f;
653 const IndexRange neighbors_range{child_curve_i * max_neighbors, neighbor_count};
654 const Span<float> neighbor_weights = all_neighbor_weights.slice(neighbors_range);
655 const Span<int> neighbor_indices = all_neighbor_indices.slice(neighbors_range);
656 const int max_index = std::max_element(neighbor_weights.begin(), neighbor_weights.end()) -
657 neighbor_weights.begin();
658 closest_index = neighbor_indices[max_index];
659 closest_weight = neighbor_weights[max_index];
661 if (index_attribute) {
662 index_attribute.span[child_curve_i] = closest_index;
664 if (weight_attribute) {
665 weight_attribute.span[child_curve_i] = closest_weight;
669 if (index_attribute) {
670 index_attribute.finish();
672 if (weight_attribute) {
673 weight_attribute.finish();
678 const Curves &guide_curves_id,
684 const int max_neighbors,
686 const std::optional<StringRef> &index_attribute_id,
687 const std::optional<StringRef> &weight_attribute_id)
693 guides_by_group, guide_curves);
697 for (KDTree_3d *kdtree : kdtrees.
values()) {
698 BLI_kdtree_3d_free(kdtree);
703 const int num_child_curves = point_attributes.
domain_size(AttrDomain::Point);
707 Array<int> all_neighbor_indices(num_child_curves * max_neighbors);
708 Array<float> all_neighbor_weights(num_child_curves * max_neighbors);
709 Array<int> all_neighbor_counts(num_child_curves);
716 all_neighbor_indices,
717 all_neighbor_weights,
718 all_neighbor_counts);
724 Array<bool> use_direct_interpolation_per_child(num_child_curves);
727 points_per_curve_by_group,
728 all_neighbor_indices,
729 all_neighbor_weights,
733 use_direct_interpolation_per_child);
735 const int num_child_points = children_curve_offsets.
last();
736 child_curves.
resize(num_child_points, num_child_curves);
742 guide_curves, parameterized_guide_offsets, parameterized_guide_lengths);
747 all_neighbor_indices,
748 all_neighbor_weights,
754 parameterized_guide_lengths,
755 use_direct_interpolation_per_child);
761 all_neighbor_indices,
762 all_neighbor_weights,
765 parameterized_guide_lengths,
766 use_direct_interpolation_per_child);
773 all_neighbor_indices,
774 all_neighbor_weights);
776 if (guide_curves_id.
mat !=
nullptr) {
794 params.set_default_remaining_outputs();
798 if (points_component ==
nullptr) {
799 points_component = points_geometry.get_component<
MeshComponent>();
801 if (points_component ==
nullptr || points_geometry.
is_empty()) {
802 params.set_default_remaining_outputs();
806 const int max_neighbors = std::max<int>(1,
params.extract_input<
int>(
"Max Neighbors"));
808 static auto normalize_fn = mf::build::SI1_SO<float3, float3>(
811 mf::build::exec_presets::AllSpanOrSingle());
825 fn::FieldEvaluator curves_evaluator{curves_context, guide_curves_id.geometry.curve_num};
826 curves_evaluator.
add(guides_up_field);
827 curves_evaluator.
add(guide_group_field);
835 points_evaluator.
add(points_up_field);
836 points_evaluator.
add(point_group_field);
843 std::optional<std::string> index_attribute_id =
844 params.get_output_anonymous_attribute_id_if_needed(
"Closest Index");
845 std::optional<std::string> weight_attribute_id =
846 params.get_output_anonymous_attribute_id_if_needed(
"Closest Weight");
857 weight_attribute_id);
860 if (
const auto *curve_edit_data =
863 new_curves.
add(*curve_edit_data);
865 new_curves.
name = guide_curves_geometry.
name;
867 params.set_output(
"Curves", std::move(new_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
bool add(const Key &key, const Value &value)
ValueIterator values() const
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
eCustomDataType data_type
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, eCustomDataType data_type)
int add(GField field, GVArray *varray_ptr)
const GVArray & get_evaluated(const int field_index) const
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
static void remember_deformed_positions_if_necessary(GeometrySet &geometry)
local_group_size(16, 16) .push_constant(Type b
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, int type, const char *name, short nclass)
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)
NodeGeometryExecFunction geometry_node_execute
NodeDeclareFunction declare