Blender V5.0
interpolate_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
6
9#include "BKE_curves.hh"
10
11#include "BLI_array_utils.hh"
12#include "BLI_assert.h"
14#include "BLI_math_vector.hh"
15#include "BLI_offset_indices.hh"
16#include "BLI_task.hh"
17
19
22
23namespace blender::geometry {
24
25using bke::CurvesGeometry;
26
27/* Returns a map that places each point in the sample index space.
28 * The map has one additional point at the end to simplify cyclic curve mapping. */
30 const bool cyclic,
31 const int samples_num)
32{
33 const IndexRange points = positions.index_range();
34 Array<float> sample_by_point(points.size() + 1);
35 sample_by_point[0] = 0.0f;
36 for (const int i : points.drop_front(1)) {
37 sample_by_point[i] = sample_by_point[i - 1] + math::distance(positions[i - 1], positions[i]);
38 }
39 sample_by_point.last() = cyclic ? sample_by_point[points.size() - 1] +
40 math::distance(positions.last(), positions.first()) :
41 sample_by_point[points.size() - 1];
42
43 /* If source segment lengths are zero use uniform mapping by index as a fallback. */
44 constexpr float length_epsilon = 1e-4f;
45 if (sample_by_point.last() <= length_epsilon) {
47 }
48
49 const float total_length = sample_by_point.last();
50 /* Factor for mapping segment length to sample index space. */
51 const float length_to_sample_count = math::safe_divide(float(samples_num), total_length);
52 for (float &sample_value : sample_by_point) {
53 sample_value *= length_to_sample_count;
54 }
55
56 return sample_by_point;
57}
58
59static void assign_samples_to_segments(const int num_dst_points,
60 const Span<float3> src_positions,
61 const bool cyclic,
62 MutableSpan<int> dst_sample_offsets)
63{
64 const IndexRange src_points = src_positions.index_range();
65 BLI_assert(src_points.size() > 0);
66 BLI_assert(num_dst_points > 0);
67 BLI_assert(num_dst_points >= src_points.size());
68 BLI_assert(dst_sample_offsets.size() == src_points.size() + 1);
69
70 /* Extra points of the destination curve that need to be distributed on source segments. */
71 const int num_free_samples = num_dst_points - int(src_points.size());
72 const Array<float> sample_by_point = build_point_to_sample_map(
73 src_positions, cyclic, num_free_samples);
74
75 int samples_start = 0;
76 for (const int src_point_i : src_points) {
77 dst_sample_offsets[src_point_i] = samples_start;
78
79 /* Use rounding to distribute samples equally over all segments. */
80 const int free_samples = math::round(sample_by_point[src_point_i + 1]) -
81 math::round(sample_by_point[src_point_i]);
82 samples_start += 1 + free_samples;
83 }
84
85 /* This also assigns any remaining samples in case of rounding error. */
86 dst_sample_offsets.last() = num_dst_points;
87}
88
89void sample_curve_padded(const Span<float3> positions,
90 const bool cyclic,
91 MutableSpan<int> r_indices,
92 MutableSpan<float> r_factors)
93{
94 const int num_dst_points = r_indices.size();
95 BLI_assert(r_factors.size() == num_dst_points);
96 const IndexRange src_points = positions.index_range();
97
98 if (num_dst_points == 0) {
99 return;
100 }
101 if (num_dst_points == 1) {
102 r_indices.first() = 0;
103 r_factors.first() = 0.0f;
104 return;
105 }
106
107 if (src_points.is_empty()) {
108 return;
109 }
110 if (src_points.size() == 1) {
111 r_indices.fill(0);
112 r_factors.fill(0.0f);
113 return;
114 }
115
116 /* If the destination curve has equal or more points then the excess samples are distributed
117 * equally over all the segments.
118 * If the destination curve is shorter the samples are placed equidistant along the source
119 * segments. */
120 if (num_dst_points >= src_points.size()) {
121 /* First destination point in each source segment. */
122 Array<int> dst_sample_offsets(src_points.size() + 1);
123 assign_samples_to_segments(num_dst_points, positions, cyclic, dst_sample_offsets);
124
125 OffsetIndices dst_samples_by_src_point = OffsetIndices<int>(dst_sample_offsets);
126 for (const int src_point_i : src_points.index_range()) {
127 const IndexRange samples = dst_samples_by_src_point[src_point_i];
128
129 r_indices.slice(samples).fill(src_point_i);
130 for (const int sample_i : samples.index_range()) {
131 const int sample = samples[sample_i];
132 const float factor = float(sample_i) / samples.size();
133 r_factors[sample] = factor;
134 }
135 }
136 }
137 else {
138 const Array<float> sample_by_point = build_point_to_sample_map(
139 positions, cyclic, num_dst_points - (cyclic ? 0 : 1));
140
141 for (const int src_point_i : src_points.index_range()) {
142 const float sample_start = sample_by_point[src_point_i];
143 const float sample_end = sample_by_point[src_point_i + 1];
144 const IndexRange samples = IndexRange::from_begin_end(math::ceil(sample_start),
145 math::ceil(sample_end));
146
147 for (const int sample : samples) {
148 r_indices[sample] = src_point_i;
149 r_factors[sample] = math::safe_divide(float(sample) - sample_start,
150 sample_end - sample_start);
151 }
152 }
153 if (!cyclic) {
154 r_indices.last() = src_points.size() - 1;
155 r_factors.last() = 0.0f;
156 }
157 }
158}
159
160static void reverse_samples(const int points_num,
161 MutableSpan<int> r_indices,
162 MutableSpan<float> r_factors)
163{
164 Vector<int> reverse_indices;
165 Vector<float> reverse_factors;
166 reverse_indices.reserve(r_indices.size());
167 reverse_factors.reserve(r_factors.size());
168 /* Indices in the last (cyclic) segment are also in the last segment when reversed. */
169 for (const int i : r_indices.index_range()) {
170 const int index = r_indices[i];
171 const float factor = r_factors[i];
172 const bool is_last_segment = index >= points_num - 1;
173
174 if (is_last_segment && factor > 0.0f) {
175 reverse_indices.append(points_num - 1);
176 reverse_factors.append(1.0f - factor);
177 }
178 }
179 /* Insert reversed indices except the last (cyclic) segment. */
180 for (const int i : r_indices.index_range()) {
181 const int index = r_indices[i];
182 const float factor = r_factors[i];
183 const bool is_last_segment = index >= points_num - 1;
184
185 if (factor > 0.0f) {
186 /* Skip the last (cyclic) segment, handled below. */
187 if (is_last_segment) {
188 continue;
189 }
190 reverse_indices.append(points_num - 2 - index);
191 reverse_factors.append(1.0f - r_factors[i]);
192 }
193 else {
194 /* Move factor 1.0 into the next segment. */
195 reverse_indices.append(points_num - 1 - index);
196 reverse_factors.append(0.0f);
197 }
198 }
199
200 r_indices.copy_from(reverse_indices);
201 r_factors.copy_from(reverse_factors);
202}
203
205 const int curve_index,
206 const bool cyclic,
207 const bool reverse,
208 MutableSpan<int> r_indices,
209 MutableSpan<float> r_factors)
210{
211 BLI_assert(curves.curves_range().contains(curve_index));
212 BLI_assert(r_indices.size() == r_factors.size());
213 const IndexRange points = curves.points_by_curve()[curve_index];
214 const Span<float3> positions = curves.positions().slice(points);
215
216 if (reverse) {
217 const int points_num = positions.size();
218 Array<float3> reverse_positions(points_num);
219 for (const int i : reverse_positions.index_range()) {
220 reverse_positions[i] = positions[points_num - 1 - i];
221 }
222
223 sample_curve_padded(reverse_positions, cyclic, r_indices, r_factors);
224
225 reverse_samples(points_num, r_indices, r_factors);
226 }
227 else {
228 sample_curve_padded(positions, cyclic, r_indices, r_factors);
229 }
230}
231
236static bool interpolate_attribute_to_curves(const StringRef attribute_id,
237 const std::array<int, CURVE_TYPES_NUM> &type_counts)
238{
239 if (bke::attribute_name_is_anonymous(attribute_id)) {
240 return true;
241 }
242 /* Bezier handles and types are interpolated manually. */
243 if (ELEM(attribute_id, "handle_type_left", "handle_type_right", "handle_left", "handle_right")) {
244 return false;
245 }
246 if (ELEM(attribute_id, "nurbs_weight")) {
247 return type_counts[CURVE_TYPE_NURBS] != 0;
248 }
249 return true;
250}
251
255static bool interpolate_attribute_to_poly_curve(const StringRef attribute_id)
256{
257 static const Set<StringRef> no_interpolation{{
258 "handle_type_left",
259 "handle_type_right",
260 "handle_right",
261 "handle_left",
262 "nurbs_weight",
263 }};
264 return !no_interpolation.contains(attribute_id);
265}
266
273
278 const CurvesGeometry &src_from_curves,
279 const CurvesGeometry &src_to_curves,
280 const bke::AttrDomain domain,
281 CurvesGeometry &dst_curves)
282{
284
285 const bke::AttributeAccessor src_from_attributes = src_from_curves.attributes();
286 const bke::AttributeAccessor src_to_attributes = src_to_curves.attributes();
287 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
288 for (const int i : ids.index_range()) {
289 bke::AttrType data_type;
290
291 const GVArray src_from_attribute = *src_from_attributes.lookup(ids[i], domain);
292 if (src_from_attribute) {
293 data_type = bke::cpp_type_to_attribute_type(src_from_attribute.type());
294
295 const GVArray src_to_attribute = *src_to_attributes.lookup(ids[i], domain, data_type);
296
297 result.src_from.append(src_from_attribute);
298 result.src_to.append(src_to_attribute ? src_to_attribute : GVArraySpan{});
299 }
300 else {
301 const GVArray src_to_attribute = *src_to_attributes.lookup(ids[i], domain);
302 /* Attribute should exist on at least one of the geometries. */
303 BLI_assert(src_to_attribute);
304
305 data_type = bke::cpp_type_to_attribute_type(src_to_attribute.type());
306
307 result.src_from.append(GVArraySpan{});
308 result.src_to.append(src_to_attribute);
309 }
310
311 bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_span(
312 ids[i], domain, data_type);
313 result.dst.append(std::move(dst_attribute));
314 }
315
316 return result;
317}
318
323 const CurvesGeometry &from_curves, const CurvesGeometry &to_curves, CurvesGeometry &dst_curves)
324{
326 auto add_attribute = [&](const bke::AttributeIter &iter) {
327 if (iter.domain != bke::AttrDomain::Point) {
328 return;
329 }
330 if (iter.data_type == bke::AttrType::String) {
331 return;
332 }
333 if (!interpolate_attribute_to_curves(iter.name, dst_curves.curve_type_counts())) {
334 return;
335 }
336 if (!interpolate_attribute_to_poly_curve(iter.name)) {
337 return;
338 }
339 /* Position is handled differently since it has non-generic interpolation for Bezier
340 * curves and because the evaluated positions are cached for each evaluated point. */
341 if (iter.name == "position") {
342 return;
343 }
344
345 ids.add(iter.name);
346 };
347
348 from_curves.attributes().foreach_attribute(add_attribute);
349 to_curves.attributes().foreach_attribute(add_attribute);
350
351 return retrieve_attribute_spans(ids, from_curves, to_curves, bke::AttrDomain::Point, dst_curves);
352}
353
358 const CurvesGeometry &from_curves, const CurvesGeometry &to_curves, CurvesGeometry &dst_curves)
359{
361 auto add_attribute = [&](const bke::AttributeIter &iter) {
362 if (iter.domain != bke::AttrDomain::Curve) {
363 return;
364 }
365 if (iter.data_type == bke::AttrType::String) {
366 return;
367 }
368 if (bke::attribute_name_is_anonymous(iter.name)) {
369 return;
370 }
371 /* Interpolation tool always outputs poly curves. */
372 if (iter.name == "curve_type") {
373 return;
374 }
375
376 ids.add(iter.name);
377 };
378
379 from_curves.attributes().foreach_attribute(add_attribute);
380 to_curves.attributes().foreach_attribute(add_attribute);
381
382 return retrieve_attribute_spans(ids, from_curves, to_curves, bke::AttrDomain::Curve, dst_curves);
383}
384
385/* Resample a span of attribute values from source curves to a destination buffer. */
386static void sample_curve_attribute(const bke::CurvesGeometry &src_curves,
387 const Span<int> src_curve_indices,
388 const OffsetIndices<int> dst_points_by_curve,
389 const GSpan src_data,
390 const IndexMask &dst_curve_mask,
391 const Span<int> dst_sample_indices,
392 const Span<float> dst_sample_factors,
393 GMutableSpan dst_data)
394{
395 const CPPType &type = src_data.type();
396 BLI_assert(dst_data.type() == type);
397
398 const OffsetIndices<int> src_points_by_curve = src_curves.points_by_curve();
399 const OffsetIndices<int> src_evaluated_points_by_curve = src_curves.evaluated_points_by_curve();
400 const VArray<int8_t> curve_types = src_curves.curve_types();
401 const VArray<int> resolutions = src_curves.resolution();
402
403#ifndef NDEBUG
404 const int dst_points_num = dst_data.size();
405 BLI_assert(dst_sample_indices.size() == dst_points_num);
406 BLI_assert(dst_sample_factors.size() == dst_points_num);
407#endif
408
409 bke::attribute_math::convert_to_static_type(type, [&](auto dummy) {
410 using T = decltype(dummy);
411 Span<T> src = src_data.typed<T>();
412 MutableSpan<T> dst = dst_data.typed<T>();
413
414 Vector<T> evaluated_data;
415 dst_curve_mask.foreach_index([&](const int i_dst_curve, const int pos) {
416 const int i_src_curve = src_curve_indices[pos];
417 if (i_src_curve < 0) {
418 return;
419 }
420
421 const IndexRange src_points = src_points_by_curve[i_src_curve];
422 const IndexRange dst_points = dst_points_by_curve[i_dst_curve];
423
424 if (curve_types[i_src_curve] == CURVE_TYPE_POLY) {
426 dst_sample_indices.slice(dst_points),
427 dst_sample_factors.slice(dst_points),
428 dst.slice(dst_points));
429 }
430 else {
431 const IndexRange src_evaluated_points = src_evaluated_points_by_curve[i_src_curve];
432 evaluated_data.reinitialize(src_evaluated_points.size());
433 src_curves.interpolate_to_evaluated(
434 i_src_curve, src.slice(src_points), evaluated_data.as_mutable_span());
435
436 Array<int> dst_sample_indices_eval(dst_points.size());
437 Array<float> dst_sample_factors_eval(dst_points.size());
438
439 if (curve_types[i_src_curve] == CURVE_TYPE_BEZIER) {
440 const Span<int> offsets = src_curves.bezier_evaluated_offsets_for_curve(i_src_curve);
441
442 for (const int i : dst_points.index_range()) {
443 const int dst_i = dst_points[i];
444 const int dst_index = dst_sample_indices[dst_i];
445 const float dst_factor = dst_sample_factors[dst_i];
447 offsets[dst_index], offsets[dst_index + 1]);
448
449 const float segment_parameter = segment_eval.first() +
450 dst_factor * segment_eval.size();
451
452 dst_sample_indices_eval[i] = math::floor(segment_parameter);
453 dst_sample_factors_eval[i] = math::mod(segment_parameter, 1.0f);
454 }
455 }
456 else if (curve_types[i_src_curve] == CURVE_TYPE_NURBS) {
457 const int src_size = src_points.size();
458 const int eval_size = src_evaluated_points.size();
459
460 for (const int i : dst_points.index_range()) {
461 const int dst_i = dst_points[i];
462 const int dst_index = dst_sample_indices[dst_i];
463 const float dst_factor = dst_sample_factors[dst_i];
464
465 const float segment_parameter = (dst_index + dst_factor) * float(eval_size) /
466 float(src_size);
467
468 dst_sample_indices_eval[i] = math::floor(segment_parameter);
469 dst_sample_factors_eval[i] = math::mod(segment_parameter, 1.0f);
470 }
471 }
472 else {
473 const int resolution = resolutions[i_src_curve];
474
475 for (const int i : dst_points.index_range()) {
476 const int dst_i = dst_points[i];
477 const int dst_index = dst_sample_indices[dst_i];
478 const float dst_factor = dst_sample_factors[dst_i];
479
480 const float segment_parameter = (dst_index + dst_factor) * resolution;
481
482 dst_sample_indices_eval[i] = math::floor(segment_parameter);
483 dst_sample_factors_eval[i] = math::mod(segment_parameter, 1.0f);
484 }
485 }
486
487 length_parameterize::interpolate(evaluated_data.as_span(),
488 dst_sample_indices_eval,
489 dst_sample_factors_eval,
490 dst.slice(dst_points));
491 }
492 });
493 });
494}
495
497{
498 const float t = parameter;
499 const float s = 1.0f - parameter;
500 return {
501 s * (3.0f * t - 1.0f),
502 9.0f * t * t - 10.0f * t,
503 10.0f * s - 9.0f * s * s,
504 t * (3.0f * t - 2.0f),
505 };
506}
507
508static int4 get_catmull_rom_indices(const int src_index,
509 const int src_index_last,
510 const bool cyclic)
511{
512 int src_index_a = src_index - 1;
513 int src_index_b = src_index;
514 int src_index_c = src_index + 1;
515 int src_index_d = src_index + 2;
516
517 if (src_index_a == -1) {
518 if (cyclic) {
519 src_index_a = src_index_last;
520 }
521 else {
522 src_index_a = 0;
523 }
524 }
525
526 if (src_index_c > src_index_last) {
527 if (cyclic) {
528 src_index_c -= src_index_last;
529 }
530 else {
531 src_index_c = src_index_last;
532 }
533 }
534
535 if (src_index_d > src_index_last) {
536 if (cyclic) {
537 src_index_d -= src_index_last;
538 }
539 else {
540 src_index_d = src_index_last;
541 }
542 }
543
544 return int4(src_index_a, src_index_b, src_index_c, src_index_d);
545}
546
547static void sample_poly_curve_positions_handles(const bool cyclic,
548 const Span<float3> src_pos,
549 const Span<int> dst_indices,
550 const Span<float> dst_factors,
551 const IndexRange dst_points,
552 const int8_t dst_type,
553 MutableSpan<float3> dst_pos,
554 MutableSpan<float3> dst_left,
555 MutableSpan<float3> dst_right,
556 MutableSpan<int8_t> dst_types_left,
557 MutableSpan<int8_t> dst_types_right)
558{
559 length_parameterize::interpolate(src_pos, dst_indices, dst_factors, dst_pos);
560
561 if (dst_type == CURVE_TYPE_BEZIER) {
562 dst_types_left.fill(BEZIER_HANDLE_VECTOR);
563 dst_types_right.fill(BEZIER_HANDLE_VECTOR);
564
565 for (const int i : dst_points.index_range()) {
566 const int i_prev = (i - 1 + dst_points.size()) % dst_points.size();
567 const int i_next = (i + 1) % dst_points.size();
568
569 /* Vector handles are one third the length of the edge. */
570 if (cyclic || i != 0) {
571 dst_left[i] = math::interpolate(dst_pos[i], dst_pos[i_prev], 1.0f / 3.0f);
572 }
573 else {
574 dst_left[i] = math::interpolate(dst_pos[i], dst_pos[i_next], -1.0f / 3.0f);
575 }
576
577 if (cyclic || i != dst_points.size() - 1) {
578 dst_right[i] = math::interpolate(dst_pos[i], dst_pos[i_next], 1.0f / 3.0f);
579 }
580 else {
581 dst_right[i] = math::interpolate(dst_pos[i], dst_pos[i_prev], -1.0f / 3.0f);
582 }
583 }
584 }
585}
586
587static void sample_catmull_rom_curve_positions_handles(const bool cyclic,
588 const IndexRange src_points,
589 const Span<float3> src_pos,
590 const Span<int> dst_indices,
591 const Span<float> dst_factors,
592 const IndexRange dst_points,
593 const int8_t dst_type,
594 MutableSpan<float3> dst_pos,
595 MutableSpan<float3> dst_left,
596 MutableSpan<float3> dst_right,
597 MutableSpan<int8_t> dst_types_left,
598 MutableSpan<int8_t> dst_types_right)
599{
600 dst_types_left.fill(BEZIER_HANDLE_ALIGN);
601 dst_types_right.fill(BEZIER_HANDLE_ALIGN);
602
603 for (const int i : dst_points.index_range()) {
604 const int src_index = dst_indices[i];
605 const float src_factor = dst_factors[i];
606
607 const int i_prev = (i - 1 + dst_points.size()) % dst_points.size();
608 const float src_factor_prev = dst_factors[i_prev];
609
610 const int i_next = (i + 1) % dst_points.size();
611 const float src_factor_next = dst_factors[i_next];
612
613 const int4 src_indices = get_catmull_rom_indices(src_index, src_points.size() - 1, cyclic);
614
615 const float3 &pos_a = src_pos[src_indices[0]];
616 const float3 &pos_b = src_pos[src_indices[1]];
617 const float3 &pos_c = src_pos[src_indices[2]];
618 const float3 &pos_d = src_pos[src_indices[3]];
619
620 if (src_factor == 0.0f) {
621 dst_pos[i] = src_pos[src_index];
622
623 if (dst_type == CURVE_TYPE_BEZIER) {
624 const float3 derivative = 0.5f * (pos_c - pos_a);
625 dst_right[i] = dst_pos[i] + derivative / 3.0f;
626 dst_left[i] = dst_pos[i] - derivative / 3.0f;
627
628 if ((cyclic || i != 0) && dst_indices[i_prev] == src_index - 1) {
629 dst_left[i] = dst_pos[i] + (dst_left[i] - dst_pos[i]) * (1.0f - src_factor_prev);
630 }
631 if ((cyclic || i != dst_points.size() - 1) && dst_indices[i_next] == src_index) {
632 dst_right[i] = dst_pos[i] + (dst_right[i] - dst_pos[i]) * src_factor_next;
633 }
634 }
635 }
636 else {
637 const float4 weights = bke::curves::catmull_rom::calculate_basis(src_factor);
638
639 dst_pos[i] = 0.5f * bke::attribute_math::mix4<float3>(weights, pos_a, pos_b, pos_c, pos_d);
640 if (dst_type == CURVE_TYPE_BEZIER) {
641 const float4 dwdt = calculate_catmull_rom_basis_derivative(src_factor);
642
643 const float3 derivative = 0.5f * bke::attribute_math::mix4<float3>(
644 dwdt, pos_a, pos_b, pos_c, pos_d);
645
646 /* Bezier handles are one third the length the derivative at the control points. */
647 dst_right[i] = dst_pos[i] + derivative / 3.0f;
648 dst_left[i] = dst_pos[i] - derivative / 3.0f;
649
650 if ((cyclic || i != 0) && dst_indices[i_prev] == src_index - 1) {
651 dst_left[i] = dst_pos[i] + (dst_left[i] - dst_pos[i]) * (src_factor - src_factor_prev);
652 }
653 if ((cyclic || i != dst_points.size() - 1) && dst_indices[i_next] == src_index) {
654 dst_right[i] = dst_pos[i] + (dst_right[i] - dst_pos[i]) * (src_factor_next - src_factor);
655 }
656 }
657 }
658 }
659}
660
661static void sample_bezier_curve_positions_handles(const bool cyclic,
662 const IndexRange src_points,
663 const Span<float3> src_pos,
664 const Span<float3> src_handle_left,
665 const Span<float3> src_handle_right,
666 const VArray<int8_t> src_types_left,
667 const VArray<int8_t> src_types_right,
668 const Span<int> dst_indices,
669 const Span<float> dst_factors,
670 const IndexRange dst_points,
671 MutableSpan<float3> dst_pos,
672 MutableSpan<float3> dst_left,
673 MutableSpan<float3> dst_right,
674 MutableSpan<int8_t> dst_types_left,
675 MutableSpan<int8_t> dst_types_right)
676{
677 const Span<float3> src_left = src_handle_left.slice(src_points);
678 const Span<float3> src_right = src_handle_right.slice(src_points);
679
680 for (const int i : dst_points.index_range()) {
681 const int src_index = dst_indices[i];
682 const float src_factor = dst_factors[i];
683
684 const int i_prev = (i - 1 + dst_points.size()) % dst_points.size();
685 const float src_factor_prev = dst_factors[i_prev];
686
687 const int i_next = (i + 1) % dst_points.size();
688 const float src_factor_next = dst_factors[i_next];
689
690 if (src_factor == 0.0f) {
691 dst_pos[i] = src_pos[src_index];
692 dst_left[i] = src_left[src_index];
693 dst_right[i] = src_right[src_index];
694
695 if ((cyclic || i != 0) && dst_indices[i_prev] == src_index - 1) {
696 dst_left[i] = dst_pos[i] + (dst_left[i] - dst_pos[i]) * (1.0f - src_factor_prev);
697 }
698 if ((cyclic || i != dst_points.size() - 1) && dst_indices[i_next] == src_index) {
699 dst_right[i] = dst_pos[i] + (dst_right[i] - dst_pos[i]) * src_factor_next;
700 }
701
702 dst_types_left[i] = src_types_left[src_index];
703 dst_types_right[i] = src_types_right[src_index];
704 }
705 else {
706 const int src_index_next = (src_index + 1) % src_pos.size();
707
709 src_pos[src_index],
710 src_right[src_index],
711 src_left[src_index_next],
712 src_pos[src_index_next],
713 src_factor);
714
715 dst_pos[i] = insert_point.position;
716 dst_left[i] = insert_point.left_handle;
717 dst_right[i] = insert_point.right_handle;
718
719 if ((cyclic || i != 0) && dst_indices[i_prev] == src_index) {
720 /* The handles already have been scaled by `src_factor`, so we divide to remove. */
721 dst_left[i] = dst_pos[i] +
722 (dst_left[i] - dst_pos[i]) * (src_factor - src_factor_prev) / src_factor;
723 }
724 if ((cyclic || i != dst_points.size() - 1) && dst_indices[i_next] == src_index) {
725 /* The handles already have been scaled by `1.0f - src_factor`, so we divide to remove.
726 */
727 dst_right[i] = dst_pos[i] + (dst_right[i] - dst_pos[i]) * (src_factor_next - src_factor) /
728 (1.0f - src_factor);
729 }
730
731 /* Output Vector type if the segment is also Vector, otherwise be aligned. */
732 if (src_types_left[src_index] == BEZIER_HANDLE_VECTOR &&
733 src_types_left[src_index_next] == BEZIER_HANDLE_VECTOR)
734 {
735 dst_types_left[i] = BEZIER_HANDLE_VECTOR;
736 dst_types_right[i] = BEZIER_HANDLE_VECTOR;
737 }
738 else {
739 dst_types_left[i] = BEZIER_HANDLE_ALIGN;
740 dst_types_right[i] = BEZIER_HANDLE_ALIGN;
741 }
742 }
743 }
744}
745
746/* Resample the positions and handles. */
748 const Span<int> src_curve_indices,
749 const OffsetIndices<int> dst_points_by_curve,
750 const VArray<int8_t> dst_types,
751 const IndexMask &dst_curve_mask,
752 const Span<int> dst_sample_indices,
753 const Span<float> dst_sample_factors,
754 MutableSpan<float3> dst_positions,
755 MutableSpan<float3> dst_handles_left,
756 MutableSpan<float3> dst_handles_right,
757 MutableSpan<int8_t> dst_handle_types_left,
758 MutableSpan<int8_t> dst_handle_types_right)
759{
760 const OffsetIndices<int> src_points_by_curve = src_curves.points_by_curve();
761 const VArray<int8_t> src_types = src_curves.curve_types();
762 const Span<float3> src_positions = src_curves.positions();
763 const VArray<bool> src_cyclic = src_curves.cyclic();
764 const std::optional<Span<float3>> src_handle_left = src_curves.handle_positions_left();
765 const std::optional<Span<float3>> src_handle_right = src_curves.handle_positions_right();
766 const VArray<int8_t> src_types_left = src_curves.handle_types_left();
767 const VArray<int8_t> src_types_right = src_curves.handle_types_right();
768
769#ifndef NDEBUG
770 const int dst_points_num = dst_positions.size();
771 BLI_assert(dst_handles_left.size() == dst_points_num);
772 BLI_assert(dst_handles_right.size() == dst_points_num);
773 BLI_assert(dst_sample_indices.size() == dst_points_num);
774 BLI_assert(dst_sample_factors.size() == dst_points_num);
775#endif
776
777 dst_curve_mask.foreach_index([&](const int i_dst_curve, const int pos) {
778 const int i_src_curve = src_curve_indices[pos];
779 if (i_src_curve < 0) {
780 return;
781 }
782
783 const bool cyclic = src_cyclic[i_src_curve];
784
785 const IndexRange src_points = src_points_by_curve[i_src_curve];
786 const IndexRange dst_points = dst_points_by_curve[i_dst_curve];
787
788 const Span<float3> src_pos = src_positions.slice(src_points);
789 const Span<int> dst_indices = dst_sample_indices.slice(dst_points);
790 const Span<float> dst_factors = dst_sample_factors.slice(dst_points);
791
792 MutableSpan<float3> dst_pos = dst_positions.slice(dst_points);
793 MutableSpan<float3> dst_left = dst_handles_left.slice(dst_points);
794 MutableSpan<float3> dst_right = dst_handles_right.slice(dst_points);
795 MutableSpan<int8_t> dst_types_left = dst_handle_types_left.slice(dst_points);
796 MutableSpan<int8_t> dst_types_right = dst_handle_types_right.slice(dst_points);
797
798 if (src_types[i_src_curve] == CURVE_TYPE_POLY) {
800 src_pos,
801 dst_indices,
802 dst_factors,
803 dst_points,
804 dst_types[i_dst_curve],
805 dst_pos,
806 dst_left,
807 dst_right,
808 dst_types_left,
809 dst_types_right);
810 }
811 else if (src_types[i_src_curve] == CURVE_TYPE_NURBS) {
812 /* NURBS take priority over Bézier, so we should never be trying to be Bézier. */
813 BLI_assert(dst_types[i_dst_curve] != CURVE_TYPE_BEZIER);
814
815 length_parameterize::interpolate(src_pos, dst_indices, dst_factors, dst_pos);
816 }
817 else if (src_types[i_src_curve] == CURVE_TYPE_CATMULL_ROM) {
819 src_points,
820 src_pos,
821 dst_indices,
822 dst_factors,
823 dst_points,
824 dst_types[i_dst_curve],
825 dst_pos,
826 dst_left,
827 dst_right,
828 dst_types_left,
829 dst_types_right);
830 }
831 else if (src_types[i_src_curve] == CURVE_TYPE_BEZIER) {
832 BLI_assert(src_handle_left);
833 BLI_assert(src_handle_right);
834
836 src_points,
837 src_pos,
838 *src_handle_left,
839 *src_handle_right,
840 src_types_left,
841 src_types_right,
842 dst_indices,
843 dst_factors,
844 dst_points,
845 dst_pos,
846 dst_left,
847 dst_right,
848 dst_types_left,
849 dst_types_right);
850 }
851 else {
853 }
854 });
855}
856
857template<typename T>
858static void mix_arrays(const Span<T> from,
859 const Span<T> to,
860 const float mix_factor,
861 const MutableSpan<T> dst)
862{
863 if (mix_factor == 0.0f) {
864 dst.copy_from(from);
865 }
866 else if (mix_factor == 1.0f) {
867 dst.copy_from(to);
868 }
869 else {
870 for (const int i : dst.index_range()) {
871 dst[i] = math::interpolate(from[i], to[i], mix_factor);
872 }
873 }
874}
875
876static void mix_arrays(const GSpan src_from,
877 const GSpan src_to,
878 const Span<float> mix_factors,
879 const IndexMask &selection,
880 const GMutableSpan dst)
881{
883 using T = decltype(dummy);
884 const Span<T> from = src_from.typed<T>();
885 const Span<T> to = src_to.typed<T>();
886 const MutableSpan<T> dst_typed = dst.typed<T>();
887 selection.foreach_index(GrainSize(512), [&](const int curve) {
888 const float mix_factor = mix_factors[curve];
889 if (mix_factor == 0.0f) {
890 dst_typed[curve] = from[curve];
891 }
892 else if (mix_factor == 1.0f) {
893 dst_typed[curve] = to[curve];
894 }
895 else {
896 dst_typed[curve] = math::interpolate(from[curve], to[curve], mix_factor);
897 }
898 });
899 });
900}
901
902static void mix_arrays(const GSpan src_from,
903 const GSpan src_to,
904 const Span<float> mix_factors,
905 const IndexMask &group_selection,
906 const OffsetIndices<int> groups,
907 const GMutableSpan dst)
908{
909 group_selection.foreach_index(GrainSize(32), [&](const int curve) {
910 const IndexRange range = groups[curve];
912 using T = decltype(dummy);
913 const Span<T> from = src_from.typed<T>();
914 const Span<T> to = src_to.typed<T>();
915 const MutableSpan<T> dst_typed = dst.typed<T>();
916 mix_arrays(from.slice(range), to.slice(range), mix_factors[curve], dst_typed.slice(range));
917 });
918 });
919}
920
921static int8_t mix_handle_type(const int8_t from_type, const int8_t to_type)
922{
923 /* Vector handles can only be mixed with other vector handles, otherwise use free handle as
924 * fallback. */
925 if (from_type == BEZIER_HANDLE_VECTOR && to_type == BEZIER_HANDLE_VECTOR) {
927 }
928 if (from_type == BEZIER_HANDLE_VECTOR || to_type == BEZIER_HANDLE_VECTOR) {
929 return BEZIER_HANDLE_FREE;
930 }
931
932 if (from_type == BEZIER_HANDLE_FREE || to_type == BEZIER_HANDLE_FREE) {
933 return BEZIER_HANDLE_FREE;
934 }
935 if (from_type == BEZIER_HANDLE_ALIGN || to_type == BEZIER_HANDLE_ALIGN) {
936 return BEZIER_HANDLE_ALIGN;
937 }
938 return BEZIER_HANDLE_AUTO;
939}
940
941static void mix_handle_type_arrays(const Span<int8_t> src_from,
942 const Span<int8_t> src_to,
943 const IndexMask &group_selection,
944 const OffsetIndices<int> groups,
945 const MutableSpan<int8_t> dst)
946{
947 group_selection.foreach_index(GrainSize(32), [&](const int curve) {
948 for (const int i : groups[curve]) {
949 dst[i] = mix_handle_type(src_from[i], src_to[i]);
950 }
951 });
952}
953
954/* Calculate the new curve's type by using the type with highest priority. */
955static void mix_curve_type(const Span<int> from_curve_indices,
956 const Span<int> to_curve_indices,
957 const VArray<int8_t> &from_types,
958 const VArray<int8_t> &to_types,
959 const IndexMask &dst_curve_mask,
960 MutableSpan<int8_t> dst_curve_types)
961{
962 dst_curve_mask.foreach_index([&](const int i_dst_curve, const int pos) {
963 const int i_from_curve = from_curve_indices[pos];
964 const int i_to_curve = to_curve_indices[pos];
965
966 if (i_from_curve < 0) {
967 dst_curve_types[i_dst_curve] = to_types[i_to_curve];
968 return;
969 }
970 if (i_to_curve < 0) {
971 dst_curve_types[i_dst_curve] = from_types[i_from_curve];
972 return;
973 }
974
975 const int8_t from_type = from_types[i_from_curve];
976 const int8_t to_type = to_types[i_to_curve];
977
978 if (from_type == CURVE_TYPE_NURBS || to_type == CURVE_TYPE_NURBS) {
979 dst_curve_types[i_dst_curve] = CURVE_TYPE_NURBS;
980 return;
981 }
982 if (from_type == CURVE_TYPE_BEZIER || to_type == CURVE_TYPE_BEZIER) {
983 dst_curve_types[i_dst_curve] = CURVE_TYPE_BEZIER;
984 return;
985 }
986 if (from_type == CURVE_TYPE_CATMULL_ROM || to_type == CURVE_TYPE_CATMULL_ROM) {
987 dst_curve_types[i_dst_curve] = CURVE_TYPE_CATMULL_ROM;
988 return;
989 }
990 dst_curve_types[i_dst_curve] = CURVE_TYPE_POLY;
991 });
992}
993
995 const CurvesGeometry &to_curves,
996 const Span<int> from_curve_indices,
997 const Span<int> to_curve_indices,
998 const Span<int> from_sample_indices,
999 const Span<int> to_sample_indices,
1000 const Span<float> from_sample_factors,
1001 const Span<float> to_sample_factors,
1002 const IndexMask &dst_curve_mask,
1003 const float mix_factor,
1004 CurvesGeometry &dst_curves,
1005 IndexMaskMemory &memory)
1006{
1007 BLI_assert(from_curve_indices.size() == dst_curve_mask.size());
1008 BLI_assert(to_curve_indices.size() == dst_curve_mask.size());
1009 BLI_assert(from_sample_indices.size() == dst_curves.points_num());
1010 BLI_assert(to_sample_indices.size() == dst_curves.points_num());
1011 BLI_assert(from_sample_factors.size() == dst_curves.points_num());
1012 BLI_assert(to_sample_factors.size() == dst_curves.points_num());
1013
1014 if (from_curves.is_empty() || to_curves.is_empty()) {
1015 return;
1016 }
1017
1020
1021 mix_curve_type(from_curve_indices,
1022 to_curve_indices,
1023 from_curves.curve_types(),
1024 to_curves.curve_types(),
1025 dst_curve_mask,
1026 dst_curves.curve_types_for_write());
1027
1028 dst_curves.update_curve_types();
1029
1030 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
1033 MutableSpan<int8_t> dst_types_left = dst_curves.handle_types_left_for_write();
1034 MutableSpan<int8_t> dst_types_right = dst_curves.handle_types_right_for_write();
1035
1037 from_curves, to_curves, dst_curves);
1039 from_curves, to_curves, dst_curves);
1040
1041 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
1042
1043 Array<bool> mix_from_to(dst_curves.curves_num());
1044 Array<bool> exclusive_from(dst_curves.curves_num());
1045 Array<bool> exclusive_to(dst_curves.curves_num());
1046 Array<float> mix_factors(dst_curves.curves_num());
1047 dst_curve_mask.foreach_index(GrainSize(512), [&](const int i_dst_curve, const int pos) {
1048 const int i_from_curve = from_curve_indices[pos];
1049 const int i_to_curve = to_curve_indices[pos];
1050 if (i_from_curve >= 0 && i_to_curve >= 0) {
1051 mix_factors[i_dst_curve] = mix_factor;
1052 mix_from_to[i_dst_curve] = true;
1053 exclusive_from[i_dst_curve] = false;
1054 exclusive_to[i_dst_curve] = false;
1055 }
1056 else if (i_to_curve >= 0) {
1057 mix_factors[i_dst_curve] = 1.0f;
1058 mix_from_to[i_dst_curve] = false;
1059 exclusive_from[i_dst_curve] = false;
1060 exclusive_to[i_dst_curve] = true;
1061 }
1062 else {
1063 mix_factors[i_dst_curve] = 0.0f;
1064 mix_from_to[i_dst_curve] = false;
1065 exclusive_from[i_dst_curve] = true;
1066 exclusive_to[i_dst_curve] = false;
1067 }
1068 });
1069
1070 /* Curve mask contains indices that may not be valid for both "from" and "to" curves. These need
1071 * to be filtered out before use with the generic array utils. These masks are exclusive so that
1072 * each element is only mixed in by one mask. */
1073 const IndexMask mix_curve_mask = IndexMask::from_bools(dst_curve_mask, mix_from_to, memory);
1074 const IndexMask from_curve_mask = IndexMask::from_bools(dst_curve_mask, exclusive_from, memory);
1075 const IndexMask to_curve_mask = IndexMask::from_bools(dst_curve_mask, exclusive_to, memory);
1076
1077 /* For every attribute, evaluate attributes from every curve in the range in the original
1078 * curve's "evaluated points", then use linear interpolation to sample to the result. */
1079 for (const int i_attribute : point_attributes.dst.index_range()) {
1080 /* Attributes that exist already on another domain can not be written to. */
1081 if (!point_attributes.dst[i_attribute]) {
1082 continue;
1083 }
1084
1085 const GSpan src_from = point_attributes.src_from[i_attribute];
1086 const GSpan src_to = point_attributes.src_to[i_attribute];
1087 GMutableSpan dst = point_attributes.dst[i_attribute].span;
1088
1089 /* Mix factors depend on which of the from/to curves geometries has attribute data. If
1090 * only one geometry has attribute data it gets the full mix weight. */
1091 if (!src_from.is_empty() && !src_to.is_empty()) {
1092 GArray<> from_samples(dst.type(), dst.size());
1093 GArray<> to_samples(dst.type(), dst.size());
1094 sample_curve_attribute(from_curves,
1095 from_curve_indices,
1096 dst_points_by_curve,
1097 src_from,
1098 dst_curve_mask,
1099 from_sample_indices,
1100 from_sample_factors,
1101 from_samples);
1102 sample_curve_attribute(to_curves,
1103 to_curve_indices,
1104 dst_points_by_curve,
1105 src_to,
1106 dst_curve_mask,
1107 to_sample_indices,
1108 to_sample_factors,
1109 to_samples);
1110 mix_arrays(from_samples, to_samples, mix_factors, dst_curve_mask, dst_points_by_curve, dst);
1111 }
1112 else if (!src_from.is_empty()) {
1113 sample_curve_attribute(from_curves,
1114 from_curve_indices,
1115 dst_points_by_curve,
1116 src_from,
1117 dst_curve_mask,
1118 from_sample_indices,
1119 from_sample_factors,
1120 dst);
1121 }
1122 else if (!src_to.is_empty()) {
1123 sample_curve_attribute(to_curves,
1124 to_curve_indices,
1125 dst_points_by_curve,
1126 src_to,
1127 dst_curve_mask,
1128 to_sample_indices,
1129 to_sample_factors,
1130 dst);
1131 }
1132 }
1133
1134 {
1135 const VArray<int8_t> dst_types = dst_curves.curve_types();
1136
1137 Array<float3> from_pos(dst_positions.size());
1138 Array<float3> to_pos(dst_positions.size());
1139 Array<float3> from_left(dst_left.size());
1140 Array<float3> to_left(dst_left.size());
1141 Array<float3> from_right(dst_right.size());
1142 Array<float3> to_right(dst_right.size());
1143
1144 Array<int8_t> from_types_left(dst_left.size());
1145 Array<int8_t> to_types_left(dst_left.size());
1146 Array<int8_t> from_types_right(dst_right.size());
1147 Array<int8_t> to_types_right(dst_right.size());
1148
1149 /* Interpolate the positions and handles to the resampled curves. */
1151 from_curve_indices,
1152 dst_points_by_curve,
1153 dst_types,
1154 dst_curve_mask,
1155 from_sample_indices,
1156 from_sample_factors,
1157 from_pos.as_mutable_span(),
1158 from_left.as_mutable_span(),
1159 from_right.as_mutable_span(),
1160 from_types_left.as_mutable_span(),
1161 from_types_right.as_mutable_span());
1163 to_curve_indices,
1164 dst_points_by_curve,
1165 dst_types,
1166 dst_curve_mask,
1167 to_sample_indices,
1168 to_sample_factors,
1169 to_pos.as_mutable_span(),
1170 to_left.as_mutable_span(),
1171 to_right.as_mutable_span(),
1172 to_types_left.as_mutable_span(),
1173 to_types_right.as_mutable_span());
1174
1175 mix_arrays(from_pos.as_span(),
1176 to_pos.as_span(),
1177 mix_factors,
1178 dst_curve_mask,
1179 dst_points_by_curve,
1180 dst_positions);
1181 mix_arrays(from_left.as_span(),
1182 to_left.as_span(),
1183 mix_factors,
1184 dst_curve_mask,
1185 dst_points_by_curve,
1186 dst_left);
1187 mix_arrays(from_right.as_span(),
1188 to_right.as_span(),
1189 mix_factors,
1190 dst_curve_mask,
1191 dst_points_by_curve,
1192 dst_right);
1193
1194 mix_handle_type_arrays(from_types_left.as_span(),
1195 to_types_left.as_span(),
1196 dst_curve_mask,
1197 dst_points_by_curve,
1198 dst_types_left);
1199 mix_handle_type_arrays(from_types_right.as_span(),
1200 to_types_right.as_span(),
1201 dst_curve_mask,
1202 dst_points_by_curve,
1203 dst_types_right);
1204
1205 dst_curves.calculate_bezier_auto_handles();
1206 }
1207
1208 for (const int i_attribute : curve_attributes.dst.index_range()) {
1209 /* Attributes that exist already on another domain can not be written to. */
1210 if (!curve_attributes.dst[i_attribute]) {
1211 continue;
1212 }
1213
1214 const GSpan src_from = curve_attributes.src_from[i_attribute];
1215 const GSpan src_to = curve_attributes.src_to[i_attribute];
1216 GMutableSpan dst = curve_attributes.dst[i_attribute].span;
1217
1218 /* Only mix "safe" attribute types for now. Other types (int, bool, etc.) are just copied from
1219 * the first curve of each pair. */
1220 const bool can_mix_attribute = ELEM(bke::cpp_type_to_custom_data_type(dst.type()),
1224 if (can_mix_attribute && !src_from.is_empty() && !src_to.is_empty()) {
1225 array_utils::copy(GVArray::from_span(src_from), from_curve_mask, dst);
1226 array_utils::copy(GVArray::from_span(src_to), to_curve_mask, dst);
1227
1228 GArray<> from_samples(dst.type(), dst.size());
1229 GArray<> to_samples(dst.type(), dst.size());
1230 array_utils::copy(GVArray::from_span(src_from), mix_curve_mask, from_samples);
1231 array_utils::copy(GVArray::from_span(src_to), mix_curve_mask, to_samples);
1232 mix_arrays(from_samples, to_samples, mix_factors, mix_curve_mask, dst);
1233 }
1234 else if (!src_from.is_empty()) {
1235 array_utils::copy(GVArray::from_span(src_from), from_curve_mask, dst);
1236 array_utils::copy(GVArray::from_span(src_from), mix_curve_mask, dst);
1237 }
1238 else if (!src_to.is_empty()) {
1239 array_utils::copy(GVArray::from_span(src_to), to_curve_mask, dst);
1240 array_utils::copy(GVArray::from_span(src_to), mix_curve_mask, dst);
1241 }
1242 }
1243
1244 for (bke::GSpanAttributeWriter &attribute : point_attributes.dst) {
1245 attribute.finish();
1246 }
1247 for (bke::GSpanAttributeWriter &attribute : curve_attributes.dst) {
1248 attribute.finish();
1249 }
1250
1251 dst_curves.tag_topology_changed();
1252}
1253
1255 const int curve_index,
1256 const bool cyclic,
1257 const bool reverse,
1258 MutableSpan<int> r_segment_indices,
1259 MutableSpan<float> r_factors)
1260{
1261 const Span<float> segment_lengths = curves.evaluated_lengths_for_curve(curve_index, cyclic);
1262 if (segment_lengths.is_empty()) {
1263 /* Handle curves with only one evaluated point. */
1264 r_segment_indices.fill(0);
1265 r_factors.fill(0.0f);
1266 return;
1267 }
1268
1269 if (reverse) {
1271 segment_lengths, !cyclic, r_segment_indices, r_factors);
1272 }
1273 else {
1274 length_parameterize::sample_uniform(segment_lengths, !cyclic, r_segment_indices, r_factors);
1275 }
1276};
1277
1278void interpolate_curves(const CurvesGeometry &from_curves,
1279 const CurvesGeometry &to_curves,
1280 const Span<int> from_curve_indices,
1281 const Span<int> to_curve_indices,
1282 const IndexMask &dst_curve_mask,
1283 const Span<bool> dst_curve_flip_direction,
1284 const float mix_factor,
1285 CurvesGeometry &dst_curves,
1286 IndexMaskMemory &memory)
1287{
1288 const VArray<bool> from_curves_cyclic = from_curves.cyclic();
1289 const VArray<bool> to_curves_cyclic = to_curves.cyclic();
1290 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
1291
1292 /* Sampling arbitrary attributes works by first interpolating them to the curve's standard
1293 * "evaluated points" and then interpolating that result with the uniform samples. This is
1294 * potentially wasteful when down-sampling a curve to many fewer points. There are two possible
1295 * solutions: only sample the necessary points for interpolation, or first sample curve
1296 * parameter/segment indices and evaluate the curve directly. */
1297 Array<int> from_sample_indices(dst_curves.points_num());
1298 Array<int> to_sample_indices(dst_curves.points_num());
1299 Array<float> from_sample_factors(dst_curves.points_num());
1300 Array<float> to_sample_factors(dst_curves.points_num());
1301
1302 from_curves.ensure_evaluated_lengths();
1303 to_curves.ensure_evaluated_lengths();
1304
1305 /* Gather uniform samples based on the accumulated lengths of the original curve. */
1306 dst_curve_mask.foreach_index(GrainSize(32), [&](const int i_dst_curve, const int pos) {
1307 const int i_from_curve = from_curve_indices[pos];
1308 const int i_to_curve = to_curve_indices[pos];
1309
1310 const IndexRange dst_points = dst_points_by_curve[i_dst_curve];
1311 /* First curve is sampled in forward direction, second curve may be reversed. */
1312 sample_curve_uniform(from_curves,
1313 i_from_curve,
1314 from_curves_cyclic[i_from_curve],
1315 false,
1316 from_sample_indices.as_mutable_span().slice(dst_points),
1317 from_sample_factors.as_mutable_span().slice(dst_points));
1318 sample_curve_uniform(to_curves,
1319 i_to_curve,
1320 to_curves_cyclic[i_to_curve],
1321 dst_curve_flip_direction[i_dst_curve],
1322 to_sample_indices.as_mutable_span().slice(dst_points),
1323 to_sample_factors.as_mutable_span().slice(dst_points));
1324 });
1325
1327 to_curves,
1328 from_curve_indices,
1329 to_curve_indices,
1330 from_sample_indices,
1331 to_sample_indices,
1332 from_sample_factors,
1333 to_sample_factors,
1334 dst_curve_mask,
1335 mix_factor,
1336 dst_curves,
1337 memory);
1338}
1339
1340} // namespace blender::geometry
Low-level operations for curves.
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ELEM(...)
@ BEZIER_HANDLE_FREE
@ BEZIER_HANDLE_ALIGN
@ BEZIER_HANDLE_VECTOR
@ BEZIER_HANDLE_AUTO
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_FLOAT2
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
Span< T > as_span() const
Definition BLI_array.hh:243
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
const T & last(const int64_t n=0) const
Definition BLI_array.hh:296
IndexRange index_range() const
Definition BLI_array.hh:360
const CPPType & type() const
MutableSpan< T > typed() const
Span< T > typed() const
const CPPType & type() const
bool is_empty() const
static GVArray from_span(GSpan span)
constexpr int64_t first() const
constexpr int64_t size() const
constexpr bool is_empty() const
static constexpr IndexRange from_begin_end(const int64_t begin, const int64_t end)
constexpr bool contains(int64_t value) const
constexpr IndexRange index_range() const
static constexpr IndexRange from_begin_end_inclusive(const int64_t begin, const int64_t last)
constexpr IndexRange drop_front(int64_t n) 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 T & first() const
Definition BLI_span.hh:679
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
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 const T & first() const
Definition BLI_span.hh:315
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 IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
bool add(const Key &key)
void append(const T &value)
void reserve(const int64_t min_capacity)
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
GAttributeReader lookup(const StringRef attribute_id) const
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
MutableSpan< int8_t > curve_types_for_write()
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
void interpolate_to_evaluated(int curve_index, GSpan src, GMutableSpan dst) const
std::optional< Span< float3 > > handle_positions_left() const
Span< float3 > positions() const
OffsetIndices< int > evaluated_points_by_curve() const
std::optional< Span< float3 > > handle_positions_right() const
AttributeAccessor attributes() const
VArray< int8_t > curve_types() const
VArray< bool > cyclic() const
MutableSpan< int8_t > handle_types_left_for_write()
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
void foreach_index(Fn &&fn) const
nullptr float
uint pos
#define T
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void fill_index_range(MutableSpan< T > span, const T start=0)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
T mix4(const float4 &weights, const T &v0, const T &v1, const T &v2, const T &v3)
Insertion insert(const float3 &point_prev, const float3 &handle_prev, const float3 &handle_next, const float3 &point_next, float parameter)
float4 calculate_basis(const float parameter)
bool attribute_name_is_anonymous(const StringRef name)
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
AttrType cpp_type_to_attribute_type(const CPPType &type)
static void sample_catmull_rom_curve_positions_handles(const bool cyclic, const IndexRange src_points, const Span< float3 > src_pos, const Span< int > dst_indices, const Span< float > dst_factors, const IndexRange dst_points, const int8_t dst_type, MutableSpan< float3 > dst_pos, MutableSpan< float3 > dst_left, MutableSpan< float3 > dst_right, MutableSpan< int8_t > dst_types_left, MutableSpan< int8_t > dst_types_right)
static AttributesForInterpolation gather_curve_attributes_to_interpolate(const CurvesGeometry &from_curves, const CurvesGeometry &to_curves, CurvesGeometry &dst_curves)
static void mix_arrays(const Span< T > from, const Span< T > to, const float mix_factor, const MutableSpan< T > dst)
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 sample_bezier_curve_positions_handles(const bool cyclic, const IndexRange src_points, const Span< float3 > src_pos, const Span< float3 > src_handle_left, const Span< float3 > src_handle_right, const VArray< int8_t > src_types_left, const VArray< int8_t > src_types_right, const Span< int > dst_indices, const Span< float > dst_factors, const IndexRange dst_points, MutableSpan< float3 > dst_pos, MutableSpan< float3 > dst_left, MutableSpan< float3 > dst_right, MutableSpan< int8_t > dst_types_left, MutableSpan< int8_t > dst_types_right)
void interpolate_curves_with_samples(const bke::CurvesGeometry &from_curves, const bke::CurvesGeometry &to_curves, Span< int > from_curve_indices, Span< int > to_curve_indices, Span< int > from_sample_indices, Span< int > to_sample_indices, Span< float > from_sample_factors, Span< float > to_sample_factors, const IndexMask &dst_curve_mask, float mix_factor, bke::CurvesGeometry &dst_curves, IndexMaskMemory &memory)
static void sample_poly_curve_positions_handles(const bool cyclic, const Span< float3 > src_pos, const Span< int > dst_indices, const Span< float > dst_factors, const IndexRange dst_points, const int8_t dst_type, MutableSpan< float3 > dst_pos, MutableSpan< float3 > dst_left, MutableSpan< float3 > dst_right, MutableSpan< int8_t > dst_types_left, MutableSpan< int8_t > dst_types_right)
static void sample_curve_positions_and_handles(const bke::CurvesGeometry &src_curves, const Span< int > src_curve_indices, const OffsetIndices< int > dst_points_by_curve, const VArray< int8_t > dst_types, const IndexMask &dst_curve_mask, const Span< int > dst_sample_indices, const Span< float > dst_sample_factors, MutableSpan< float3 > dst_positions, MutableSpan< float3 > dst_handles_left, MutableSpan< float3 > dst_handles_right, MutableSpan< int8_t > dst_handle_types_left, MutableSpan< int8_t > dst_handle_types_right)
static float4 calculate_catmull_rom_basis_derivative(const float parameter)
static Array< float > build_point_to_sample_map(const Span< float3 > positions, const bool cyclic, const int samples_num)
static void mix_curve_type(const Span< int > from_curve_indices, const Span< int > to_curve_indices, const VArray< int8_t > &from_types, const VArray< int8_t > &to_types, const IndexMask &dst_curve_mask, MutableSpan< int8_t > dst_curve_types)
void sample_curve_padded(const Span< float3 > positions, bool cyclic, MutableSpan< int > r_indices, MutableSpan< float > r_factors)
static void sample_curve_attribute(const bke::CurvesGeometry &src_curves, const Span< int > src_curve_indices, const OffsetIndices< int > dst_points_by_curve, const GSpan src_data, const IndexMask &dst_curve_mask, const Span< int > dst_sample_indices, const Span< float > dst_sample_factors, GMutableSpan dst_data)
static void mix_handle_type_arrays(const Span< int8_t > src_from, const Span< int8_t > src_to, const IndexMask &group_selection, const OffsetIndices< int > groups, const MutableSpan< int8_t > dst)
static int4 get_catmull_rom_indices(const int src_index, const int src_index_last, const bool cyclic)
static void sample_curve_uniform(const bke::CurvesGeometry &curves, const int curve_index, const bool cyclic, const bool reverse, MutableSpan< int > r_segment_indices, MutableSpan< float > r_factors)
void interpolate_curves(const bke::CurvesGeometry &from_curves, const bke::CurvesGeometry &to_curves, Span< int > from_curve_indices, Span< int > to_curve_indices, const IndexMask &dst_curve_mask, Span< bool > dst_curve_flip_direction, float mix_factor, bke::CurvesGeometry &dst_curves, IndexMaskMemory &memory)
static bool interpolate_attribute_to_poly_curve(const StringRef attribute_id)
static void assign_samples_to_segments(const int num_dst_points, const Span< float3 > src_positions, const bool cyclic, MutableSpan< int > dst_sample_offsets)
static bool interpolate_attribute_to_curves(const StringRef attribute_id, const std::array< int, CURVE_TYPES_NUM > &type_counts)
static int8_t mix_handle_type(const int8_t from_type, const int8_t to_type)
static AttributesForInterpolation gather_point_attributes_to_interpolate(const CurvesGeometry &from_curves, const CurvesGeometry &to_curves, CurvesGeometry &dst_curves)
static void reverse_samples(const int points_num, MutableSpan< int > r_indices, MutableSpan< float > r_factors)
void sample_uniform_reverse(Span< float > accumulated_segment_lengths, bool include_first_point, MutableSpan< int > r_segment_indices, MutableSpan< float > r_factors)
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)
T safe_divide(const T &a, const T &b)
T floor(const T &a)
T distance(const T &a, const T &b)
T interpolate(const T &a, const T &b, const FactorT &t)
T ceil(const T &a)
T mod(const T &a, const T &b)
T round(const T &a)
VecBase< int32_t, 4 > int4
VecBase< float, 4 > float4
VecBase< float, 3 > float3
Vector< bke::GSpanAttributeWriter > dst
i
Definition text_draw.cc:230