Blender V4.3
resample_curves.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_array_utils.hh"
6#include "BLI_math_color.hh"
8
10#include "BLI_task.hh"
11
12#include "FN_field.hh"
14
15#include "BKE_attribute_math.hh"
16#include "BKE_curves.hh"
17#include "BKE_curves_utils.hh"
19
21
22namespace blender::geometry {
23
25{
26 static auto max_one_fn = mf::build::SI1_SO<int, int>(
27 "Clamp Above One",
28 [](int value) { return std::max(1, value); },
29 mf::build::exec_presets::AllSpanOrSingle());
30 return fn::Field<int>(fn::FieldOperation::Create(max_one_fn, {count_field}));
31}
32
34{
35 static auto get_count_fn = mf::build::SI2_SO<float, float, int>(
36 "Length Input to Count",
37 [](const float curve_length, const float sample_length) {
38 /* Find the number of sampled segments by dividing the total length by
39 * the sample length. Then there is one more sampled point than segment. */
40 if (UNLIKELY(sample_length == 0.0f)) {
41 return 1;
42 }
43 const int count = int(curve_length / sample_length) + 1;
44 return std::max(1, count);
45 },
46 mf::build::exec_presets::AllSpanOrSingle());
47
48 auto get_count_op = fn::FieldOperation::Create(
49 get_count_fn,
50 {fn::Field<float>(std::make_shared<bke::CurveLengthFieldInput>()), length_field});
51
52 return fn::Field<int>(std::move(get_count_op));
53}
54
59static bool interpolate_attribute_to_curves(const StringRef attribute_id,
60 const std::array<int, CURVE_TYPES_NUM> &type_counts)
61{
62 if (bke::attribute_name_is_anonymous(attribute_id)) {
63 return true;
64 }
65 if (ELEM(attribute_id, "handle_type_left", "handle_type_right", "handle_left", "handle_right")) {
66 return type_counts[CURVE_TYPE_BEZIER] != 0;
67 }
68 if (ELEM(attribute_id, "nurbs_weight")) {
69 return type_counts[CURVE_TYPE_NURBS] != 0;
70 }
71 return true;
72}
73
77static bool interpolate_attribute_to_poly_curve(const StringRef attribute_id)
78{
79 static const Set<StringRef> no_interpolation{{
80 "handle_type_left",
81 "handle_type_right",
82 "handle_right",
83 "handle_left",
84 "nurbs_weight",
85 }};
86 return !no_interpolation.contains(attribute_id);
87}
88
93 const CurvesGeometry &src_curves,
94 CurvesGeometry &dst_curves,
98{
99 const bke::AttributeAccessor src_attributes = src_curves.attributes();
100 for (const int i : ids.index_range()) {
101 const bke::GAttributeReader src_attribute = src_attributes.lookup(ids[i],
103 src.append(src_attribute.varray);
104
106 src_attribute.varray.type());
107 bke::GSpanAttributeWriter dst_attribute =
109 ids[i], bke::AttrDomain::Point, data_type);
110 dst.append(dst_attribute.span);
111 dst_attributes.append(std::move(dst_attribute));
112 }
113}
114
129
134 const CurvesGeometry &src_curves,
135 CurvesGeometry &dst_curves,
136 AttributesForResample &result,
137 const ResampleCurvesOutputAttributeIDs &output_ids)
138{
140 VectorSet<StringRef> ids_no_interpolation;
141 src_curves.attributes().foreach_attribute([&](const bke::AttributeIter &iter) {
142 if (iter.domain != bke::AttrDomain::Point) {
143 return;
144 }
145 if (iter.data_type == CD_PROP_STRING) {
146 return;
147 }
148 if (!interpolate_attribute_to_curves(iter.name, dst_curves.curve_type_counts())) {
149 return;
150 }
152 ids.add_new(iter.name);
153 }
154 else {
155 ids_no_interpolation.add_new(iter.name);
156 }
157 });
158
159 /* Position is handled differently since it has non-generic interpolation for Bezier
160 * curves and because the evaluated positions are cached for each evaluated point. */
161 ids.remove_contained("position");
162
164 ids, src_curves, dst_curves, result.src, result.dst, result.dst_attributes);
165
166 /* Attributes that aren't interpolated like Bezier handles still have to be copied
167 * to the result when there are any unselected curves of the corresponding type. */
168 retrieve_attribute_spans(ids_no_interpolation,
169 src_curves,
170 dst_curves,
171 result.src_no_interpolation,
172 result.dst_no_interpolation,
173 result.dst_attributes);
174
175 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
176 if (output_ids.tangent_id) {
177 result.src_evaluated_tangents = src_curves.evaluated_tangents();
180 result.dst_tangents = dst_attribute.span.typed<float3>();
181 result.dst_attributes.append(std::move(dst_attribute));
182 }
183 if (output_ids.normal_id) {
184 result.src_evaluated_normals = src_curves.evaluated_normals();
187 result.dst_normals = dst_attribute.span.typed<float3>();
188 result.dst_attributes.append(std::move(dst_attribute));
189 }
190}
191
193 const IndexMask &unselected_curves,
194 const AttributesForResample &attributes,
195 CurvesGeometry &dst_curves)
196{
197 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
198 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
199 array_utils::copy_group_to_group(src_points_by_curve,
200 dst_points_by_curve,
201 unselected_curves,
202 src_curves.positions(),
203 dst_curves.positions_for_write());
204
205 for (const int i : attributes.src.index_range()) {
206 array_utils::copy_group_to_group(src_points_by_curve,
207 dst_points_by_curve,
208 unselected_curves,
209 attributes.src[i],
210 attributes.dst[i]);
211 }
212 for (const int i : attributes.src_no_interpolation.index_range()) {
213 array_utils::copy_group_to_group(src_points_by_curve,
214 dst_points_by_curve,
215 unselected_curves,
216 attributes.src_no_interpolation[i],
217 attributes.dst_no_interpolation[i]);
218 }
219
220 if (!attributes.dst_tangents.is_empty()) {
222 dst_points_by_curve, unselected_curves, float3(0), attributes.dst_tangents);
223 }
224 if (!attributes.dst_normals.is_empty()) {
226 dst_points_by_curve, unselected_curves, float3(0), attributes.dst_normals);
227 }
228}
229
231{
232 for (const int i : data.index_range()) {
233 data[i] = math::normalize(data[i]);
234 }
235}
236
237static void normalize_curve_point_data(const IndexMaskSegment curve_selection,
238 const OffsetIndices<int> points_by_curve,
240{
241 for (const int i_curve : curve_selection) {
242 normalize_span(data.slice(points_by_curve[i_curve]));
243 }
244}
245
252 /* Use a default alignment that works for all attribute types, and don't use the inline buffer
253 * because it doesn't necessarily have the correct alignment. */
255 alignas(AllocatorType::min_alignment) std::array<std::byte, 1024> inline_buffer;
256
257 template<typename T> MutableSpan<T> resize(const int64_t size)
258 {
259 const int64_t size_in_bytes = sizeof(T) * size;
260 if (size_in_bytes <= this->inline_buffer.size()) {
261 return MutableSpan<std::byte>(this->inline_buffer).slice(0, size_in_bytes).cast<T>();
262 }
263 this->heap_allocated.resize(size_in_bytes);
264 return this->heap_allocated.as_mutable_span().cast<T>();
265 }
266};
267
268static void resample_to_uniform(const CurvesGeometry &src_curves,
269 const IndexMask &selection,
270 const ResampleCurvesOutputAttributeIDs &output_ids,
271 CurvesGeometry &dst_curves)
272{
273 if (src_curves.curves_range().is_empty()) {
274 return;
275 }
276
277 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
278 const OffsetIndices evaluated_points_by_curve = src_curves.evaluated_points_by_curve();
279 const VArray<bool> curves_cyclic = src_curves.cyclic();
280 const VArray<int8_t> curve_types = src_curves.curve_types();
281 const Span<float3> evaluated_positions = src_curves.evaluated_positions();
282
283 /* All resampled curves are poly curves. */
284 dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
285
286 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
287
288 AttributesForResample attributes;
289 gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes, output_ids);
290
291 src_curves.ensure_evaluated_lengths();
292
293 /* Sampling arbitrary attributes works by first interpolating them to the curve's standard
294 * "evaluated points" and then interpolating that result with the uniform samples. This is
295 * potentially wasteful when down-sampling a curve to many fewer points. There are two possible
296 * solutions: only sample the necessary points for interpolation, or first sample curve
297 * parameter/segment indices and evaluate the curve directly. */
298 Array<int> sample_indices(dst_curves.points_num());
299 Array<float> sample_factors(dst_curves.points_num());
300
301 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
302
303 /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on
304 * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a
305 * time or one curve at a time. */
306 selection.foreach_segment(GrainSize(512), [&](const IndexMaskSegment selection_segment) {
307 EvalDataBuffer evaluated_buffer;
308
309 /* Gather uniform samples based on the accumulated lengths of the original curve. */
310 for (const int i_curve : selection_segment) {
311 const bool cyclic = curves_cyclic[i_curve];
312 const IndexRange dst_points = dst_points_by_curve[i_curve];
313 const Span<float> lengths = src_curves.evaluated_lengths_for_curve(i_curve, cyclic);
314 if (lengths.is_empty()) {
315 /* Handle curves with only one evaluated point. */
316 sample_indices.as_mutable_span().slice(dst_points).fill(0);
317 sample_factors.as_mutable_span().slice(dst_points).fill(0.0f);
318 }
319 else {
321 !curves_cyclic[i_curve],
322 sample_indices.as_mutable_span().slice(dst_points),
323 sample_factors.as_mutable_span().slice(dst_points));
324 }
325 }
326
327 /* For every attribute, evaluate attributes from every curve in the range in the original
328 * curve's "evaluated points", then use linear interpolation to sample to the result. */
329 for (const int i_attribute : attributes.dst.index_range()) {
330 const CPPType &type = attributes.src[i_attribute].type();
331 bke::attribute_math::convert_to_static_type(type, [&](auto dummy) {
332 using T = decltype(dummy);
333 Span<T> src = attributes.src[i_attribute].typed<T>();
334 MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>();
335
336 for (const int i_curve : selection_segment) {
337 const IndexRange src_points = src_points_by_curve[i_curve];
338 const IndexRange dst_points = dst_points_by_curve[i_curve];
339
340 if (curve_types[i_curve] == CURVE_TYPE_POLY) {
342 sample_indices.as_span().slice(dst_points),
343 sample_factors.as_span().slice(dst_points),
344 dst.slice(dst_points));
345 }
346 else {
347 MutableSpan evaluated = evaluated_buffer.resize<T>(
348 evaluated_points_by_curve[i_curve].size());
349 src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated);
350
351 length_parameterize::interpolate(evaluated.as_span(),
352 sample_indices.as_span().slice(dst_points),
353 sample_factors.as_span().slice(dst_points),
354 dst.slice(dst_points));
355 }
356 }
357 });
358 }
359
360 auto interpolate_evaluated_data = [&](const Span<float3> src, MutableSpan<float3> dst) {
361 for (const int i_curve : selection_segment) {
362 const IndexRange src_points = evaluated_points_by_curve[i_curve];
363 const IndexRange dst_points = dst_points_by_curve[i_curve];
365 sample_indices.as_span().slice(dst_points),
366 sample_factors.as_span().slice(dst_points),
367 dst.slice(dst_points));
368 }
369 };
370
371 /* Interpolate the evaluated positions to the resampled curves. */
372 interpolate_evaluated_data(evaluated_positions, dst_positions);
373
374 if (!attributes.dst_tangents.is_empty()) {
375 interpolate_evaluated_data(attributes.src_evaluated_tangents, attributes.dst_tangents);
376 normalize_curve_point_data(selection_segment, dst_points_by_curve, attributes.dst_tangents);
377 }
378 if (!attributes.dst_normals.is_empty()) {
379 interpolate_evaluated_data(attributes.src_evaluated_normals, attributes.dst_normals);
380 normalize_curve_point_data(selection_segment, dst_points_by_curve, attributes.dst_normals);
381 }
382
383 /* Fill the default value for non-interpolating attributes that still must be copied. */
384 for (GMutableSpan dst : attributes.dst_no_interpolation) {
385 for (const int i_curve : selection_segment) {
386 const IndexRange dst_points = dst_points_by_curve[i_curve];
387 dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size());
388 }
389 }
390 });
391
392 IndexMaskMemory memory;
393 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
394 copy_or_defaults_for_unselected_curves(src_curves, unselected, attributes, dst_curves);
395
396 for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) {
397 attribute.finish();
398 }
399}
400
402 const fn::FieldContext &field_context,
403 const fn::Field<bool> &selection_field,
404 const fn::Field<int> &count_field,
405 const ResampleCurvesOutputAttributeIDs &output_ids)
406{
407 if (src_curves.curves_range().is_empty()) {
408 return {};
409 }
410 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
411
413 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
414
415 fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
416 evaluator.set_selection(selection_field);
417 evaluator.add_with_destination(count_field, dst_offsets.drop_back(1));
418 evaluator.evaluate();
419 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
420 IndexMaskMemory memory;
421 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
422
423 /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
424 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
426 return {};
427 }
428 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
429
430 resample_to_uniform(src_curves, selection, output_ids, dst_curves);
431
432 return dst_curves;
433}
434
436 const IndexMask &selection,
437 const VArray<int> &counts,
438 const ResampleCurvesOutputAttributeIDs &output_ids)
439{
440 if (src_curves.curves_range().is_empty()) {
441 return {};
442 }
443 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
444
446 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
447
448 array_utils::copy(counts, selection, dst_offsets);
449
450 IndexMaskMemory memory;
451 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
452
453 /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
454 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
455 /* We assume the counts are at least 1. */
456 BLI_assert(std::all_of(dst_offsets.begin(),
457 dst_offsets.drop_back(1).end(),
458 [&](const int count) { return count > 0; }));
460 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
461
462 resample_to_uniform(src_curves, selection, output_ids, dst_curves);
463
464 return dst_curves;
465}
466
468 const fn::FieldContext &field_context,
469 const fn::Field<bool> &selection_field,
470 const fn::Field<int> &count_field,
471 const ResampleCurvesOutputAttributeIDs &output_ids)
472{
473 return resample_to_uniform(src_curves,
474 field_context,
475 selection_field,
476 get_count_input_max_one(count_field),
477 output_ids);
478}
479
481 const IndexMask &selection,
482 const VArray<float> &sample_lengths,
483 const ResampleCurvesOutputAttributeIDs &output_ids)
484{
485 if (src_curves.curves_range().is_empty()) {
486 return {};
487 }
488 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
489 const VArray<bool> curves_cyclic = src_curves.cyclic();
490
492 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
493
494 src_curves.ensure_evaluated_lengths();
495 selection.foreach_index(GrainSize(1024), [&](const int curve_i) {
496 const float curve_length = src_curves.evaluated_length_total_for_curve(curve_i,
497 curves_cyclic[curve_i]);
498 dst_offsets[curve_i] = int(curve_length / sample_lengths[curve_i]) + 1;
499 });
500
501 IndexMaskMemory memory;
502 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
503
504 /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
505 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
507 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
508
509 resample_to_uniform(src_curves, selection, output_ids, dst_curves);
510
511 return dst_curves;
512}
513
515 const fn::FieldContext &field_context,
516 const fn::Field<bool> &selection_field,
517 const fn::Field<float> &segment_length_field,
518 const ResampleCurvesOutputAttributeIDs &output_ids)
519{
520 return resample_to_uniform(src_curves,
521 field_context,
522 selection_field,
523 get_count_input_from_length(segment_length_field),
524 output_ids);
525}
526
528 const IndexMask &selection,
529 const ResampleCurvesOutputAttributeIDs &output_ids)
530{
531 if (src_curves.curves_range().is_empty()) {
532 return {};
533 }
534 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
535 const OffsetIndices src_evaluated_points_by_curve = src_curves.evaluated_points_by_curve();
536 const Span<float3> evaluated_positions = src_curves.evaluated_positions();
537
538 IndexMaskMemory memory;
539 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
540
542 dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
543 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
544 offset_indices::copy_group_sizes(src_evaluated_points_by_curve, selection, dst_offsets);
545 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
547 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
548
549 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
550
551 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
552
553 AttributesForResample attributes;
554 gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes, output_ids);
555
557 selection.foreach_segment(GrainSize(512), [&](const IndexMaskSegment selection_segment) {
558 /* Evaluate generic point attributes directly to the result attributes. */
559 for (const int i_attribute : attributes.dst.index_range()) {
560 for (const int i_curve : selection_segment) {
561 const IndexRange src_points = src_points_by_curve[i_curve];
562 const IndexRange dst_points = dst_points_by_curve[i_curve];
563 src_curves.interpolate_to_evaluated(i_curve,
564 attributes.src[i_attribute].slice(src_points),
565 attributes.dst[i_attribute].slice(dst_points));
566 }
567 }
568
569 auto copy_evaluated_data = [&](const Span<float3> src, MutableSpan<float3> dst) {
570 for (const int i_curve : selection_segment) {
571 const IndexRange src_points = src_evaluated_points_by_curve[i_curve];
572 const IndexRange dst_points = dst_points_by_curve[i_curve];
573 dst.slice(dst_points).copy_from(src.slice(src_points));
574 }
575 };
576
577 /* Copy the evaluated positions to the selected curves. */
578 copy_evaluated_data(evaluated_positions, dst_positions);
579
580 if (!attributes.dst_tangents.is_empty()) {
581 copy_evaluated_data(attributes.src_evaluated_tangents, attributes.dst_tangents);
582 normalize_curve_point_data(selection_segment, dst_points_by_curve, attributes.dst_tangents);
583 }
584 if (!attributes.dst_normals.is_empty()) {
585 copy_evaluated_data(attributes.src_evaluated_normals, attributes.dst_normals);
586 normalize_curve_point_data(selection_segment, dst_points_by_curve, attributes.dst_normals);
587 }
588
589 /* Fill the default value for non-interpolating attributes that still must be copied. */
590 for (GMutableSpan dst : attributes.dst_no_interpolation) {
591 for (const int i_curve : selection_segment) {
592 const IndexRange dst_points = dst_points_by_curve[i_curve];
593 dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size());
594 }
595 }
596 });
597
598 copy_or_defaults_for_unselected_curves(src_curves, unselected, attributes, dst_curves);
599
600 for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) {
601 attribute.finish();
602 }
603
604 return dst_curves;
605}
606
608 const fn::FieldContext &field_context,
609 const fn::Field<bool> &selection_field,
610 const ResampleCurvesOutputAttributeIDs &output_ids)
611{
612 if (src_curves.curves_range().is_empty()) {
613 return {};
614 }
615 fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
616 evaluator.set_selection(selection_field);
617 evaluator.evaluate();
619 src_curves, evaluator.get_evaluated_selection_as_mask(), output_ids);
620}
621
622} // namespace blender::geometry
Low-level operations for curves.
Low-level operations for curves.
#define BLI_assert(a)
Definition BLI_assert.h:50
#define UNLIKELY(x)
#define ELEM(...)
@ CURVE_TYPE_BEZIER
@ CURVE_TYPE_NURBS
@ CURVE_TYPE_POLY
@ CD_PROP_FLOAT3
@ CD_PROP_STRING
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
MutableSpan< T > typed() const
static constexpr size_t min_alignment
constexpr int64_t size() const
constexpr bool is_empty() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr MutableSpan< NewT > cast() const
Definition BLI_span.hh:736
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:619
constexpr T * end() const
Definition BLI_span.hh:549
constexpr T * begin() const
Definition BLI_span.hh:545
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:690
bool contains(const Key &key) const
Definition BLI_set.hh:291
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
void remove_contained(const Key &key)
void append(const T &value)
void resize(const int64_t new_size)
MutableSpan< T > as_mutable_span()
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
GAttributeReader lookup(const StringRef attribute_id) const
MutableSpan< float3 > positions_for_write()
OffsetIndices< int > points_by_curve() const
void ensure_can_interpolate_to_evaluated() const
IndexRange curves_range() const
const std::array< int, CURVE_TYPES_NUM > & curve_type_counts() const
MutableAttributeAccessor attributes_for_write()
Span< float > evaluated_lengths_for_curve(int curve_index, bool cyclic) const
Span< float3 > evaluated_tangents() const
void interpolate_to_evaluated(int curve_index, GSpan src, GMutableSpan dst) const
Span< float3 > evaluated_normals() const
Span< float3 > positions() const
OffsetIndices< int > evaluated_points_by_curve() const
void resize(int points_num, int curves_num)
void fill_curve_types(CurveType type)
float evaluated_length_total_for_curve(int curve_index, bool cyclic) const
MutableSpan< int > offsets_for_write()
Span< float3 > evaluated_positions() const
VArray< int8_t > curve_types() const
VArray< bool > cyclic() const
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:244
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
OffsetIndices slice(const IndexRange range) const
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
int count
#define T
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void copy_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves)
void fill_points(OffsetIndices< int > points_by_curve, const IndexMask &curve_selection, GPointer value, GMutableSpan dst)
bool attribute_name_is_anonymous(const StringRef name)
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
static fn::Field< int > get_count_input_max_one(const fn::Field< int > &count_field)
static AttributesForInterpolation retrieve_attribute_spans(const Span< StringRef > ids, const CurvesGeometry &src_from_curves, const CurvesGeometry &src_to_curves, const bke::AttrDomain domain, CurvesGeometry &dst_curves)
static void normalize_span(MutableSpan< float3 > data)
static void copy_or_defaults_for_unselected_curves(const CurvesGeometry &src_curves, const IndexMask &unselected_curves, const AttributesForResample &attributes, CurvesGeometry &dst_curves)
CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, const IndexMask &selection, const VArray< int > &counts, const ResampleCurvesOutputAttributeIDs &output_ids={})
static bool interpolate_attribute_to_poly_curve(const StringRef attribute_id)
CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, const IndexMask &selection, const ResampleCurvesOutputAttributeIDs &output_ids={})
static void resample_to_uniform(const CurvesGeometry &src_curves, const IndexMask &selection, const ResampleCurvesOutputAttributeIDs &output_ids, CurvesGeometry &dst_curves)
static bool interpolate_attribute_to_curves(const StringRef attribute_id, const std::array< int, CURVE_TYPES_NUM > &type_counts)
static fn::Field< int > get_count_input_from_length(const fn::Field< float > &length_field)
static AttributesForInterpolation gather_point_attributes_to_interpolate(const CurvesGeometry &from_curves, const CurvesGeometry &to_curves, CurvesGeometry &dst_curves)
static void normalize_curve_point_data(const IndexMaskSegment curve_selection, const OffsetIndices< int > points_by_curve, MutableSpan< float3 > data)
CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, const IndexMask &selection, const VArray< float > &sample_lengths, const ResampleCurvesOutputAttributeIDs &output_ids={})
void interpolate(const Span< T > src, const Span< int > indices, const Span< float > factors, MutableSpan< T > dst)
void sample_uniform(Span< float > accumulated_segment_lengths, bool include_last_point, MutableSpan< int > r_segment_indices, MutableSpan< float > r_factors)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
void copy_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask, MutableSpan< int > sizes)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
std::optional< OffsetIndices< int > > accumulate_counts_to_offsets_with_overflow_check(MutableSpan< int > counts_to_offsets, int start_offset=0)
VecBase< float, 3 > float3
__int64 int64_t
Definition stdint.h:89
Vector< bke::GSpanAttributeWriter > dst_attributes
MutableSpan< T > resize(const int64_t size)
std::array< std::byte, 1024 > inline_buffer
Vector< std::byte, 0, AllocatorType > heap_allocated