Blender V5.0
node_geo_curve_sample.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_math_color.hh"
7#include "BLI_math_vector.hh"
8
11
12#include "BKE_curves.hh"
13
15#include "UI_resources.hh"
16
18
19#include "node_geometry_util.hh"
20
22
24
26{
27 b.add_input<decl::Geometry>("Curves")
29 .supported_type(GeometryComponent::Type::Curve)
30 .description("Curves to sample positions on");
31
32 if (const bNode *node = b.node_or_null()) {
33 const NodeGeometryCurveSample &storage = node_storage(*node);
34 b.add_input(eCustomDataType(storage.data_type), "Value").hide_value().field_on_all();
35 }
36
37 auto &factor = b.add_input<decl::Float>("Factor")
38 .min(0.0f)
39 .max(1.0f)
41 .supports_field()
42 .structure_type(StructureType::Dynamic)
43 .make_available([](bNode &node) {
44 node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR;
45 });
46 auto &length = b.add_input<decl::Float>("Length")
47 .min(0.0f)
49 .supports_field()
50 .structure_type(StructureType::Dynamic)
51 .make_available([](bNode &node) {
52 node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH;
53 });
54 auto &index = b.add_input<decl::Int>("Curve Index")
55 .supports_field()
56 .structure_type(StructureType::Dynamic)
57 .make_available(
58 [](bNode &node) { node_storage(node).use_all_curves = false; });
59
60 if (const bNode *node = b.node_or_null()) {
61 const NodeGeometryCurveSample &storage = node_storage(*node);
62 const GeometryNodeCurveSampleMode mode = GeometryNodeCurveSampleMode(storage.mode);
63 b.add_output(eCustomDataType(storage.data_type), "Value").dependent_field({2, 3, 4});
64
65 factor.available(mode == GEO_NODE_CURVE_SAMPLE_FACTOR);
66 length.available(mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
67 index.available(!storage.use_all_curves);
68 }
69
70 b.add_output<decl::Vector>("Position").dependent_field({2, 3, 4});
71 b.add_output<decl::Vector>("Tangent").dependent_field({2, 3, 4});
72 b.add_output<decl::Vector>("Normal").dependent_field({2, 3, 4});
73}
74
75static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
76{
77 layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
78 layout->prop(ptr, "mode", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
79 layout->prop(ptr, "use_all_curves", UI_ITEM_NONE, std::nullopt, ICON_NONE);
80}
81
82static void node_init(bNodeTree * /*tree*/, bNode *node)
83{
86 data->use_all_curves = false;
87 data->data_type = CD_PROP_FLOAT;
88 node->storage = data;
89}
90
92{
93 const NodeDeclaration &declaration = *params.node_type().static_declaration;
94 search_link_ops_for_declarations(params, declaration.inputs.as_span().take_front(1));
95 search_link_ops_for_declarations(params, declaration.inputs.as_span().take_back(3));
96 search_link_ops_for_declarations(params, declaration.outputs.as_span().take_back(3));
97
98 const std::optional<eCustomDataType> type = bke::socket_type_to_custom_data_type(
99 eNodeSocketDatatype(params.other_socket().type));
100 if (type && *type != CD_PROP_STRING) {
101 /* The input and output sockets have the same name. */
102 params.add_item(IFACE_("Value"), [type](LinkSearchOpParams &params) {
103 bNode &node = params.add_node("GeometryNodeSampleCurve");
104 node_storage(node).data_type = *type;
105 params.update_and_connect_available_socket(node, "Value");
106 });
107 }
108}
109
110static void sample_indices_and_lengths(const Span<float> accumulated_lengths,
111 const Span<float> sample_lengths,
112 const GeometryNodeCurveSampleMode length_mode,
113 const IndexMask &mask,
114 MutableSpan<int> r_segment_indices,
115 MutableSpan<float> r_length_in_segment)
116{
117 const float total_length = accumulated_lengths.last();
119
120 mask.foreach_index_optimized<int>([&](const int i) {
121 const float sample_length = length_mode == GEO_NODE_CURVE_SAMPLE_FACTOR ?
122 sample_lengths[i] * total_length :
123 sample_lengths[i];
124 int segment_i;
125 float factor_in_segment;
126 length_parameterize::sample_at_length(accumulated_lengths,
127 std::clamp(sample_length, 0.0f, total_length),
128 segment_i,
129 factor_in_segment,
130 &hint);
131 const float segment_start = segment_i == 0 ? 0.0f : accumulated_lengths[segment_i - 1];
132 const float segment_end = accumulated_lengths[segment_i];
133 const float segment_length = segment_end - segment_start;
134
135 r_segment_indices[i] = segment_i;
136 r_length_in_segment[i] = factor_in_segment * segment_length;
137 });
138}
139
140static void sample_indices_and_factors_to_compressed(const Span<float> accumulated_lengths,
141 const Span<float> sample_lengths,
142 const GeometryNodeCurveSampleMode length_mode,
143 const IndexMask &mask,
144 MutableSpan<int> r_segment_indices,
145 MutableSpan<float> r_factor_in_segment)
146{
147 const float total_length = accumulated_lengths.last();
149
150 switch (length_mode) {
152 mask.foreach_index_optimized<int>([&](const int i, const int pos) {
153 const float length = sample_lengths[i] * total_length;
154 length_parameterize::sample_at_length(accumulated_lengths,
155 std::clamp(length, 0.0f, total_length),
156 r_segment_indices[pos],
157 r_factor_in_segment[pos],
158 &hint);
159 });
160 break;
162 mask.foreach_index_optimized<int>([&](const int i, const int pos) {
163 const float length = sample_lengths[i];
164 length_parameterize::sample_at_length(accumulated_lengths,
165 std::clamp(length, 0.0f, total_length),
166 r_segment_indices[pos],
167 r_factor_in_segment[pos],
168 &hint);
169 });
170 break;
171 }
172}
173
178class SampleFloatSegmentsFunction : public mf::MultiFunction {
179 private:
180 Array<float> accumulated_lengths_;
181 GeometryNodeCurveSampleMode length_mode_;
182
183 public:
185 const GeometryNodeCurveSampleMode length_mode)
186 : accumulated_lengths_(std::move(accumulated_lengths)), length_mode_(length_mode)
187 {
188 static const mf::Signature signature = []() {
189 mf::Signature signature;
190 mf::SignatureBuilder builder{"Sample Curve Index", signature};
191 builder.single_input<float>("Length");
192
193 builder.single_output<int>("Curve Index");
194 builder.single_output<float>("Length in Curve");
195 return signature;
196 }();
197 this->set_signature(&signature);
198 }
199
200 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
201 {
202 const VArraySpan<float> lengths = params.readonly_single_input<float>(0, "Length");
203 MutableSpan<int> indices = params.uninitialized_single_output<int>(1, "Curve Index");
204 MutableSpan<float> lengths_in_segments = params.uninitialized_single_output<float>(
205 2, "Length in Curve");
206
208 accumulated_lengths_, lengths, length_mode_, mask, indices, lengths_in_segments);
209 }
210};
211
212class SampleCurveFunction : public mf::MultiFunction {
213 private:
219 GeometrySet geometry_set_;
220 GField src_field_;
221 GeometryNodeCurveSampleMode length_mode_;
222
223 mf::Signature signature_;
224
225 std::optional<bke::CurvesFieldContext> source_context_;
226 std::unique_ptr<FieldEvaluator> source_evaluator_;
227 const GVArray *source_data_;
228
229 public:
231 const GeometryNodeCurveSampleMode length_mode,
232 const GField &src_field)
233 : geometry_set_(std::move(geometry_set)), src_field_(src_field), length_mode_(length_mode)
234 {
235 mf::SignatureBuilder builder{"Sample Curve", signature_};
236 builder.single_input<int>("Curve Index");
237 builder.single_input<float>("Length");
238 builder.single_output<float3>("Position", mf::ParamFlag::SupportsUnusedOutput);
239 builder.single_output<float3>("Tangent", mf::ParamFlag::SupportsUnusedOutput);
240 builder.single_output<float3>("Normal", mf::ParamFlag::SupportsUnusedOutput);
241 builder.single_output("Value", src_field_.cpp_type(), mf::ParamFlag::SupportsUnusedOutput);
242 this->set_signature(&signature_);
243
244 this->evaluate_source();
245 }
246
247 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
248 {
249 MutableSpan<float3> sampled_positions = params.uninitialized_single_output_if_required<float3>(
250 2, "Position");
251 MutableSpan<float3> sampled_tangents = params.uninitialized_single_output_if_required<float3>(
252 3, "Tangent");
253 MutableSpan<float3> sampled_normals = params.uninitialized_single_output_if_required<float3>(
254 4, "Normal");
255 GMutableSpan sampled_values = params.uninitialized_single_output_if_required(5, "Value");
256
257 auto return_default = [&]() {
258 if (!sampled_positions.is_empty()) {
259 index_mask::masked_fill(sampled_positions, {0, 0, 0}, mask);
260 }
261 if (!sampled_tangents.is_empty()) {
262 index_mask::masked_fill(sampled_tangents, {0, 0, 0}, mask);
263 }
264 if (!sampled_normals.is_empty()) {
265 index_mask::masked_fill(sampled_normals, {0, 0, 0}, mask);
266 }
267 };
268
269 if (!geometry_set_.has_curves()) {
270 return_default();
271 return;
272 }
273
274 const Curves &curves_id = *geometry_set_.get_curves();
275 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
276 if (curves.is_empty()) {
277 return_default();
278 return;
279 }
281 Span<float3> evaluated_positions = curves.evaluated_positions();
282 Span<float3> evaluated_tangents;
283 Span<float3> evaluated_normals;
284 if (!sampled_tangents.is_empty()) {
285 evaluated_tangents = curves.evaluated_tangents();
286 }
287 if (!sampled_normals.is_empty()) {
288 evaluated_normals = curves.evaluated_normals();
289 }
290
291 const OffsetIndices points_by_curve = curves.points_by_curve();
292 const OffsetIndices evaluated_points_by_curve = curves.evaluated_points_by_curve();
293 const VArray<int> curve_indices = params.readonly_single_input<int>(0, "Curve Index");
294 const VArraySpan<float> lengths = params.readonly_single_input<float>(1, "Length");
295 const VArray<bool> cyclic = curves.cyclic();
296
298 Array<float> factors;
299 GArray<> src_original_values(source_data_->type());
300 GArray<> src_evaluated_values(source_data_->type());
301
302 auto fill_invalid = [&](const IndexMask &mask) {
303 if (!sampled_positions.is_empty()) {
304 index_mask::masked_fill(sampled_positions, float3(0), mask);
305 }
306 if (!sampled_tangents.is_empty()) {
307 index_mask::masked_fill(sampled_tangents, float3(0), mask);
308 }
309 if (!sampled_normals.is_empty()) {
310 index_mask::masked_fill(sampled_normals, float3(0), mask);
311 }
312 if (!sampled_values.is_empty()) {
313 bke::attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) {
314 using T = decltype(dummy);
315 index_mask::masked_fill<T>(sampled_values.typed<T>(), {}, mask);
316 });
317 }
318 };
319
320 auto sample_curve = [&](const int curve_i, const IndexMask &mask) {
321 const IndexRange evaluated_points = evaluated_points_by_curve[curve_i];
322 if (evaluated_points.size() == 1) {
323 if (!sampled_positions.is_empty()) {
325 sampled_positions, evaluated_positions[evaluated_points.first()], mask);
326 }
327 if (!sampled_tangents.is_empty()) {
329 sampled_tangents, evaluated_tangents[evaluated_points.first()], mask);
330 }
331 if (!sampled_normals.is_empty()) {
333 sampled_normals, evaluated_normals[evaluated_points.first()], mask);
334 }
335 if (!sampled_values.is_empty()) {
336 bke::attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) {
337 using T = decltype(dummy);
338 const T &value = source_data_->typed<T>()[points_by_curve[curve_i].first()];
339 index_mask::masked_fill<T>(sampled_values.typed<T>(), value, mask);
340 });
341 }
342 return;
343 }
344
345 const Span<float> accumulated_lengths = curves.evaluated_lengths_for_curve(curve_i,
346 cyclic[curve_i]);
347 if (accumulated_lengths.is_empty()) {
348 /* Sanity check in case of invalid evaluation (for example NURBS with invalid order). */
349 fill_invalid(mask);
350 return;
351 }
352
353 /* Store the sampled indices and factors in arrays the size of the mask.
354 * Then, during interpolation, move the results back to the masked indices. */
355 indices.reinitialize(mask.size());
356 factors.reinitialize(mask.size());
358 accumulated_lengths, lengths, length_mode_, mask, indices, factors);
359
360 if (!sampled_positions.is_empty()) {
361 length_parameterize::interpolate_to_masked<float3>(
362 evaluated_positions.slice(evaluated_points),
363 indices,
364 factors,
365 mask,
366 sampled_positions);
367 }
368 if (!sampled_tangents.is_empty()) {
369 length_parameterize::interpolate_to_masked<float3>(
370 evaluated_tangents.slice(evaluated_points), indices, factors, mask, sampled_tangents);
371 mask.foreach_index(
372 [&](const int i) { sampled_tangents[i] = math::normalize(sampled_tangents[i]); });
373 }
374 if (!sampled_normals.is_empty()) {
375 length_parameterize::interpolate_to_masked<float3>(
376 evaluated_normals.slice(evaluated_points), indices, factors, mask, sampled_normals);
377 mask.foreach_index(
378 [&](const int i) { sampled_normals[i] = math::normalize(sampled_normals[i]); });
379 }
380 if (!sampled_values.is_empty()) {
381 const IndexRange points = points_by_curve[curve_i];
382 src_original_values.reinitialize(points.size());
383 source_data_->materialize_compressed_to_uninitialized(points, src_original_values.data());
384 src_evaluated_values.reinitialize(evaluated_points.size());
385 curves.interpolate_to_evaluated(curve_i, src_original_values, src_evaluated_values);
386 bke::attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) {
387 using T = decltype(dummy);
388 const Span<T> src_evaluated_values_typed = src_evaluated_values.as_span().typed<T>();
389 MutableSpan<T> sampled_values_typed = sampled_values.typed<T>();
390 length_parameterize::interpolate_to_masked<T>(
391 src_evaluated_values_typed, indices, factors, mask, sampled_values_typed);
392 });
393 }
394 };
395
396 if (const std::optional<int> curve_i = curve_indices.get_if_single()) {
397 if (curves.curves_range().contains(*curve_i)) {
398 sample_curve(*curve_i, mask);
399 }
400 else {
401 fill_invalid(mask);
402 }
403 }
404 else {
405 Vector<int> valid_indices;
406 Vector<int> invalid_indices;
407 VectorSet<int> used_curves;
408 devirtualize_varray(curve_indices, [&](const auto curve_indices) {
409 mask.foreach_index([&](const int i) {
410 const int curve_i = curve_indices[i];
411 if (curves.curves_range().contains(curve_i)) {
412 used_curves.add(curve_i);
413 valid_indices.append(i);
414 }
415 else {
416 invalid_indices.append(i);
417 }
418 });
419 });
420
421 IndexMaskMemory memory;
422 const IndexMask valid_indices_mask = valid_indices.size() == mask.size() ?
423 mask :
424 IndexMask::from_indices(valid_indices.as_span(),
425 memory);
426 Array<IndexMask> mask_by_curve(used_curves.size());
428 valid_indices_mask,
429 memory,
430 [&](const int i) { return used_curves.index_of(curve_indices[i]); },
431 mask_by_curve);
432
433 for (const int i : mask_by_curve.index_range()) {
434 sample_curve(used_curves[i], mask_by_curve[i]);
435 }
436 fill_invalid(IndexMask::from_indices<int>(invalid_indices, memory));
437 }
438 }
439
440 private:
441 void evaluate_source()
442 {
443 const Curves &curves_id = *geometry_set_.get_curves();
444 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
445 source_context_.emplace(bke::CurvesFieldContext{curves_id, AttrDomain::Point});
446 source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, curves.points_num());
447 source_evaluator_->add(src_field_);
448 source_evaluator_->evaluate();
449 source_data_ = &source_evaluator_->get_evaluated(0);
450 }
451};
452
454{
455
456 Array<float> curve_lengths(curves.curves_num());
457 const VArray<bool> cyclic = curves.cyclic();
458 float length = 0.0f;
459 for (const int i : curves.curves_range()) {
460 length += curves.evaluated_length_total_for_curve(i, cyclic[i]);
461 curve_lengths[i] = length;
462 }
463 return curve_lengths;
464}
465
467{
468 GeometrySet geometry_set = params.extract_input<GeometrySet>("Curves");
469 if (!geometry_set.has_curves()) {
470 params.set_default_remaining_outputs();
471 return;
472 }
473
474 const Curves &curves_id = *geometry_set.get_curves();
475 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
476 if (curves.is_empty()) {
477 params.set_default_remaining_outputs();
478 return;
479 }
480
482
483 const NodeGeometryCurveSample &storage = node_storage(params.node());
485
486 const StringRef length_input_name = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? "Factor" : "Length";
487 auto sample_length = params.extract_input<bke::SocketValueVariant>(length_input_name);
488
489 GField src_values_field = params.extract_input<GField>("Value");
490
491 std::string error_message;
492
497 std::shared_ptr<FieldOperation> sample_op;
498 if (curves.curves_num() == 1) {
501 std::make_unique<SampleCurveFunction>(
502 std::move(geometry_set), mode, std::move(src_values_field)),
503 {&curve_index, &sample_length},
504 {&position, &tangent, &normal, &value},
505 params.user_data(),
506 error_message))
507 {
508 params.set_default_remaining_outputs();
509 params.error_message_add(NodeWarningType::Error, std::move(error_message));
510 return;
511 }
512 }
513 else {
514 if (storage.use_all_curves) {
515 bke::SocketValueVariant curve_index;
516 bke::SocketValueVariant length_in_curve;
517 if (!execute_multi_function_on_value_variant(std::make_unique<SampleFloatSegmentsFunction>(
518 curve_accumulated_lengths(curves), mode),
519 {&sample_length},
520 {&curve_index, &length_in_curve},
521 params.user_data(),
522 error_message))
523 {
524 params.set_default_remaining_outputs();
525 params.error_message_add(NodeWarningType::Error, std::move(error_message));
526 return;
527 }
529 std::make_shared<SampleCurveFunction>(std::move(geometry_set),
531 std::move(src_values_field)),
532 {&curve_index, &length_in_curve},
533 {&position, &tangent, &normal, &value},
534 params.user_data(),
535 error_message))
536 {
537 params.set_default_remaining_outputs();
538 params.error_message_add(NodeWarningType::Error, std::move(error_message));
539 return;
540 }
541 }
542 else {
543 auto curve_index = params.extract_input<bke::SocketValueVariant>("Curve Index");
545 std::make_shared<SampleCurveFunction>(
546 std::move(geometry_set), mode, std::move(src_values_field)),
547 {&curve_index, &sample_length},
548 {&position, &tangent, &normal, &value},
549 params.user_data(),
550 error_message))
551 {
552 params.set_default_remaining_outputs();
553 params.error_message_add(NodeWarningType::Error, std::move(error_message));
554 return;
555 }
556 }
557 }
558
559 params.set_output("Position", std::move(position));
560 params.set_output("Tangent", std::move(tangent));
561 params.set_output("Normal", std::move(normal));
562 params.set_output("Value", std::move(value));
563}
564
565static void node_register()
566{
567 static blender::bke::bNodeType ntype;
568
569 geo_node_type_base(&ntype, "GeometryNodeSampleCurve", GEO_NODE_SAMPLE_CURVE);
570 ntype.ui_name = "Sample Curve";
571 ntype.ui_description =
572 "Retrieve data from a point on a curve at a certain distance from its start";
573 ntype.enum_name_legacy = "SAMPLE_CURVE";
576 ntype.declare = node_declare;
577 ntype.initfunc = node_init;
579 ntype, "NodeGeometryCurveSample", node_free_standard_storage, node_copy_standard_storage);
583}
584NOD_REGISTER_NODE(node_register)
585
586} // namespace blender::nodes::node_geo_curve_sample_cc
Low-level operations for curves.
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_SAMPLE_CURVE
#define IFACE_(msgid)
@ CD_PROP_FLOAT
@ CD_PROP_STRING
GeometryNodeCurveSampleMode
@ GEO_NODE_CURVE_SAMPLE_FACTOR
@ GEO_NODE_CURVE_SAMPLE_LENGTH
eNodeSocketDatatype
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_DISTANCE
Definition RNA_types.hh:256
@ PROP_FACTOR
Definition RNA_types.hh:251
@ UI_ITEM_R_EXPAND
#define UI_ITEM_NONE
BMesh const char void * data
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
static void from_groups(const IndexMask &universe, IndexMaskMemory &memory, Fn &&get_group_index, MutableSpan< IndexMask > r_masks)
constexpr int64_t size() const
constexpr bool is_empty() const
Definition BLI_span.hh:260
bool add(const Key &key)
int64_t size() const
int64_t index_of(const Key &key) const
int64_t size() const
void append(const T &value)
Span< T > as_span() const
void materialize_compressed_to_uninitialized(const IndexMask &mask, void *dst) const
constexpr int64_t first() const
constexpr int64_t size() const
constexpr bool is_empty() const
Definition BLI_span.hh:509
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
OffsetIndices< int > points_by_curve() const
void ensure_can_interpolate_to_evaluated() const
IndexRange curves_range() const
Span< float3 > evaluated_tangents() const
Span< float3 > evaluated_normals() const
OffsetIndices< int > evaluated_points_by_curve() const
float evaluated_length_total_for_curve(int curve_index, bool cyclic) const
Span< float3 > evaluated_positions() const
VArray< bool > cyclic() const
static SocketValueVariant From(T &&value)
void set_signature(const Signature *signature)
Vector< SocketDeclaration * > inputs
Vector< SocketDeclaration * > outputs
SampleCurveFunction(GeometrySet geometry_set, const GeometryNodeCurveSampleMode length_mode, const GField &src_field)
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
SampleFloatSegmentsFunction(Array< float > accumulated_lengths, const GeometryNodeCurveSampleMode length_mode)
static ushort indices[]
uint pos
float length(VecOp< float, D >) RET
VecBase< float, 3 > float3
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:5414
std::optional< eCustomDataType > socket_type_to_custom_data_type(eNodeSocketDatatype type)
Definition node.cc:5144
GField make_constant_field(const CPPType &type, const void *value)
Definition field.cc:528
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
void sample_at_length(const Span< float > accumulated_segment_lengths, const float sample_length, int &r_segment_index, float &r_factor, SampleSegmentHint *hint=nullptr)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
static void sample_indices_and_factors_to_compressed(const Span< float > accumulated_lengths, const Span< float > sample_lengths, const GeometryNodeCurveSampleMode length_mode, const IndexMask &mask, MutableSpan< int > r_segment_indices, MutableSpan< float > r_factor_in_segment)
static Array< float > curve_accumulated_lengths(const bke::CurvesGeometry &curves)
static void node_declare(NodeDeclarationBuilder &b)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_geo_exec(GeoNodeExecParams params)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void sample_indices_and_lengths(const Span< float > accumulated_lengths, const Span< float > sample_lengths, const GeometryNodeCurveSampleMode length_mode, const IndexMask &mask, MutableSpan< int > r_segment_indices, MutableSpan< float > r_length_in_segment)
static void node_init(bNodeTree *, bNode *node)
void search_link_ops_for_declarations(GatherLinkSearchOpParams &params, Span< SocketDeclaration * > declarations)
bool execute_multi_function_on_value_variant(const MultiFunction &fn, const std::shared_ptr< MultiFunction > &owned_fn, const Span< SocketValueVariant * > input_values, const Span< SocketValueVariant * > output_values, GeoNodesUserData *user_data, std::string &r_error_message)
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:42
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:54
CurvesGeometry geometry
void * storage
const Curves * get_curves() const
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:259
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:378
NodeDeclareFunction declare
Definition BKE_node.hh:362
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238