Blender V4.5
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
14#include "UI_interface.hh"
15#include "UI_resources.hh"
16
18
19#include "node_geometry_util.hh"
20
22
24
26{
27 b.add_input<decl::Geometry>("Curves").only_realized_data().supported_type(
28 GeometryComponent::Type::Curve);
29
30 if (const bNode *node = b.node_or_null()) {
31 const NodeGeometryCurveSample &storage = node_storage(*node);
32 b.add_input(eCustomDataType(storage.data_type), "Value").hide_value().field_on_all();
33 }
34
35 auto &factor = b.add_input<decl::Float>("Factor")
36 .min(0.0f)
37 .max(1.0f)
39 .supports_field()
40 .make_available([](bNode &node) {
41 node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR;
42 });
43 auto &length = b.add_input<decl::Float>("Length")
44 .min(0.0f)
46 .supports_field()
47 .make_available([](bNode &node) {
48 node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH;
49 });
50 auto &index =
51 b.add_input<decl::Int>("Curve Index").supports_field().make_available([](bNode &node) {
52 node_storage(node).use_all_curves = false;
53 });
54
55 if (const bNode *node = b.node_or_null()) {
56 const NodeGeometryCurveSample &storage = node_storage(*node);
57 const GeometryNodeCurveSampleMode mode = GeometryNodeCurveSampleMode(storage.mode);
58 b.add_output(eCustomDataType(storage.data_type), "Value").dependent_field({2, 3, 4});
59
60 factor.available(mode == GEO_NODE_CURVE_SAMPLE_FACTOR);
61 length.available(mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
62 index.available(!storage.use_all_curves);
63 }
64
65 b.add_output<decl::Vector>("Position").dependent_field({2, 3, 4});
66 b.add_output<decl::Vector>("Tangent").dependent_field({2, 3, 4});
67 b.add_output<decl::Vector>("Normal").dependent_field({2, 3, 4});
68}
69
70static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
71{
72 layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
73 layout->prop(ptr, "mode", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
74 layout->prop(ptr, "use_all_curves", UI_ITEM_NONE, std::nullopt, ICON_NONE);
75}
76
77static void node_init(bNodeTree * /*tree*/, bNode *node)
78{
81 data->use_all_curves = false;
82 data->data_type = CD_PROP_FLOAT;
83 node->storage = data;
84}
85
87{
88 const NodeDeclaration &declaration = *params.node_type().static_declaration;
89 search_link_ops_for_declarations(params, declaration.inputs.as_span().take_front(1));
90 search_link_ops_for_declarations(params, declaration.inputs.as_span().take_back(3));
91 search_link_ops_for_declarations(params, declaration.outputs.as_span().take_back(3));
92
93 const std::optional<eCustomDataType> type = bke::socket_type_to_custom_data_type(
94 eNodeSocketDatatype(params.other_socket().type));
95 if (type && *type != CD_PROP_STRING) {
96 /* The input and output sockets have the same name. */
97 params.add_item(IFACE_("Value"), [type](LinkSearchOpParams &params) {
98 bNode &node = params.add_node("GeometryNodeSampleCurve");
99 node_storage(node).data_type = *type;
100 params.update_and_connect_available_socket(node, "Value");
101 });
102 }
103}
104
105static void sample_indices_and_lengths(const Span<float> accumulated_lengths,
106 const Span<float> sample_lengths,
107 const GeometryNodeCurveSampleMode length_mode,
108 const IndexMask &mask,
109 MutableSpan<int> r_segment_indices,
110 MutableSpan<float> r_length_in_segment)
111{
112 const float total_length = accumulated_lengths.last();
114
115 mask.foreach_index_optimized<int>([&](const int i) {
116 const float sample_length = length_mode == GEO_NODE_CURVE_SAMPLE_FACTOR ?
117 sample_lengths[i] * total_length :
118 sample_lengths[i];
119 int segment_i;
120 float factor_in_segment;
121 length_parameterize::sample_at_length(accumulated_lengths,
122 std::clamp(sample_length, 0.0f, total_length),
123 segment_i,
124 factor_in_segment,
125 &hint);
126 const float segment_start = segment_i == 0 ? 0.0f : accumulated_lengths[segment_i - 1];
127 const float segment_end = accumulated_lengths[segment_i];
128 const float segment_length = segment_end - segment_start;
129
130 r_segment_indices[i] = segment_i;
131 r_length_in_segment[i] = factor_in_segment * segment_length;
132 });
133}
134
135static void sample_indices_and_factors_to_compressed(const Span<float> accumulated_lengths,
136 const Span<float> sample_lengths,
137 const GeometryNodeCurveSampleMode length_mode,
138 const IndexMask &mask,
139 MutableSpan<int> r_segment_indices,
140 MutableSpan<float> r_factor_in_segment)
141{
142 const float total_length = accumulated_lengths.last();
144
145 switch (length_mode) {
147 mask.foreach_index_optimized<int>([&](const int i, const int pos) {
148 const float length = sample_lengths[i] * total_length;
149 length_parameterize::sample_at_length(accumulated_lengths,
150 std::clamp(length, 0.0f, total_length),
151 r_segment_indices[pos],
152 r_factor_in_segment[pos],
153 &hint);
154 });
155 break;
157 mask.foreach_index_optimized<int>([&](const int i, const int pos) {
158 const float length = sample_lengths[i];
159 length_parameterize::sample_at_length(accumulated_lengths,
160 std::clamp(length, 0.0f, total_length),
161 r_segment_indices[pos],
162 r_factor_in_segment[pos],
163 &hint);
164 });
165 break;
166 }
167}
168
173class SampleFloatSegmentsFunction : public mf::MultiFunction {
174 private:
175 Array<float> accumulated_lengths_;
176 GeometryNodeCurveSampleMode length_mode_;
177
178 public:
180 const GeometryNodeCurveSampleMode length_mode)
181 : accumulated_lengths_(std::move(accumulated_lengths)), length_mode_(length_mode)
182 {
183 static const mf::Signature signature = []() {
184 mf::Signature signature;
185 mf::SignatureBuilder builder{"Sample Curve Index", signature};
186 builder.single_input<float>("Length");
187
188 builder.single_output<int>("Curve Index");
189 builder.single_output<float>("Length in Curve");
190 return signature;
191 }();
192 this->set_signature(&signature);
193 }
194
195 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
196 {
197 const VArraySpan<float> lengths = params.readonly_single_input<float>(0, "Length");
198 MutableSpan<int> indices = params.uninitialized_single_output<int>(1, "Curve Index");
199 MutableSpan<float> lengths_in_segments = params.uninitialized_single_output<float>(
200 2, "Length in Curve");
201
203 accumulated_lengths_, lengths, length_mode_, mask, indices, lengths_in_segments);
204 }
205};
206
207class SampleCurveFunction : public mf::MultiFunction {
208 private:
214 GeometrySet geometry_set_;
215 GField src_field_;
216 GeometryNodeCurveSampleMode length_mode_;
217
218 mf::Signature signature_;
219
220 std::optional<bke::CurvesFieldContext> source_context_;
221 std::unique_ptr<FieldEvaluator> source_evaluator_;
222 const GVArray *source_data_;
223
224 public:
226 const GeometryNodeCurveSampleMode length_mode,
227 const GField &src_field)
228 : geometry_set_(std::move(geometry_set)), src_field_(src_field), length_mode_(length_mode)
229 {
230 mf::SignatureBuilder builder{"Sample Curve", signature_};
231 builder.single_input<int>("Curve Index");
232 builder.single_input<float>("Length");
233 builder.single_output<float3>("Position", mf::ParamFlag::SupportsUnusedOutput);
234 builder.single_output<float3>("Tangent", mf::ParamFlag::SupportsUnusedOutput);
235 builder.single_output<float3>("Normal", mf::ParamFlag::SupportsUnusedOutput);
236 builder.single_output("Value", src_field_.cpp_type(), mf::ParamFlag::SupportsUnusedOutput);
237 this->set_signature(&signature_);
238
239 this->evaluate_source();
240 }
241
242 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
243 {
244 MutableSpan<float3> sampled_positions = params.uninitialized_single_output_if_required<float3>(
245 2, "Position");
246 MutableSpan<float3> sampled_tangents = params.uninitialized_single_output_if_required<float3>(
247 3, "Tangent");
248 MutableSpan<float3> sampled_normals = params.uninitialized_single_output_if_required<float3>(
249 4, "Normal");
250 GMutableSpan sampled_values = params.uninitialized_single_output_if_required(5, "Value");
251
252 auto return_default = [&]() {
253 if (!sampled_positions.is_empty()) {
254 index_mask::masked_fill(sampled_positions, {0, 0, 0}, mask);
255 }
256 if (!sampled_tangents.is_empty()) {
257 index_mask::masked_fill(sampled_tangents, {0, 0, 0}, mask);
258 }
259 if (!sampled_normals.is_empty()) {
260 index_mask::masked_fill(sampled_normals, {0, 0, 0}, mask);
261 }
262 };
263
264 if (!geometry_set_.has_curves()) {
265 return_default();
266 return;
267 }
268
269 const Curves &curves_id = *geometry_set_.get_curves();
270 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
271 if (curves.is_empty()) {
272 return_default();
273 return;
274 }
276 Span<float3> evaluated_positions = curves.evaluated_positions();
277 Span<float3> evaluated_tangents;
278 Span<float3> evaluated_normals;
279 if (!sampled_tangents.is_empty()) {
280 evaluated_tangents = curves.evaluated_tangents();
281 }
282 if (!sampled_normals.is_empty()) {
283 evaluated_normals = curves.evaluated_normals();
284 }
285
286 const OffsetIndices points_by_curve = curves.points_by_curve();
287 const OffsetIndices evaluated_points_by_curve = curves.evaluated_points_by_curve();
288 const VArray<int> curve_indices = params.readonly_single_input<int>(0, "Curve Index");
289 const VArraySpan<float> lengths = params.readonly_single_input<float>(1, "Length");
290 const VArray<bool> cyclic = curves.cyclic();
291
293 Array<float> factors;
294 GArray<> src_original_values(source_data_->type());
295 GArray<> src_evaluated_values(source_data_->type());
296
297 auto fill_invalid = [&](const IndexMask &mask) {
298 if (!sampled_positions.is_empty()) {
299 index_mask::masked_fill(sampled_positions, float3(0), mask);
300 }
301 if (!sampled_tangents.is_empty()) {
302 index_mask::masked_fill(sampled_tangents, float3(0), mask);
303 }
304 if (!sampled_normals.is_empty()) {
305 index_mask::masked_fill(sampled_normals, float3(0), mask);
306 }
307 if (!sampled_values.is_empty()) {
308 bke::attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) {
309 using T = decltype(dummy);
310 index_mask::masked_fill<T>(sampled_values.typed<T>(), {}, mask);
311 });
312 }
313 };
314
315 auto sample_curve = [&](const int curve_i, const IndexMask &mask) {
316 const IndexRange evaluated_points = evaluated_points_by_curve[curve_i];
317 if (evaluated_points.size() == 1) {
318 if (!sampled_positions.is_empty()) {
320 sampled_positions, evaluated_positions[evaluated_points.first()], mask);
321 }
322 if (!sampled_tangents.is_empty()) {
324 sampled_tangents, evaluated_tangents[evaluated_points.first()], mask);
325 }
326 if (!sampled_normals.is_empty()) {
328 sampled_normals, evaluated_normals[evaluated_points.first()], mask);
329 }
330 if (!sampled_values.is_empty()) {
331 bke::attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) {
332 using T = decltype(dummy);
333 const T &value = source_data_->typed<T>()[points_by_curve[curve_i].first()];
334 index_mask::masked_fill<T>(sampled_values.typed<T>(), value, mask);
335 });
336 }
337 return;
338 }
339
340 const Span<float> accumulated_lengths = curves.evaluated_lengths_for_curve(curve_i,
341 cyclic[curve_i]);
342 if (accumulated_lengths.is_empty()) {
343 /* Sanity check in case of invalid evaluation (for example NURBS with invalid order). */
344 fill_invalid(mask);
345 return;
346 }
347
348 /* Store the sampled indices and factors in arrays the size of the mask.
349 * Then, during interpolation, move the results back to the masked indices. */
350 indices.reinitialize(mask.size());
351 factors.reinitialize(mask.size());
353 accumulated_lengths, lengths, length_mode_, mask, indices, factors);
354
355 if (!sampled_positions.is_empty()) {
356 length_parameterize::interpolate_to_masked<float3>(
357 evaluated_positions.slice(evaluated_points),
358 indices,
359 factors,
360 mask,
361 sampled_positions);
362 }
363 if (!sampled_tangents.is_empty()) {
364 length_parameterize::interpolate_to_masked<float3>(
365 evaluated_tangents.slice(evaluated_points), indices, factors, mask, sampled_tangents);
366 mask.foreach_index(
367 [&](const int i) { sampled_tangents[i] = math::normalize(sampled_tangents[i]); });
368 }
369 if (!sampled_normals.is_empty()) {
370 length_parameterize::interpolate_to_masked<float3>(
371 evaluated_normals.slice(evaluated_points), indices, factors, mask, sampled_normals);
372 mask.foreach_index(
373 [&](const int i) { sampled_normals[i] = math::normalize(sampled_normals[i]); });
374 }
375 if (!sampled_values.is_empty()) {
376 const IndexRange points = points_by_curve[curve_i];
377 src_original_values.reinitialize(points.size());
378 source_data_->materialize_compressed_to_uninitialized(points, src_original_values.data());
379 src_evaluated_values.reinitialize(evaluated_points.size());
380 curves.interpolate_to_evaluated(curve_i, src_original_values, src_evaluated_values);
381 bke::attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) {
382 using T = decltype(dummy);
383 const Span<T> src_evaluated_values_typed = src_evaluated_values.as_span().typed<T>();
384 MutableSpan<T> sampled_values_typed = sampled_values.typed<T>();
385 length_parameterize::interpolate_to_masked<T>(
386 src_evaluated_values_typed, indices, factors, mask, sampled_values_typed);
387 });
388 }
389 };
390
391 if (const std::optional<int> curve_i = curve_indices.get_if_single()) {
392 if (curves.curves_range().contains(*curve_i)) {
393 sample_curve(*curve_i, mask);
394 }
395 else {
396 fill_invalid(mask);
397 }
398 }
399 else {
400 Vector<int> valid_indices;
401 Vector<int> invalid_indices;
402 VectorSet<int> used_curves;
403 devirtualize_varray(curve_indices, [&](const auto curve_indices) {
404 mask.foreach_index([&](const int i) {
405 const int curve_i = curve_indices[i];
406 if (curves.curves_range().contains(curve_i)) {
407 used_curves.add(curve_i);
408 valid_indices.append(i);
409 }
410 else {
411 invalid_indices.append(i);
412 }
413 });
414 });
415
416 IndexMaskMemory memory;
417 const IndexMask valid_indices_mask = valid_indices.size() == mask.size() ?
418 mask :
419 IndexMask::from_indices(valid_indices.as_span(),
420 memory);
421 Array<IndexMask> mask_by_curve(used_curves.size());
423 valid_indices_mask,
424 memory,
425 [&](const int i) { return used_curves.index_of(curve_indices[i]); },
426 mask_by_curve);
427
428 for (const int i : mask_by_curve.index_range()) {
429 sample_curve(used_curves[i], mask_by_curve[i]);
430 }
431 fill_invalid(IndexMask::from_indices<int>(invalid_indices, memory));
432 }
433 }
434
435 private:
436 void evaluate_source()
437 {
438 const Curves &curves_id = *geometry_set_.get_curves();
439 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
440 source_context_.emplace(bke::CurvesFieldContext{curves_id, AttrDomain::Point});
441 source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, curves.points_num());
442 source_evaluator_->add(src_field_);
443 source_evaluator_->evaluate();
444 source_data_ = &source_evaluator_->get_evaluated(0);
445 }
446};
447
449{
450
451 Array<float> curve_lengths(curves.curves_num());
452 const VArray<bool> cyclic = curves.cyclic();
453 float length = 0.0f;
454 for (const int i : curves.curves_range()) {
455 length += curves.evaluated_length_total_for_curve(i, cyclic[i]);
456 curve_lengths[i] = length;
457 }
458 return curve_lengths;
459}
460
462{
463 GeometrySet geometry_set = params.extract_input<GeometrySet>("Curves");
464 if (!geometry_set.has_curves()) {
465 params.set_default_remaining_outputs();
466 return;
467 }
468
469 const Curves &curves_id = *geometry_set.get_curves();
470 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
471 if (curves.is_empty()) {
472 params.set_default_remaining_outputs();
473 return;
474 }
475
477
478 const NodeGeometryCurveSample &storage = node_storage(params.node());
480
481 Field<float> length_field = params.extract_input<Field<float>>(
482 mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? "Factor" : "Length");
483 GField src_values_field = params.extract_input<GField>("Value");
484
485 std::shared_ptr<FieldOperation> sample_op;
486 if (curves.curves_num() == 1) {
487 sample_op = FieldOperation::Create(
488 std::make_unique<SampleCurveFunction>(
489 std::move(geometry_set), mode, std::move(src_values_field)),
490 {fn::make_constant_field<int>(0), std::move(length_field)});
491 }
492 else {
493 if (storage.use_all_curves) {
494 auto index_fn = std::make_unique<SampleFloatSegmentsFunction>(
495 curve_accumulated_lengths(curves), mode);
496 auto index_op = FieldOperation::Create(std::move(index_fn), {std::move(length_field)});
497 Field<int> curve_index = Field<int>(index_op, 0);
498 Field<float> length_in_curve = Field<float>(index_op, 1);
499 sample_op = FieldOperation::Create(
500 std::make_unique<SampleCurveFunction>(
501 std::move(geometry_set), GEO_NODE_CURVE_SAMPLE_LENGTH, std::move(src_values_field)),
502 {std::move(curve_index), std::move(length_in_curve)});
503 }
504 else {
505 Field<int> curve_index = params.extract_input<Field<int>>("Curve Index");
506 Field<float> length_in_curve = std::move(length_field);
507 sample_op = FieldOperation::Create(
508 std::make_unique<SampleCurveFunction>(
509 std::move(geometry_set), mode, std::move(src_values_field)),
510 {std::move(curve_index), std::move(length_in_curve)});
511 }
512 }
513
514 params.set_output("Position", Field<float3>(sample_op, 0));
515 params.set_output("Tangent", Field<float3>(sample_op, 1));
516 params.set_output("Normal", Field<float3>(sample_op, 2));
517 params.set_output("Value", GField(sample_op, 3));
518}
519
520static void node_register()
521{
522 static blender::bke::bNodeType ntype;
523
524 geo_node_type_base(&ntype, "GeometryNodeSampleCurve", GEO_NODE_SAMPLE_CURVE);
525 ntype.ui_name = "Sample Curve";
526 ntype.ui_description =
527 "Retrieve data from a point on a curve at a certain distance from its start";
528 ntype.enum_name_legacy = "SAMPLE_CURVE";
531 ntype.declare = node_declare;
532 ntype.initfunc = node_init;
534 ntype, "NodeGeometryCurveSample", node_free_standard_storage, node_copy_standard_storage);
538}
539NOD_REGISTER_NODE(node_register)
540
541} // namespace blender::nodes::node_geo_curve_sample_cc
Low-level operations for curves.
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1215
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:447
#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:244
@ PROP_FACTOR
Definition RNA_types.hh:239
@ 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
void set_signature(const Signature *signature)
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:242
Vector< SocketDeclaration * > inputs
Vector< SocketDeclaration * > outputs
void make_available(bNode &node) const
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
#define this
VecBase< float, 3 > float3
float length(VecOp< float, D >) RET
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:2748
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:5603
std::optional< eCustomDataType > socket_type_to_custom_data_type(eNodeSocketDatatype type)
Definition node.cc:5355
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)
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:226
std::string ui_description
Definition BKE_node.hh:232
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:277
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:347
const char * enum_name_legacy
Definition BKE_node.hh:235
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:247
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:371
NodeDeclareFunction declare
Definition BKE_node.hh:355
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:4227