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