Blender V4.5
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#include "BLI_math_vector.hh"
9
11#include "BLI_task.hh"
12
13#include "FN_field.hh"
15
16#include "BKE_attribute_math.hh"
17#include "BKE_curves.hh"
18#include "BKE_curves_utils.hh"
19#include "BKE_deform.hh"
21
23
24namespace blender::geometry {
25
27{
28 static auto max_one_fn = mf::build::SI1_SO<int, int>(
29 "Clamp Above One",
30 [](int value) { return std::max(1, value); },
31 mf::build::exec_presets::AllSpanOrSingle());
32 return fn::Field<int>(fn::FieldOperation::Create(max_one_fn, {count_field}));
33}
34
35static int get_count_from_length(const float curve_length,
36 const float sample_length,
37 const bool keep_last_segment)
38{
39 /* Find the number of sampled segments by dividing the total length by
40 * the sample length. Then there is one more sampled point than segment. */
41 if (UNLIKELY(sample_length == 0.0f)) {
42 return 1;
43 }
44 const int count = int(curve_length / sample_length) + 1;
45 return std::max(keep_last_segment ? 2 : 1, count);
46}
47
49 const bool keep_last_segment)
50{
51 static auto get_count_fn = mf::build::SI3_SO<float, float, bool, int>(
52 "Length Input to Count",
54 mf::build::exec_presets::SomeSpanOrSingle<0, 1>());
55
56 auto get_count_op = fn::FieldOperation::Create(
57 get_count_fn,
58 {fn::Field<float>(std::make_shared<bke::CurveLengthFieldInput>()),
59 length_field,
60 fn::make_constant_field(keep_last_segment)});
61
62 return fn::Field<int>(std::move(get_count_op));
63}
64
69static bool interpolate_attribute_to_curves(const StringRef attribute_id,
70 const std::array<int, CURVE_TYPES_NUM> &type_counts)
71{
72 if (bke::attribute_name_is_anonymous(attribute_id)) {
73 return true;
74 }
75 if (ELEM(attribute_id, "handle_type_left", "handle_type_right", "handle_left", "handle_right")) {
76 return type_counts[CURVE_TYPE_BEZIER] != 0;
77 }
78 if (ELEM(attribute_id, "nurbs_weight")) {
79 return type_counts[CURVE_TYPE_NURBS] != 0;
80 }
81 return true;
82}
83
87static bool interpolate_attribute_to_poly_curve(const StringRef attribute_id)
88{
89 static const Set<StringRef> no_interpolation{{
90 "handle_type_left",
91 "handle_type_right",
92 "handle_right",
93 "handle_left",
94 "nurbs_weight",
95 }};
96 return !no_interpolation.contains(attribute_id);
97}
98
103 const CurvesGeometry &src_curves,
104 CurvesGeometry &dst_curves,
107 Vector<bke::GSpanAttributeWriter> &dst_attributes)
108{
109 const bke::AttributeAccessor src_attributes = src_curves.attributes();
110 for (const int i : ids.index_range()) {
111 const bke::GAttributeReader src_attribute = src_attributes.lookup(ids[i],
113 src.append(src_attribute.varray);
114
116 src_attribute.varray.type());
117 bke::GSpanAttributeWriter dst_attribute =
119 ids[i], bke::AttrDomain::Point, data_type);
120 dst.append(dst_attribute.span);
121 dst_attributes.append(std::move(dst_attribute));
122 }
123}
124
139
144 const CurvesGeometry &src_curves,
145 CurvesGeometry &dst_curves,
147 const ResampleCurvesOutputAttributeIDs &output_ids)
148{
150 VectorSet<StringRef> ids_no_interpolation;
151 src_curves.attributes().foreach_attribute([&](const bke::AttributeIter &iter) {
152 if (iter.domain != bke::AttrDomain::Point) {
153 return;
154 }
155 if (iter.data_type == CD_PROP_STRING) {
156 return;
157 }
158 if (!interpolate_attribute_to_curves(iter.name, dst_curves.curve_type_counts())) {
159 return;
160 }
162 ids.add_new(iter.name);
163 }
164 else {
165 ids_no_interpolation.add_new(iter.name);
166 }
167 });
168
169 /* Position is handled differently since it has non-generic interpolation for Bezier
170 * curves and because the evaluated positions are cached for each evaluated point. */
171 ids.remove_contained("position");
172
174 ids, src_curves, dst_curves, result.src, result.dst, result.dst_attributes);
175
176 /* Attributes that aren't interpolated like Bezier handles still have to be copied
177 * to the result when there are any unselected curves of the corresponding type. */
178 retrieve_attribute_spans(ids_no_interpolation,
179 src_curves,
180 dst_curves,
181 result.src_no_interpolation,
182 result.dst_no_interpolation,
183 result.dst_attributes);
184
185 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
186 if (output_ids.tangent_id) {
187 result.src_evaluated_tangents = src_curves.evaluated_tangents();
190 result.dst_tangents = dst_attribute.span.typed<float3>();
191 result.dst_attributes.append(std::move(dst_attribute));
192 }
193 if (output_ids.normal_id) {
194 result.src_evaluated_normals = src_curves.evaluated_normals();
197 result.dst_normals = dst_attribute.span.typed<float3>();
198 result.dst_attributes.append(std::move(dst_attribute));
199 }
200}
201
203 const IndexMask &unselected_curves,
204 const AttributesForResample &attributes,
205 CurvesGeometry &dst_curves)
206{
207 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
208 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
209 array_utils::copy_group_to_group(src_points_by_curve,
210 dst_points_by_curve,
211 unselected_curves,
212 src_curves.positions(),
213 dst_curves.positions_for_write());
214
215 for (const int i : attributes.src.index_range()) {
216 array_utils::copy_group_to_group(src_points_by_curve,
217 dst_points_by_curve,
218 unselected_curves,
219 attributes.src[i],
220 attributes.dst[i]);
221 }
222 for (const int i : attributes.src_no_interpolation.index_range()) {
223 array_utils::copy_group_to_group(src_points_by_curve,
224 dst_points_by_curve,
225 unselected_curves,
226 attributes.src_no_interpolation[i],
227 attributes.dst_no_interpolation[i]);
228 }
229
230 if (!attributes.dst_tangents.is_empty()) {
232 dst_points_by_curve, unselected_curves, float3(0), attributes.dst_tangents);
233 }
234 if (!attributes.dst_normals.is_empty()) {
236 dst_points_by_curve, unselected_curves, float3(0), attributes.dst_normals);
237 }
238}
239
241{
242 for (const int i : data.index_range()) {
244 }
245}
246
247static void normalize_curve_point_data(const IndexMaskSegment curve_selection,
248 const OffsetIndices<int> points_by_curve,
250{
251 for (const int i_curve : curve_selection) {
252 normalize_span(data.slice(points_by_curve[i_curve]));
253 }
254}
255
262 /* Use a default alignment that works for all attribute types, and don't use the inline buffer
263 * because it doesn't necessarily have the correct alignment. */
265 alignas(AllocatorType::min_alignment) std::array<std::byte, 1024> inline_buffer;
266
267 template<typename T> MutableSpan<T> resize(const int64_t size)
268 {
269 const int64_t size_in_bytes = sizeof(T) * size;
270 if (size_in_bytes <= this->inline_buffer.size()) {
271 return MutableSpan<std::byte>(this->inline_buffer).slice(0, size_in_bytes).cast<T>();
272 }
273 this->heap_allocated.resize(size_in_bytes);
274 return this->heap_allocated.as_mutable_span().cast<T>();
275 }
276};
277
278static void resample_to_uniform(const CurvesGeometry &src_curves,
279 const IndexMask &selection,
280 const ResampleCurvesOutputAttributeIDs &output_ids,
281 CurvesGeometry &dst_curves)
282{
283 if (src_curves.curves_range().is_empty()) {
284 return;
285 }
286
287 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
288 const OffsetIndices evaluated_points_by_curve = src_curves.evaluated_points_by_curve();
289 const VArray<bool> curves_cyclic = src_curves.cyclic();
290 const VArray<int8_t> curve_types = src_curves.curve_types();
291 const Span<float3> evaluated_positions = src_curves.evaluated_positions();
292
293 /* All resampled curves are poly curves. */
294 dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
295
296 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
297
298 AttributesForResample attributes;
299 gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes, output_ids);
300
301 src_curves.ensure_evaluated_lengths();
302
303 /* Sampling arbitrary attributes works by first interpolating them to the curve's standard
304 * "evaluated points" and then interpolating that result with the uniform samples. This is
305 * potentially wasteful when down-sampling a curve to many fewer points. There are two possible
306 * solutions: only sample the necessary points for interpolation, or first sample curve
307 * parameter/segment indices and evaluate the curve directly. */
308 Array<int> sample_indices(dst_curves.points_num());
309 Array<float> sample_factors(dst_curves.points_num());
310
311 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
312
313 /* Use a "for each group of curves: for each attribute: for each curve" pattern to work on
314 * smaller sections of data that ideally fit into CPU cache better than simply one attribute at a
315 * time or one curve at a time. */
316 selection.foreach_segment(GrainSize(512), [&](const IndexMaskSegment selection_segment) {
317 EvalDataBuffer evaluated_buffer;
318
319 /* Gather uniform samples based on the accumulated lengths of the original curve. */
320 for (const int i_curve : selection_segment) {
321 const bool cyclic = curves_cyclic[i_curve];
322 const IndexRange dst_points = dst_points_by_curve[i_curve];
323 const Span<float> lengths = src_curves.evaluated_lengths_for_curve(i_curve, cyclic);
324 if (lengths.is_empty()) {
325 /* Handle curves with only one evaluated point. */
326 sample_indices.as_mutable_span().slice(dst_points).fill(0);
327 sample_factors.as_mutable_span().slice(dst_points).fill(0.0f);
328 }
329 else {
331 !curves_cyclic[i_curve],
332 sample_indices.as_mutable_span().slice(dst_points),
333 sample_factors.as_mutable_span().slice(dst_points));
334 }
335 }
336
337 /* For every attribute, evaluate attributes from every curve in the range in the original
338 * curve's "evaluated points", then use linear interpolation to sample to the result. */
339 for (const int i_attribute : attributes.dst.index_range()) {
340 const CPPType &type = attributes.src[i_attribute].type();
341 bke::attribute_math::convert_to_static_type(type, [&](auto dummy) {
342 using T = decltype(dummy);
343 Span<T> src = attributes.src[i_attribute].typed<T>();
344 MutableSpan<T> dst = attributes.dst[i_attribute].typed<T>();
345
346 for (const int i_curve : selection_segment) {
347 const IndexRange src_points = src_points_by_curve[i_curve];
348 const IndexRange dst_points = dst_points_by_curve[i_curve];
349
350 if (curve_types[i_curve] == CURVE_TYPE_POLY) {
352 sample_indices.as_span().slice(dst_points),
353 sample_factors.as_span().slice(dst_points),
354 dst.slice(dst_points));
355 }
356 else {
357 MutableSpan evaluated = evaluated_buffer.resize<T>(
358 evaluated_points_by_curve[i_curve].size());
359 src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated);
360
362 sample_indices.as_span().slice(dst_points),
363 sample_factors.as_span().slice(dst_points),
364 dst.slice(dst_points));
365 }
366 }
367 });
368 }
369
370 auto interpolate_evaluated_data = [&](const Span<float3> src, MutableSpan<float3> dst) {
371 for (const int i_curve : selection_segment) {
372 const IndexRange src_points = evaluated_points_by_curve[i_curve];
373 const IndexRange dst_points = dst_points_by_curve[i_curve];
375 sample_indices.as_span().slice(dst_points),
376 sample_factors.as_span().slice(dst_points),
377 dst.slice(dst_points));
378 }
379 };
380
381 /* Interpolate the evaluated positions to the resampled curves. */
382 interpolate_evaluated_data(evaluated_positions, dst_positions);
383
384 if (!attributes.dst_tangents.is_empty()) {
385 interpolate_evaluated_data(attributes.src_evaluated_tangents, attributes.dst_tangents);
386 normalize_curve_point_data(selection_segment, dst_points_by_curve, attributes.dst_tangents);
387 }
388 if (!attributes.dst_normals.is_empty()) {
389 interpolate_evaluated_data(attributes.src_evaluated_normals, attributes.dst_normals);
390 normalize_curve_point_data(selection_segment, dst_points_by_curve, attributes.dst_normals);
391 }
392
393 /* Fill the default value for non-interpolating attributes that still must be copied. */
394 for (GMutableSpan dst : attributes.dst_no_interpolation) {
395 for (const int i_curve : selection_segment) {
396 const IndexRange dst_points = dst_points_by_curve[i_curve];
397 dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size());
398 }
399 }
400 });
401
402 IndexMaskMemory memory;
403 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
404 copy_or_defaults_for_unselected_curves(src_curves, unselected, attributes, dst_curves);
405
406 for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) {
407 attribute.finish();
408 }
409}
410
412 const fn::FieldContext &field_context,
413 const fn::Field<bool> &selection_field,
414 const fn::Field<int> &count_field,
415 const ResampleCurvesOutputAttributeIDs &output_ids)
416{
417 if (src_curves.curves_range().is_empty()) {
418 return {};
419 }
420 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
421
423 /* Copy vertex groups from source curves to allow copying vertex group attributes. */
425 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
426
427 fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
428 evaluator.set_selection(selection_field);
429 evaluator.add_with_destination(count_field, dst_offsets.drop_back(1));
430 evaluator.evaluate();
431 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
432 IndexMaskMemory memory;
433 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
434
435 /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
436 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
438 return {};
439 }
440 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
441
442 resample_to_uniform(src_curves, selection, output_ids, dst_curves);
443
444 bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves);
445 return dst_curves;
446}
447
449 const IndexMask &selection,
450 const VArray<int> &counts,
451 const ResampleCurvesOutputAttributeIDs &output_ids)
452{
453 if (src_curves.curves_range().is_empty()) {
454 return {};
455 }
456 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
457
459 /* Copy vertex groups from source curves to allow copying vertex group attributes. */
461 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
462
463 array_utils::copy(counts, selection, dst_offsets);
464
465 IndexMaskMemory memory;
466 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
467
468 /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
469 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
470 /* We assume the counts are at least 1. */
471 BLI_assert(std::all_of(dst_offsets.begin(),
472 dst_offsets.drop_back(1).end(),
473 [&](const int count) { return count > 0; }));
475 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
476
477 resample_to_uniform(src_curves, selection, output_ids, dst_curves);
478
479 bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves);
480 return dst_curves;
481}
482
484 const fn::FieldContext &field_context,
485 const fn::Field<bool> &selection_field,
486 const fn::Field<int> &count_field,
487 const ResampleCurvesOutputAttributeIDs &output_ids)
488{
489 return resample_to_uniform(src_curves,
490 field_context,
491 selection_field,
492 get_count_input_max_one(count_field),
493 output_ids);
494}
495
497 const IndexMask &selection,
498 const VArray<float> &sample_lengths,
499 const ResampleCurvesOutputAttributeIDs &output_ids,
500 const bool keep_last_segment)
501{
502 if (src_curves.curves_range().is_empty()) {
503 return {};
504 }
505 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
506 const VArray<bool> curves_cyclic = src_curves.cyclic();
507
509 /* Copy vertex groups from source curves to allow copying vertex group attributes. */
511 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
512
513 src_curves.ensure_evaluated_lengths();
514 selection.foreach_index(GrainSize(1024), [&](const int curve_i) {
515 const float curve_length = src_curves.evaluated_length_total_for_curve(curve_i,
516 curves_cyclic[curve_i]);
517 dst_offsets[curve_i] = get_count_from_length(
518 curve_length, sample_lengths[curve_i], keep_last_segment);
519 });
520
521 IndexMaskMemory memory;
522 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
523
524 /* Fill the counts for the curves that aren't selected and accumulate the counts into offsets. */
525 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
527 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
528
529 resample_to_uniform(src_curves, selection, output_ids, dst_curves);
530
531 bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves);
532 return dst_curves;
533}
534
536 const fn::FieldContext &field_context,
537 const fn::Field<bool> &selection_field,
538 const fn::Field<float> &segment_length_field,
539 const ResampleCurvesOutputAttributeIDs &output_ids,
540 const bool keep_last_segment)
541{
542 return resample_to_uniform(src_curves,
543 field_context,
544 selection_field,
545 get_count_input_from_length(segment_length_field, keep_last_segment),
546 output_ids);
547}
548
550 const IndexMask &selection,
551 const ResampleCurvesOutputAttributeIDs &output_ids)
552{
553 if (src_curves.curves_range().is_empty()) {
554 return {};
555 }
556 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
557 const OffsetIndices src_evaluated_points_by_curve = src_curves.evaluated_points_by_curve();
558 const Span<float3> evaluated_positions = src_curves.evaluated_positions();
559
560 IndexMaskMemory memory;
561 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
562
564 /* Copy vertex groups from source curves to allow copying vertex group attributes. */
566 dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
567 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
568 offset_indices::copy_group_sizes(src_evaluated_points_by_curve, selection, dst_offsets);
569 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
571 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
572
573 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
574
575 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
576
577 AttributesForResample attributes;
578 gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes, output_ids);
579
581 selection.foreach_segment(GrainSize(512), [&](const IndexMaskSegment selection_segment) {
582 /* Evaluate generic point attributes directly to the result attributes. */
583 for (const int i_attribute : attributes.dst.index_range()) {
584 for (const int i_curve : selection_segment) {
585 const IndexRange src_points = src_points_by_curve[i_curve];
586 const IndexRange dst_points = dst_points_by_curve[i_curve];
587 src_curves.interpolate_to_evaluated(i_curve,
588 attributes.src[i_attribute].slice(src_points),
589 attributes.dst[i_attribute].slice(dst_points));
590 }
591 }
592
593 auto copy_evaluated_data = [&](const Span<float3> src, MutableSpan<float3> dst) {
594 for (const int i_curve : selection_segment) {
595 const IndexRange src_points = src_evaluated_points_by_curve[i_curve];
596 const IndexRange dst_points = dst_points_by_curve[i_curve];
597 dst.slice(dst_points).copy_from(src.slice(src_points));
598 }
599 };
600
601 /* Copy the evaluated positions to the selected curves. */
602 copy_evaluated_data(evaluated_positions, dst_positions);
603
604 if (!attributes.dst_tangents.is_empty()) {
605 copy_evaluated_data(attributes.src_evaluated_tangents, attributes.dst_tangents);
606 normalize_curve_point_data(selection_segment, dst_points_by_curve, attributes.dst_tangents);
607 }
608 if (!attributes.dst_normals.is_empty()) {
609 copy_evaluated_data(attributes.src_evaluated_normals, attributes.dst_normals);
610 normalize_curve_point_data(selection_segment, dst_points_by_curve, attributes.dst_normals);
611 }
612
613 /* Fill the default value for non-interpolating attributes that still must be copied. */
614 for (GMutableSpan dst : attributes.dst_no_interpolation) {
615 for (const int i_curve : selection_segment) {
616 const IndexRange dst_points = dst_points_by_curve[i_curve];
617 dst.type().value_initialize_n(dst.slice(dst_points).data(), dst_points.size());
618 }
619 }
620 });
621
622 copy_or_defaults_for_unselected_curves(src_curves, unselected, attributes, dst_curves);
623
624 for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) {
625 attribute.finish();
626 }
627
628 bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves);
629 return dst_curves;
630}
631
633 const fn::FieldContext &field_context,
634 const fn::Field<bool> &selection_field,
635 const ResampleCurvesOutputAttributeIDs &output_ids)
636{
637 if (src_curves.curves_range().is_empty()) {
638 return {};
639 }
640 fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
641 evaluator.set_selection(selection_field);
642 evaluator.evaluate();
644 src_curves, evaluator.get_evaluated_selection_as_mask(), output_ids);
645}
646
647} // namespace blender::geometry
Low-level operations for curves.
Low-level operations for curves.
support for deformation groups and hooks.
void BKE_defgroup_copy_list(ListBase *outbase, const ListBase *inbase)
Definition deform.cc:71
#define BLI_assert(a)
Definition BLI_assert.h:46
#define UNLIKELY(x)
#define ELEM(...)
@ CURVE_TYPE_BEZIER
@ CURVE_TYPE_NURBS
@ CURVE_TYPE_POLY
@ CD_PROP_FLOAT3
@ CD_PROP_STRING
BMesh const char void * data
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
void value_initialize_n(void *ptr, int64_t n) const
GMutableSpan slice(const int64_t start, int64_t size) const
const CPPType & type() const
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:573
constexpr MutableSpan< NewT > cast() const
Definition BLI_span.hh:749
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:618
constexpr Span< T > as_span() const
Definition BLI_span.hh:661
constexpr T * end() const
Definition BLI_span.hh:548
constexpr T * begin() const
Definition BLI_span.hh:544
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
NonCopyable(const NonCopyable &other)=delete
NonMovable(NonMovable &&other)=delete
bool contains(const Key &key) const
Definition BLI_set.hh:310
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
void add_new(const Key &key)
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)
AttributeAccessor attributes() const
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)
void set_selection(Field< bool > selection)
Definition FN_field.hh:383
IndexMask get_evaluated_selection_as_mask() const
Definition field.cc:817
int add_with_destination(GField field, GVMutableArray dst)
Definition field.cc:738
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:242
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
void foreach_segment(Fn &&fn) const
OffsetIndices slice(const IndexRange range) const
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)
void copy_custom_knots(const bke::CurvesGeometry &src_curves, const IndexMask &exclude_curves, bke::CurvesGeometry &dst_curves)
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)
GField make_constant_field(const CPPType &type, const void *value)
Definition field.cc:528
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 int get_count_from_length(const float curve_length, const float sample_length, const bool keep_last_segment)
CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, const IndexMask &selection, const VArray< float > &sample_lengths, const ResampleCurvesOutputAttributeIDs &output_ids={}, bool keep_last_segment=false)
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 AttributesForInterpolation gather_point_attributes_to_interpolate(const CurvesGeometry &from_curves, const CurvesGeometry &to_curves, CurvesGeometry &dst_curves)
static fn::Field< int > get_count_input_from_length(const fn::Field< float > &length_field, const bool keep_last_segment)
static void normalize_curve_point_data(const IndexMaskSegment curve_selection, const OffsetIndices< int > points_by_curve, MutableSpan< float3 > data)
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
ListBase vertex_group_names
Vector< bke::GSpanAttributeWriter > dst_attributes
GuardedAlignedAllocator<> AllocatorType
MutableSpan< T > resize(const int64_t size)
std::array< std::byte, 1024 > inline_buffer
Vector< std::byte, 0, AllocatorType > heap_allocated
i
Definition text_draw.cc:230