Blender V5.0
trim_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
8
10
11#include "BKE_attribute.hh"
12#include "BKE_attribute_math.hh"
13#include "BKE_curves.hh"
14#include "BKE_curves_utils.hh"
15
16#include "GEO_trim_curves.hh"
17
18namespace blender::geometry {
19
20/* -------------------------------------------------------------------- */
23
37 const float sample_length,
38 const bool cyclic,
39 const int resolution,
40 const int num_curve_points)
41{
42 BLI_assert(!cyclic || lengths.size() / resolution >= 2);
43 const int last_index = num_curve_points - 1;
44 if (sample_length <= 0.0f) {
45 return {{0, 1}, 0.0f};
46 }
47 if (sample_length >= lengths.last()) {
48 return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} :
49 bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0};
50 }
51 int eval_index;
52 float eval_factor;
53 length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor);
54
55 const int index = eval_index / resolution;
56 const int next_index = (index == last_index) ? 0 : index + 1;
57 const float parameter = (eval_factor + eval_index) / resolution - index;
58
59 return bke::curves::CurvePoint{{index, next_index}, parameter};
60}
61
66 const float sample_length,
67 const bool cyclic,
68 const int evaluated_size)
69{
70 const int last_index = evaluated_size - 1;
71 if (sample_length <= 0.0f) {
72 return {{0, 1}, 0.0f};
73 }
74 if (sample_length >= lengths.last()) {
75 return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} :
76 bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0};
77 }
78
79 int eval_index;
80 float eval_factor;
81 length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor);
82
83 const int next_eval_index = (eval_index == last_index) ? 0 : eval_index + 1;
84 return bke::curves::CurvePoint{{eval_index, next_eval_index}, eval_factor};
85}
86
91 const Span<float> lengths,
92 const float sample_length,
93 const bool cyclic,
94 const int num_curve_points)
95{
96 const int last_index = num_curve_points - 1;
97 if (sample_length <= 0.0f) {
98 return {{0, 1}, 0.0f};
99 }
100 if (sample_length >= lengths.last()) {
101 return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} :
102 bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0};
103 }
104 int eval_index;
105 float eval_factor;
106 length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor);
107
108 /* Find the segment index from the offset mapping. */
109 const int *offset = std::upper_bound(bezier_offsets.begin(), bezier_offsets.end(), eval_index);
110 const int left = offset - bezier_offsets.begin() - 1;
111 const int right = left == last_index ? 0 : left + 1;
112
113 const int prev_offset = bezier_offsets[left];
114 const float offset_in_segment = eval_factor + (eval_index - prev_offset);
115 const int segment_resolution = bezier_offsets[left + 1] - prev_offset;
116 const float parameter = std::clamp(offset_in_segment / segment_resolution, 0.0f, 1.0f);
117
118 return {{left, right}, parameter};
119}
120
122 const bke::CurvesGeometry &src_curves,
123 const OffsetIndices<int> evaluated_points_by_curve,
124 const int64_t curve_index,
125 const Span<float> accumulated_lengths,
126 const float sample_length,
127 const bool cyclic,
128 const int resolution,
129 const int num_curve_points)
130{
132 num_curve_points, evaluated_points_by_curve[curve_index].size(), cyclic, resolution))
133 {
134 const Span<int> bezier_offsets = src_curves.bezier_evaluated_offsets_for_curve(curve_index);
135 return lookup_point_bezier(
136 bezier_offsets, accumulated_lengths, sample_length, cyclic, num_curve_points);
137 }
139 accumulated_lengths, sample_length, cyclic, resolution, num_curve_points);
140}
141
143 const bke::CurvesGeometry &src_curves,
144 const OffsetIndices<int> evaluated_points_by_curve,
145 const CurveType curve_type,
146 const int64_t curve_index,
147 const Span<float> accumulated_lengths,
148 const float sample_length,
149 const bool cyclic,
150 const int resolution,
151 const int num_curve_points)
152{
153 if (num_curve_points == 1) {
154 return {{0, 0}, 0.0f};
155 }
156
157 if (curve_type == CURVE_TYPE_CATMULL_ROM) {
159 accumulated_lengths, sample_length, cyclic, resolution, num_curve_points);
160 }
161 if (curve_type == CURVE_TYPE_BEZIER) {
162 return lookup_point_bezier(src_curves,
163 evaluated_points_by_curve,
164 curve_index,
165 accumulated_lengths,
166 sample_length,
167 cyclic,
168 resolution,
169 num_curve_points);
170 }
171 if (curve_type == CURVE_TYPE_POLY) {
172 return lookup_point_polygonal(accumulated_lengths, sample_length, cyclic, num_curve_points);
173 }
174 /* Handle evaluated curve. */
175 BLI_assert(resolution > 0);
177 accumulated_lengths, sample_length, cyclic, evaluated_points_by_curve[curve_index].size());
178}
179
181
182/* -------------------------------------------------------------------- */
185
186static void fill_bezier_data(bke::CurvesGeometry &dst_curves, const IndexMask &selection)
187{
188 if (!dst_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
189 return;
190 }
191 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
192 MutableSpan<float3> handle_positions_left = dst_curves.handle_positions_left_for_write();
193 MutableSpan<float3> handle_positions_right = dst_curves.handle_positions_right_for_write();
194 MutableSpan<int8_t> handle_types_left = dst_curves.handle_types_left_for_write();
195 MutableSpan<int8_t> handle_types_right = dst_curves.handle_types_right_for_write();
196
197 selection.foreach_index(GrainSize(4096), [&](const int curve_i) {
198 const IndexRange points = dst_points_by_curve[curve_i];
199 handle_types_right.slice(points).fill(int8_t(BEZIER_HANDLE_FREE));
200 handle_types_left.slice(points).fill(int8_t(BEZIER_HANDLE_FREE));
201 handle_positions_left.slice(points).fill({0.0f, 0.0f, 0.0f});
202 handle_positions_right.slice(points).fill({0.0f, 0.0f, 0.0f});
203 });
204}
205static void fill_nurbs_data(bke::CurvesGeometry &dst_curves, const IndexMask &selection)
206{
207 if (!dst_curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
208 return;
209 }
211 dst_curves.points_by_curve(), selection, 0.0f, dst_curves.nurbs_weights_for_write());
212}
213
214template<typename T>
216 MutableSpan<T> dst_data,
217 const bke::curves::IndexRangeCyclic src_range,
218 int64_t dst_index)
219{
220 int64_t increment;
221 if (src_range.cycles()) {
222 increment = src_range.size_before_loop();
223 dst_data.slice(dst_index, increment).copy_from(src_data.slice(src_range.first(), increment));
224 dst_index += increment;
225
226 increment = src_range.size_after_loop();
227 dst_data.slice(dst_index, increment)
228 .copy_from(src_data.slice(src_range.curve_range().first(), increment));
229 dst_index += increment;
230 }
231 else {
232 increment = src_range.one_after_last() - int64_t(src_range.first());
233 dst_data.slice(dst_index, increment).copy_from(src_data.slice(src_range.first(), increment));
234 dst_index += increment;
235 }
236 return dst_index;
237}
238
240
241/* -------------------------------------------------------------------- */
244
245template<typename T>
246static T interpolate_catmull_rom(const Span<T> src_data,
247 const bke::curves::CurvePoint insertion_point,
248 const bool src_cyclic)
249{
250 BLI_assert(insertion_point.index >= 0 && insertion_point.next_index < src_data.size());
251 int i0;
252 if (insertion_point.index == 0) {
253 i0 = src_cyclic ? src_data.size() - 1 : insertion_point.index;
254 }
255 else {
256 i0 = insertion_point.index - 1;
257 }
258 int i3 = insertion_point.next_index + 1;
259 if (i3 == src_data.size()) {
260 i3 = src_cyclic ? 0 : insertion_point.next_index;
261 }
263 src_data[insertion_point.index],
264 src_data[insertion_point.next_index],
265 src_data[i3],
266 insertion_point.parameter);
267}
268
270 const Span<float3> positions,
271 const Span<float3> handles_left,
272 const Span<float3> handles_right,
273 const bke::curves::CurvePoint insertion_point)
274{
276 insertion_point.index + 1 == insertion_point.next_index ||
277 (insertion_point.next_index >= 0 && insertion_point.next_index < insertion_point.index));
278 return bke::curves::bezier::insert(positions[insertion_point.index],
279 handles_right[insertion_point.index],
280 handles_left[insertion_point.next_index],
281 positions[insertion_point.next_index],
282 insertion_point.parameter);
283}
284
286
287/* -------------------------------------------------------------------- */
290
304template<typename T, bool include_start_point = true>
305static void sample_interval_linear(const Span<T> src_data,
306 MutableSpan<T> dst_data,
308 const IndexRange dst_range,
309 const bke::curves::CurvePoint start_point,
310 const bke::curves::CurvePoint end_point)
311{
312 int64_t dst_index = dst_range.first();
313
314 if (start_point.is_controlpoint()) {
315 /* 'start_point' is included in the copy iteration. */
316 if constexpr (!include_start_point) {
317 /* Skip first. */
318 src_range = src_range.drop_front();
319 }
320 }
321 else if constexpr (!include_start_point) {
322 /* Do nothing (excluded). */
323 }
324 else {
325 /* General case, sample 'start_point' */
326 dst_data[dst_index] = bke::attribute_math::mix2(
327 start_point.parameter, src_data[start_point.index], src_data[start_point.next_index]);
328 ++dst_index;
329 }
330
331 dst_index = copy_point_data_between_endpoints(src_data, dst_data, src_range, dst_index);
332 if (dst_range.size() == 1) {
333 BLI_assert(dst_index == dst_range.one_after_last());
334 return;
335 }
336
337 /* Handle last case */
338 if (end_point.is_controlpoint()) {
339 /* 'end_point' is included in the copy iteration. */
340 }
341 else {
342 dst_data[dst_index] = bke::attribute_math::mix2(
343 end_point.parameter, src_data[end_point.index], src_data[end_point.next_index]);
344#ifndef NDEBUG
345 ++dst_index;
346#endif
347 }
348 BLI_assert(dst_index == dst_range.one_after_last());
349}
350
351template<typename T>
352static void sample_interval_catmull_rom(const Span<T> src_data,
353 MutableSpan<T> dst_data,
355 const IndexRange dst_range,
356 const bke::curves::CurvePoint start_point,
357 const bke::curves::CurvePoint end_point,
358 const bool src_cyclic)
359{
360 int64_t dst_index = dst_range.first();
361
362 if (start_point.is_controlpoint()) {
363 }
364 else {
365 /* General case, sample 'start_point' */
366 dst_data[dst_index] = interpolate_catmull_rom(src_data, start_point, src_cyclic);
367 ++dst_index;
368 }
369
370 dst_index = copy_point_data_between_endpoints(src_data, dst_data, src_range, dst_index);
371 if (dst_range.size() == 1) {
372 BLI_assert(dst_index == dst_range.one_after_last());
373 return;
374 }
375
376 /* Handle last case */
377 if (end_point.is_controlpoint()) {
378 /* 'end_point' is included in the copy iteration. */
379 }
380 else {
381 dst_data[dst_index] = interpolate_catmull_rom(src_data, end_point, src_cyclic);
382#ifndef NDEBUG
383 ++dst_index;
384#endif
385 }
386 BLI_assert(dst_index == dst_range.one_after_last());
387}
388
389template<bool include_start_point = true>
390static void sample_interval_bezier(const Span<float3> src_positions,
391 const Span<float3> src_handles_l,
392 const Span<float3> src_handles_r,
393 const Span<int8_t> src_types_l,
394 const Span<int8_t> src_types_r,
395 MutableSpan<float3> dst_positions,
396 MutableSpan<float3> dst_handles_l,
397 MutableSpan<float3> dst_handles_r,
398 MutableSpan<int8_t> dst_types_l,
399 MutableSpan<int8_t> dst_types_r,
401 const IndexRange dst_range,
402 const bke::curves::CurvePoint start_point,
403 const bke::curves::CurvePoint end_point)
404{
405 bke::curves::bezier::Insertion start_point_insert;
406 int64_t dst_index = dst_range.first();
407
408 bool start_point_trimmed = false;
409 if (start_point.is_controlpoint()) {
410 /* The 'start_point' control point is included in the copy iteration. */
411 if constexpr (!include_start_point) {
412 src_range = src_range.drop_front();
413 }
414 }
415 else if constexpr (!include_start_point) {
416 /* Do nothing, 'start_point' is excluded. */
417 }
418 else {
419 /* General case, sample 'start_point'. */
420 start_point_insert = knot_insert_bezier(
421 src_positions, src_handles_l, src_handles_r, start_point);
422 dst_positions[dst_range.first()] = start_point_insert.position;
423 dst_handles_l[dst_range.first()] = start_point_insert.left_handle;
424 dst_handles_r[dst_range.first()] = start_point_insert.right_handle;
425 dst_types_l[dst_range.first()] = src_types_l[start_point.index];
426 dst_types_r[dst_range.first()] = src_types_r[start_point.index];
427
428 start_point_trimmed = true;
429 ++dst_index;
430 }
431
432 /* Copy point data between the 'start_point' and 'end_point'. */
433 int64_t increment = src_range.cycles() ? src_range.size_before_loop() :
434 src_range.one_after_last() - src_range.first();
435
436 const IndexRange dst_range_to_end(dst_index, increment);
437 const IndexRange src_range_to_end(src_range.first(), increment);
438 dst_positions.slice(dst_range_to_end).copy_from(src_positions.slice(src_range_to_end));
439 dst_handles_l.slice(dst_range_to_end).copy_from(src_handles_l.slice(src_range_to_end));
440 dst_handles_r.slice(dst_range_to_end).copy_from(src_handles_r.slice(src_range_to_end));
441 dst_types_l.slice(dst_range_to_end).copy_from(src_types_l.slice(src_range_to_end));
442 dst_types_r.slice(dst_range_to_end).copy_from(src_types_r.slice(src_range_to_end));
443 dst_index += increment;
444
445 if (dst_range.size() == 1) {
446 BLI_assert(dst_index == dst_range.one_after_last());
447 return;
448 }
449
450 increment = src_range.size_after_loop();
451 if (src_range.cycles() && increment > 0) {
452 const IndexRange dst_range_looped(dst_index, increment);
453 const IndexRange src_range_looped(src_range.curve_range().first(), increment);
454 dst_positions.slice(dst_range_looped).copy_from(src_positions.slice(src_range_looped));
455 dst_handles_l.slice(dst_range_looped).copy_from(src_handles_l.slice(src_range_looped));
456 dst_handles_r.slice(dst_range_looped).copy_from(src_handles_r.slice(src_range_looped));
457 dst_types_l.slice(dst_range_looped).copy_from(src_types_l.slice(src_range_looped));
458 dst_types_r.slice(dst_range_looped).copy_from(src_types_r.slice(src_range_looped));
459 dst_index += increment;
460 }
461
462 if (start_point_trimmed) {
463 dst_handles_l[dst_range.first() + 1] = start_point_insert.handle_next;
464 /* No need to change handle type (remains the same). */
465 }
466
467 /* Handle 'end_point' */
468 bke::curves::bezier::Insertion end_point_insert;
469 if (end_point.parameter == 0.0f) {
470 if (end_point.index == start_point.index) {
471 /* Start point is same point or in the same segment. */
472 if (start_point.parameter == 0.0f) {
473 /* Same point. */
474 BLI_assert(dst_range.size() == 1LL + src_range.size_range());
475 dst_handles_l[dst_range.first()] = dst_positions[dst_range.first()];
476 dst_handles_r[dst_range.last()] = dst_positions[dst_range.first()];
477 }
478 else if (start_point.parameter == 1.0f) {
479 /* Start is next controlpoint, do nothing. */
480 }
481 else {
482 /* Within the segment. */
483 BLI_assert(dst_range.size() == 1LL + src_range.size_range() || dst_range.size() == 2);
484 dst_handles_r[dst_range.last()] = start_point_insert.handle_prev;
485 }
486 }
487 /* Start point is considered 'before' the endpoint and ignored. */
488 }
489 else if (end_point.parameter == 1.0f) {
490 if (end_point.next_index == start_point.index) {
491 /* Start point is same or in 'next' segment. */
492 if (start_point.parameter == 0.0f) {
493 /* Same point */
494 BLI_assert(dst_range.size() == 1LL + src_range.size_range());
495 dst_handles_l[dst_range.first()] = dst_positions[dst_range.first()];
496 dst_handles_r[dst_range.last()] = dst_positions[dst_range.first()];
497 }
498 else if (start_point.parameter == 1.0f) {
499 /* Start is next controlpoint, do nothing. */
500 }
501 else {
502 /* In next segment. */
503 BLI_assert(dst_range.size() == 1LL + src_range.size_range() || dst_range.size() == 2);
504 dst_handles_r[dst_range.last()] = start_point_insert.handle_prev;
505 }
506 }
507 }
508 else {
509 /* Trimmed in both ends within the same (and only) segment! Ensure both end points is not a
510 * loop. */
511 if (start_point.index == end_point.index && start_point.parameter < 1.0f) {
512 BLI_assert(dst_range.size() == 2 || dst_range.size() == 2ll + src_range.size_range() ||
513 dst_range.size() == 1LL + src_range.size_range());
514
515 if (start_point.parameter > end_point.parameter && start_point.parameter < 1.0f) {
516 /* Start point comes after the endpoint within the segment. */
517 BLI_assert(end_point.parameter >= 0.0f);
518
519 const float parameter = end_point.parameter / start_point.parameter;
520 end_point_insert = bke::curves::bezier::insert(dst_positions[dst_index - 1],
521 start_point_insert.handle_prev,
522 start_point_insert.left_handle,
523 start_point_insert.position,
524 parameter);
525
526 /* Update start-point handle. */
527 dst_handles_l[dst_range.first()] = end_point_insert.handle_next;
528 }
529 else {
530 /* Start point lies before the endpoint within the segment. */
531
532 const float parameter = (end_point.parameter - start_point.parameter) /
533 (1.0f - start_point.parameter);
534 /* Unused only when parameter == 0.0f! */
535 const float3 handle_next = start_point.parameter == 0.0f ?
536 src_handles_l[end_point.next_index] :
537 start_point_insert.handle_next;
538 end_point_insert = bke::curves::bezier::insert(dst_positions[dst_index - 1],
539 dst_handles_r[dst_index - 1],
540 handle_next,
541 src_positions[end_point.next_index],
542 parameter);
543 }
544 }
545 else {
546 /* General case, compute the insertion point. */
547 end_point_insert = knot_insert_bezier(
548 src_positions, src_handles_l, src_handles_r, end_point);
549
550 if ((start_point.parameter >= end_point.parameter && end_point.index == start_point.index) ||
551 (start_point.parameter == 0.0f && end_point.next_index == start_point.index))
552 {
553 /* Start point is next controlpoint. */
554 dst_handles_l[dst_range.first()] = end_point_insert.handle_next;
555 /* No need to change handle type (remains the same). */
556 }
557 }
558
559 dst_handles_r[dst_index - 1] = end_point_insert.handle_prev;
560 dst_types_r[dst_index - 1] = src_types_l[end_point.index];
561
562 dst_handles_l[dst_index] = end_point_insert.left_handle;
563 dst_handles_r[dst_index] = end_point_insert.right_handle;
564 dst_positions[dst_index] = end_point_insert.position;
565 dst_types_l[dst_index] = src_types_l[end_point.next_index];
566 dst_types_r[dst_index] = src_types_r[end_point.next_index];
567#ifndef NDEBUG
568 ++dst_index;
569#endif
570 }
571 BLI_assert(dst_index == dst_range.one_after_last());
572}
573
575
576/* -------------------------------------------------------------------- */
579
580static void trim_attribute_linear(const bke::CurvesGeometry &src_curves,
581 bke::CurvesGeometry &dst_curves,
582 const IndexMask &selection,
583 const Span<bke::curves::CurvePoint> start_points,
584 const Span<bke::curves::CurvePoint> end_points,
586 MutableSpan<bke::AttributeTransferData> transfer_attributes)
587{
588 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
589 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
590 for (bke::AttributeTransferData &attribute : transfer_attributes) {
591 bke::attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) {
592 using T = decltype(dummy);
593
594 selection.foreach_index(GrainSize(512), [&](const int curve_i) {
595 const IndexRange src_points = src_points_by_curve[curve_i];
596 sample_interval_linear<T>(attribute.src.template typed<T>().slice(src_points),
597 attribute.dst.span.typed<T>(),
598 src_ranges[curve_i],
599 dst_points_by_curve[curve_i],
600 start_points[curve_i],
601 end_points[curve_i]);
602 });
603 });
604 }
605}
606
607static void trim_polygonal_curves(const bke::CurvesGeometry &src_curves,
608 bke::CurvesGeometry &dst_curves,
609 const IndexMask &selection,
610 const Span<bke::curves::CurvePoint> start_points,
611 const Span<bke::curves::CurvePoint> end_points,
613 MutableSpan<bke::AttributeTransferData> transfer_attributes)
614{
615 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
616 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
617 const Span<float3> src_positions = src_curves.positions();
618 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
619
620 selection.foreach_index(GrainSize(512), [&](const int curve_i) {
621 const IndexRange src_points = src_points_by_curve[curve_i];
622 const IndexRange dst_points = dst_points_by_curve[curve_i];
623
624 sample_interval_linear<float3>(src_positions.slice(src_points),
625 dst_positions,
626 src_ranges[curve_i],
627 dst_points,
628 start_points[curve_i],
629 end_points[curve_i]);
630 });
631 fill_bezier_data(dst_curves, selection);
632 fill_nurbs_data(dst_curves, selection);
633 trim_attribute_linear(src_curves,
634 dst_curves,
635 selection,
636 start_points,
637 end_points,
638 src_ranges,
639 transfer_attributes);
640}
641
642static void trim_catmull_rom_curves(const bke::CurvesGeometry &src_curves,
643 bke::CurvesGeometry &dst_curves,
644 const IndexMask &selection,
645 const Span<bke::curves::CurvePoint> start_points,
646 const Span<bke::curves::CurvePoint> end_points,
648 MutableSpan<bke::AttributeTransferData> transfer_attributes)
649{
650 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
651 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
652 const Span<float3> src_positions = src_curves.positions();
653 const VArray<bool> src_cyclic = src_curves.cyclic();
654 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
655
656 selection.foreach_index(GrainSize(512), [&](const int curve_i) {
657 const IndexRange src_points = src_points_by_curve[curve_i];
658 const IndexRange dst_points = dst_points_by_curve[curve_i];
659
660 sample_interval_catmull_rom<float3>(src_positions.slice(src_points),
661 dst_positions,
662 src_ranges[curve_i],
663 dst_points,
664 start_points[curve_i],
665 end_points[curve_i],
666 src_cyclic[curve_i]);
667 });
668 fill_bezier_data(dst_curves, selection);
669 fill_nurbs_data(dst_curves, selection);
670
671 for (bke::AttributeTransferData &attribute : transfer_attributes) {
672 bke::attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) {
673 using T = decltype(dummy);
674
675 selection.foreach_index(GrainSize(512), [&](const int curve_i) {
676 const IndexRange src_points = src_points_by_curve[curve_i];
677 const IndexRange dst_points = dst_points_by_curve[curve_i];
678
679 sample_interval_catmull_rom<T>(attribute.src.template typed<T>().slice(src_points),
680 attribute.dst.span.typed<T>(),
681 src_ranges[curve_i],
682 dst_points,
683 start_points[curve_i],
684 end_points[curve_i],
685 src_cyclic[curve_i]);
686 });
687 });
688 }
689}
690
691static void trim_bezier_curves(const bke::CurvesGeometry &src_curves,
692 bke::CurvesGeometry &dst_curves,
693 const IndexMask &selection,
694 const Span<bke::curves::CurvePoint> start_points,
695 const Span<bke::curves::CurvePoint> end_points,
697 MutableSpan<bke::AttributeTransferData> transfer_attributes)
698{
699 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
700 const Span<float3> src_positions = src_curves.positions();
701 const VArraySpan<int8_t> src_types_l{src_curves.handle_types_left()};
702 const VArraySpan<int8_t> src_types_r{src_curves.handle_types_right()};
703 const Span<float3> src_handles_l = *src_curves.handle_positions_left();
704 const Span<float3> src_handles_r = *src_curves.handle_positions_right();
705
706 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
707 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
708 MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write();
709 MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
710 MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
711 MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write();
712
713 selection.foreach_index(GrainSize(512), [&](const int curve_i) {
714 const IndexRange src_points = src_points_by_curve[curve_i];
715 const IndexRange dst_points = dst_points_by_curve[curve_i];
716
717 sample_interval_bezier(src_positions.slice(src_points),
718 src_handles_l.slice(src_points),
719 src_handles_r.slice(src_points),
720 src_types_l.slice(src_points),
721 src_types_r.slice(src_points),
722 dst_positions,
723 dst_handles_l,
724 dst_handles_r,
725 dst_types_l,
726 dst_types_r,
727 src_ranges[curve_i],
728 dst_points,
729 start_points[curve_i],
730 end_points[curve_i]);
731 });
732 fill_nurbs_data(dst_curves, selection);
733 trim_attribute_linear(src_curves,
734 dst_curves,
735 selection,
736 start_points,
737 end_points,
738 src_ranges,
739 transfer_attributes);
740}
741
742static void trim_evaluated_curves(const bke::CurvesGeometry &src_curves,
743 bke::CurvesGeometry &dst_curves,
744 const IndexMask &selection,
745 const Span<bke::curves::CurvePoint> start_points,
746 const Span<bke::curves::CurvePoint> end_points,
748 MutableSpan<bke::AttributeTransferData> transfer_attributes)
749{
750 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
751 const OffsetIndices src_evaluated_points_by_curve = src_curves.evaluated_points_by_curve();
752 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
753 const Span<float3> src_eval_positions = src_curves.evaluated_positions();
754 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
755
756 selection.foreach_index(GrainSize(512), [&](const int curve_i) {
757 const IndexRange src_evaluated_points = src_evaluated_points_by_curve[curve_i];
758 const IndexRange dst_points = dst_points_by_curve[curve_i];
759 sample_interval_linear<float3>(src_eval_positions.slice(src_evaluated_points),
760 dst_positions,
761 src_ranges[curve_i],
762 dst_points,
763 start_points[curve_i],
764 end_points[curve_i]);
765 });
766 fill_bezier_data(dst_curves, selection);
767 fill_nurbs_data(dst_curves, selection);
768
769 for (bke::AttributeTransferData &attribute : transfer_attributes) {
770 bke::attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) {
771 using T = decltype(dummy);
772
773 selection.foreach_segment(GrainSize(512), [&](const IndexMaskSegment segment) {
774 Vector<std::byte> evaluated_buffer;
775 for (const int64_t curve_i : segment) {
776 const IndexRange src_points = src_points_by_curve[curve_i];
777
778 /* Interpolate onto the evaluated point domain and sample the evaluated domain. */
779 evaluated_buffer.reinitialize(sizeof(T) * src_evaluated_points_by_curve[curve_i].size());
780 MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>();
781 src_curves.interpolate_to_evaluated(curve_i, attribute.src.slice(src_points), evaluated);
782 sample_interval_linear<T>(evaluated,
783 attribute.dst.span.typed<T>(),
784 src_ranges[curve_i],
785 dst_points_by_curve[curve_i],
786 start_points[curve_i],
787 end_points[curve_i]);
788 }
789 });
790 });
791 }
792}
793
794/* -------------------------------------------------------------------- */
797
798static float trim_sample_length(const Span<float> accumulated_lengths,
799 const float sample_length,
801{
802 float length = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ?
803 sample_length * accumulated_lengths.last() :
804 sample_length;
805 return std::clamp(length, 0.0f, accumulated_lengths.last());
806}
807
812 const IndexMask &selection,
813 const VArray<float> &starts,
814 const VArray<float> &ends,
816 MutableSpan<int> dst_curve_size,
820{
821 const OffsetIndices points_by_curve = curves.points_by_curve();
822 const OffsetIndices evaluated_points_by_curve = curves.evaluated_points_by_curve();
823 const VArray<bool> src_cyclic = curves.cyclic();
824 const VArray<int> resolution = curves.resolution();
825 const VArray<int8_t> curve_types = curves.curve_types();
827
828 selection.foreach_index(GrainSize(128), [&](const int curve_i) {
829 CurveType curve_type = CurveType(curve_types[curve_i]);
830
831 int point_count;
832 if (curve_type == CURVE_TYPE_NURBS) {
833 /* The result curve is a poly curve. */
834 point_count = evaluated_points_by_curve[curve_i].size();
835 }
836 else {
837 point_count = points_by_curve[curve_i].size();
838 }
839 if (point_count == 1) {
840 /* Single point. */
841 dst_curve_size[curve_i] = 1;
842 src_ranges[curve_i] = bke::curves::IndexRangeCyclic(0, 0, 1, 1);
843 start_points[curve_i] = {{0, 0}, 0.0f};
844 end_points[curve_i] = {{0, 0}, 0.0f};
845 return;
846 }
847
848 const bool cyclic = src_cyclic[curve_i];
849 const Span<float> lengths = curves.evaluated_lengths_for_curve(curve_i, cyclic);
850 BLI_assert(lengths.size() > 0);
851
852 const float start_length = trim_sample_length(lengths, starts[curve_i], mode);
853 float end_length;
854
855 bool equal_sample_point;
856 if (cyclic) {
857 end_length = trim_sample_length(lengths, ends[curve_i], mode);
858 const float cyclic_start = start_length == lengths.last() ? 0.0f : start_length;
859 const float cyclic_end = end_length == lengths.last() ? 0.0f : end_length;
860 equal_sample_point = cyclic_start == cyclic_end;
861 }
862 else {
863 end_length = ends[curve_i] <= starts[curve_i] ?
864 start_length :
865 trim_sample_length(lengths, ends[curve_i], mode);
866 equal_sample_point = start_length == end_length;
867 }
868
869 start_points[curve_i] = lookup_curve_point(curves,
870 evaluated_points_by_curve,
871 curve_type,
872 curve_i,
873 lengths,
874 start_length,
875 cyclic,
876 resolution[curve_i],
877 point_count);
878 if (equal_sample_point) {
879 end_points[curve_i] = start_points[curve_i];
880 if (end_length <= start_length) {
881 /* Single point. */
882 dst_curve_size[curve_i] = 1;
883 if (start_points[curve_i].is_controlpoint()) {
884 /* Only iterate if control point. */
885 const int single_point_index = start_points[curve_i].parameter == 1.0f ?
886 start_points[curve_i].next_index :
887 start_points[curve_i].index;
889 single_point_index, 1, point_count);
890 }
891 /* else: leave empty range */
892 }
893 else {
894 /* Split. */
896 start_points[curve_i], end_points[curve_i], point_count)
897 .push_loop();
898 const int count = 1 + !start_points[curve_i].is_controlpoint() + point_count;
899 BLI_assert(count > 1);
900 dst_curve_size[curve_i] = count;
901 }
902 }
903 else {
904 /* General case. */
905 end_points[curve_i] = lookup_curve_point(curves,
906 evaluated_points_by_curve,
907 curve_type,
908 curve_i,
909 lengths,
910 end_length,
911 cyclic,
912 resolution[curve_i],
913 point_count);
914
916 start_points[curve_i], end_points[curve_i], point_count);
917 const int count = src_ranges[curve_i].size() + !start_points[curve_i].is_controlpoint() +
918 !end_points[curve_i].is_controlpoint();
919 BLI_assert(count > 1);
920 dst_curve_size[curve_i] = count;
921 }
922 BLI_assert(dst_curve_size[curve_i] > 0);
923 });
924}
925
927
929 const IndexMask &selection,
930 const VArray<float> &starts,
931 const VArray<float> &ends,
933 const bke::AttributeFilter &attribute_filter)
934{
935 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
936 IndexMaskMemory memory;
937 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
938
939 BLI_assert(selection.size() > 0);
940 BLI_assert(selection.last() <= src_curves.curves_num());
941 BLI_assert(starts.size() == src_curves.curves_num());
942 BLI_assert(starts.size() == ends.size());
943 src_curves.ensure_evaluated_lengths();
944
946 MutableSpan<int> dst_curve_offsets = dst_curves.offsets_for_write();
947 Array<bke::curves::CurvePoint, 16> start_points(src_curves.curves_num());
948 Array<bke::curves::CurvePoint, 16> end_points(src_curves.curves_num());
951 selection,
952 starts,
953 ends,
954 mode,
955 dst_curve_offsets,
956 start_points,
957 end_points,
958 src_ranges);
959 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_curve_offsets);
961 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
962 dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num());
963
964 /* Populate curve domain. */
965 const bke::AttributeAccessor src_attributes = src_curves.attributes();
966 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
967 Set<std::string> transfer_curve_skip = {"cyclic", "curve_type", "nurbs_order", "knots_mode"};
968 if (dst_curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
969 /* If a NURBS curve is copied keep */
970 transfer_curve_skip.remove("nurbs_order");
971 transfer_curve_skip.remove("knots_mode");
972 }
973
974 /* Fetch custom point domain attributes for transfer (copy). */
976 src_attributes,
977 dst_attributes,
980 {"position",
981 "handle_left",
982 "handle_right",
983 "handle_type_left",
984 "handle_type_right",
985 "nurbs_weight"}));
986
987 auto trim_catmull = [&](const IndexMask &selection) {
988 trim_catmull_rom_curves(src_curves,
989 dst_curves,
990 selection,
991 start_points,
992 end_points,
993 src_ranges,
994 transfer_attributes);
995 };
996 auto trim_poly = [&](const IndexMask &selection) {
997 trim_polygonal_curves(src_curves,
998 dst_curves,
999 selection,
1000 start_points,
1001 end_points,
1002 src_ranges,
1003 transfer_attributes);
1004 };
1005 auto trim_bezier = [&](const IndexMask &selection) {
1006 trim_bezier_curves(src_curves,
1007 dst_curves,
1008 selection,
1009 start_points,
1010 end_points,
1011 src_ranges,
1012 transfer_attributes);
1013 };
1014 auto trim_evaluated = [&](const IndexMask &selection) {
1015 dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
1016 /* Ensure evaluated positions are available. */
1017 src_curves.evaluated_positions();
1018 trim_evaluated_curves(src_curves,
1019 dst_curves,
1020 selection,
1021 start_points,
1022 end_points,
1023 src_ranges,
1024 transfer_attributes);
1025 };
1026
1027 /* Populate point domain. */
1029 src_curves.curve_type_counts(),
1030 selection,
1031 trim_catmull,
1032 trim_poly,
1033 trim_bezier,
1034 trim_evaluated);
1035
1036 /* Cleanup/close context */
1037 for (bke::AttributeTransferData &attribute : transfer_attributes) {
1038 attribute.dst.finish();
1039 }
1040
1041 /* Copy unselected */
1042 if (unselected.is_empty()) {
1043 /* Since all curves were trimmed, none of them are cyclic and the attribute can be removed. */
1044 dst_curves.attributes_for_write().remove("cyclic");
1045 }
1046 else {
1047 /* Only trimmed curves are no longer cyclic. */
1048 if (bke::SpanAttributeWriter cyclic = dst_attributes.lookup_for_write_span<bool>("cyclic")) {
1049 index_mask::masked_fill(cyclic.span, false, selection);
1050 cyclic.finish();
1051 }
1052
1053 Set<std::string> copy_point_skip;
1054 if (!dst_curves.has_curve_with_type(CURVE_TYPE_NURBS) &&
1056 {
1057 copy_point_skip.add("nurbs_weight");
1058 }
1059
1061 src_attributes,
1064 bke::attribute_filter_with_skip_ref(attribute_filter, copy_point_skip),
1065 src_points_by_curve,
1066 dst_points_by_curve,
1067 unselected,
1068 dst_attributes);
1069 }
1070
1072 dst_curves.tag_topology_changed();
1073 if (src_curves.nurbs_has_custom_knots()) {
1076 }
1077 return dst_curves;
1078}
1079
1081
1082} // namespace blender::geometry
@ ATTR_DOMAIN_MASK_POINT
Low-level operations for curves.
Low-level operations for curves.
#define BLI_assert(a)
Definition BLI_assert.h:46
@ BEZIER_HANDLE_FREE
@ NURBS_KNOT_MODE_NORMAL
GeometryNodeCurveSampleMode
@ GEO_NODE_CURVE_SAMPLE_FACTOR
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
constexpr int64_t first() const
constexpr int64_t one_after_last() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
bool add(const Key &key)
Definition BLI_set.hh:248
bool remove(const Key &key)
Definition BLI_set.hh:385
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr const T * begin() const
Definition BLI_span.hh:220
VArray< int8_t > handle_types_left() const
MutableSpan< float3 > positions_for_write()
OffsetIndices< int > points_by_curve() const
MutableSpan< int8_t > handle_types_right_for_write()
VArray< int8_t > handle_types_right() const
void ensure_can_interpolate_to_evaluated() const
IndexRange curves_range() const
const std::array< int, CURVE_TYPES_NUM > & curve_type_counts() const
MutableSpan< float3 > handle_positions_left_for_write()
MutableAttributeAccessor attributes_for_write()
MutableSpan< float3 > handle_positions_right_for_write()
Span< float > evaluated_lengths_for_curve(int curve_index, bool cyclic) const
VArray< int > resolution() const
Span< int > bezier_evaluated_offsets_for_curve(int curve_index) const
std::optional< Span< float3 > > handle_positions_left() const
Span< float3 > positions() const
OffsetIndices< int > evaluated_points_by_curve() const
bool has_curve_with_type(CurveType type) const
std::optional< Span< float3 > > handle_positions_right() const
MutableSpan< float > nurbs_weights_for_write()
void resize(int points_num, int curves_num)
void fill_curve_types(CurveType type)
AttributeAccessor attributes() const
MutableSpan< int > offsets_for_write()
bool nurbs_has_custom_knots() const
Span< float3 > evaluated_positions() const
VArray< int8_t > curve_types() const
VArray< bool > cyclic() const
MutableSpan< int8_t > handle_types_left_for_write()
bool remove(const StringRef attribute_id)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
constexpr IndexRangeCyclic drop_front(const int n=1) const
constexpr IndexRange curve_range() const
static IndexRangeCyclic get_range_between_endpoints(const CurvePoint start_point, const CurvePoint end_point, const int iterable_range_size)
static IndexRangeCyclic get_range_from_size(const int start_index, const int iterator_size, const int iterable_range_size)
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
float length(VecOp< float, D >) RET
int count
static int left
#define T
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
T mix2(float factor, const T &a, const T &b)
Insertion insert(const float3 &point_prev, const float3 &handle_prev, const float3 &handle_next, const float3 &point_next, float parameter)
bool has_vector_handles(int num_curve_points, int64_t evaluated_size, bool cyclic, int resolution)
T interpolate(const T &a, const T &b, const T &c, const T &d, const float parameter)
void update_custom_knot_modes(const IndexMask &mask, const KnotsMode mode_for_regular, const KnotsMode mode_for_cyclic, bke::CurvesGeometry &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)
void foreach_curve_by_type(const VArray< int8_t > &types, const std::array< int, CURVE_TYPES_NUM > &type_counts, const IndexMask &selection, FunctionRef< void(IndexMask)> catmull_rom_fn, FunctionRef< void(IndexMask)> poly_fn, FunctionRef< void(IndexMask)> bezier_fn, FunctionRef< void(IndexMask)> nurbs_fn)
Vector< AttributeTransferData > retrieve_attributes_for_transfer(const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, AttrDomainMask domain_mask, const AttributeFilter &attribute_filter={})
auto attribute_filter_with_skip_ref(AttributeFilter filter, const Span< StringRef > skip)
void copy_attributes_group_to_group(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
static void trim_bezier_curves(const bke::CurvesGeometry &src_curves, bke::CurvesGeometry &dst_curves, const IndexMask &selection, const Span< bke::curves::CurvePoint > start_points, const Span< bke::curves::CurvePoint > end_points, const Span< bke::curves::IndexRangeCyclic > src_ranges, MutableSpan< bke::AttributeTransferData > transfer_attributes)
static bke::curves::CurvePoint lookup_point_bezier(const Span< int > bezier_offsets, const Span< float > lengths, const float sample_length, const bool cyclic, const int num_curve_points)
static void sample_interval_linear(const Span< T > src_data, MutableSpan< T > dst_data, bke::curves::IndexRangeCyclic src_range, const IndexRange dst_range, const bke::curves::CurvePoint start_point, const bke::curves::CurvePoint end_point)
static bke::curves::CurvePoint lookup_curve_point(const bke::CurvesGeometry &src_curves, const OffsetIndices< int > evaluated_points_by_curve, const CurveType curve_type, const int64_t curve_index, const Span< float > accumulated_lengths, const float sample_length, const bool cyclic, const int resolution, const int num_curve_points)
static bke::curves::bezier::Insertion knot_insert_bezier(const Span< float3 > positions, const Span< float3 > handles_left, const Span< float3 > handles_right, const bke::curves::CurvePoint insertion_point)
bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, const IndexMask &selection, const VArray< float > &starts, const VArray< float > &ends, GeometryNodeCurveSampleMode mode, const bke::AttributeFilter &attribute_filter)
static int64_t copy_point_data_between_endpoints(const Span< T > src_data, MutableSpan< T > dst_data, const bke::curves::IndexRangeCyclic src_range, int64_t dst_index)
static void trim_polygonal_curves(const bke::CurvesGeometry &src_curves, bke::CurvesGeometry &dst_curves, const IndexMask &selection, const Span< bke::curves::CurvePoint > start_points, const Span< bke::curves::CurvePoint > end_points, const Span< bke::curves::IndexRangeCyclic > src_ranges, MutableSpan< bke::AttributeTransferData > transfer_attributes)
static float trim_sample_length(const Span< float > accumulated_lengths, const float sample_length, const GeometryNodeCurveSampleMode mode)
static T interpolate_catmull_rom(const Span< T > src_data, const bke::curves::CurvePoint insertion_point, const bool src_cyclic)
static bke::curves::CurvePoint lookup_point_polygonal(const Span< float > lengths, const float sample_length, const bool cyclic, const int evaluated_size)
static bke::curves::CurvePoint lookup_point_uniform_spacing(const Span< float > lengths, const float sample_length, const bool cyclic, const int resolution, const int num_curve_points)
static void trim_attribute_linear(const bke::CurvesGeometry &src_curves, bke::CurvesGeometry &dst_curves, const IndexMask &selection, const Span< bke::curves::CurvePoint > start_points, const Span< bke::curves::CurvePoint > end_points, const Span< bke::curves::IndexRangeCyclic > src_ranges, MutableSpan< bke::AttributeTransferData > transfer_attributes)
static void compute_curve_trim_parameters(const bke::CurvesGeometry &curves, const IndexMask &selection, const VArray< float > &starts, const VArray< float > &ends, const GeometryNodeCurveSampleMode mode, MutableSpan< int > dst_curve_size, MutableSpan< bke::curves::CurvePoint > start_points, MutableSpan< bke::curves::CurvePoint > end_points, MutableSpan< bke::curves::IndexRangeCyclic > src_ranges)
static void sample_interval_catmull_rom(const Span< T > src_data, MutableSpan< T > dst_data, bke::curves::IndexRangeCyclic src_range, const IndexRange dst_range, const bke::curves::CurvePoint start_point, const bke::curves::CurvePoint end_point, const bool src_cyclic)
static void trim_catmull_rom_curves(const bke::CurvesGeometry &src_curves, bke::CurvesGeometry &dst_curves, const IndexMask &selection, const Span< bke::curves::CurvePoint > start_points, const Span< bke::curves::CurvePoint > end_points, const Span< bke::curves::IndexRangeCyclic > src_ranges, MutableSpan< bke::AttributeTransferData > transfer_attributes)
static void fill_nurbs_data(bke::CurvesGeometry &dst_curves, const IndexMask &selection)
static void sample_interval_bezier(const Span< float3 > src_positions, const Span< float3 > src_handles_l, const Span< float3 > src_handles_r, const Span< int8_t > src_types_l, const Span< int8_t > src_types_r, MutableSpan< float3 > dst_positions, MutableSpan< float3 > dst_handles_l, MutableSpan< float3 > dst_handles_r, MutableSpan< int8_t > dst_types_l, MutableSpan< int8_t > dst_types_r, bke::curves::IndexRangeCyclic src_range, const IndexRange dst_range, const bke::curves::CurvePoint start_point, const bke::curves::CurvePoint end_point)
static void trim_evaluated_curves(const bke::CurvesGeometry &src_curves, bke::CurvesGeometry &dst_curves, const IndexMask &selection, const Span< bke::curves::CurvePoint > start_points, const Span< bke::curves::CurvePoint > end_points, const Span< bke::curves::IndexRangeCyclic > src_ranges, MutableSpan< bke::AttributeTransferData > transfer_attributes)
static void fill_bezier_data(bke::CurvesGeometry &dst_curves, const IndexMask &selection)
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)
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)
VecBase< float, 3 > float3