Blender V5.0
set_curve_type.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_attribute.hh"
7#include "BKE_curves.hh"
8#include "BKE_curves_utils.hh"
9#include "BKE_deform.hh"
10
11#include "BLI_array_utils.hh"
12#include "BLI_task.hh"
13
14#include "GEO_set_curve_type.hh"
15
16namespace blender::geometry {
17
24static bool is_nurbs_to_bezier_one_to_one(const KnotsMode knots_mode)
25{
27 return true;
28 }
29 return false;
30}
31
32template<typename T>
33static void scale_input_assign(const Span<T> src,
34 const int scale,
35 const int offset,
37{
38 for (const int i : dst.index_range()) {
39 dst[i] = src[i * scale + offset];
40 }
41}
42
47template<typename T> static void bezier_generic_to_nurbs(const Span<T> src, MutableSpan<T> dst)
48{
49 for (const int i : src.index_range()) {
50 dst[i * 3] = src[i];
51 dst[i * 3 + 1] = src[i];
52 dst[i * 3 + 2] = src[i];
53 }
54}
55
56static void bezier_generic_to_nurbs(const GSpan src, GMutableSpan dst)
57{
59 using T = decltype(dummy);
60 bezier_generic_to_nurbs(src.typed<T>(), dst.typed<T>());
61 });
62}
63
64static void bezier_positions_to_nurbs(const Span<float3> src_positions,
65 const Span<float3> src_handles_l,
66 const Span<float3> src_handles_r,
67 MutableSpan<float3> dst_positions)
68{
69 for (const int i : src_positions.index_range()) {
70 dst_positions[i * 3] = src_handles_l[i];
71 dst_positions[i * 3 + 1] = src_positions[i];
72 dst_positions[i * 3 + 2] = src_handles_r[i];
73 }
74}
75
76static void catmull_rom_to_bezier_handles(const Span<float3> src_positions,
77 const bool cyclic,
78 MutableSpan<float3> dst_handles_l,
79 MutableSpan<float3> dst_handles_r)
80{
81 /* Catmull Rom curves are the same as Bezier curves with automatically defined handle positions.
82 * This constant defines the portion of the distance between the next/previous points to use for
83 * the length of the handles. */
84 constexpr float handle_scale = 1.0f / 6.0f;
85
86 if (src_positions.size() == 1) {
87 dst_handles_l.first() = src_positions.first();
88 dst_handles_r.first() = src_positions.first();
89 return;
90 }
91
92 const float3 first_offset = cyclic ? src_positions[1] - src_positions.last() :
93 src_positions[1] - src_positions[0];
94 dst_handles_r.first() = src_positions.first() + first_offset * handle_scale;
95 dst_handles_l.first() = src_positions.first() - first_offset * handle_scale;
96
97 const float3 last_offset = cyclic ? src_positions.first() - src_positions.last(1) :
98 src_positions.last() - src_positions.last(1);
99 dst_handles_l.last() = src_positions.last() - last_offset * handle_scale;
100 dst_handles_r.last() = src_positions.last() + last_offset * handle_scale;
101
102 for (const int i : src_positions.index_range().drop_front(1).drop_back(1)) {
103 const float3 left_offset = src_positions[i - 1] - src_positions[i + 1];
104 dst_handles_l[i] = src_positions[i] + left_offset * handle_scale;
105
106 const float3 right_offset = src_positions[i + 1] - src_positions[i - 1];
107 dst_handles_r[i] = src_positions[i] + right_offset * handle_scale;
108 }
109}
110
111static void catmull_rom_to_nurbs_positions(const Span<float3> src_positions,
112 const bool cyclic,
113 MutableSpan<float3> dst_positions)
114{
115 /* Convert the Catmull Rom position data to Bezier handles in order to reuse the Bezier to
116 * NURBS positions assignment. If this becomes a bottleneck, this step could be avoided. */
117 Array<float3, 32> bezier_handles_l(src_positions.size());
118 Array<float3, 32> bezier_handles_r(src_positions.size());
119 catmull_rom_to_bezier_handles(src_positions, cyclic, bezier_handles_l, bezier_handles_r);
120 bezier_positions_to_nurbs(src_positions, bezier_handles_l, bezier_handles_r, dst_positions);
121}
122
123template<typename T>
124static void nurbs_to_bezier_assign(const Span<T> src,
125 const MutableSpan<T> dst,
126 const KnotsMode knots_mode)
127{
128 switch (knots_mode) {
130 for (const int i : dst.index_range()) {
131 dst[i] = src[(i + 1) % src.size()];
132 }
133 break;
135 for (const int i : dst.index_range().drop_back(1).drop_front(1)) {
136 dst[i] = src[i + 1];
137 }
138 dst.first() = src.first();
139 dst.last() = src.last();
140 break;
141 default:
142 /* Every 3rd NURBS position (starting from index 1) should have its attributes transferred.
143 */
144 scale_input_assign<T>(src, 3, 1, dst);
145 }
146}
147
148static void nurbs_to_bezier_assign(const GSpan src, const KnotsMode knots_mode, GMutableSpan dst)
149{
151 using T = decltype(dummy);
152 nurbs_to_bezier_assign(src.typed<T>(), dst.typed<T>(), knots_mode);
153 });
154}
155
157 const KnotsMode knots_mode)
158{
159 const int nurbs_positions_num = nurbs_positions.size();
160 Vector<float3> handle_positions;
161
162 if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
163 const bool is_periodic = knots_mode == NURBS_KNOT_MODE_NORMAL;
164 if (is_periodic) {
165 handle_positions.append(nurbs_positions[1] +
166 ((nurbs_positions[0] - nurbs_positions[1]) / 3));
167 }
168 else {
169 handle_positions.append(2 * nurbs_positions[0] - nurbs_positions[1]);
170 handle_positions.append(nurbs_positions[1]);
171 }
172
173 /* Place Bezier handles on interior NURBS hull segments. Those handles can be either placed on
174 * endpoints, midpoints or 1/3 of the distance of a hull segment. */
175 const int segments_num = nurbs_positions_num - 1;
176 const bool ignore_interior_segment = segments_num == 3 && is_periodic == false;
177 if (ignore_interior_segment == false) {
178 const float mid_offset = float(segments_num - 1) / 2.0f;
179 for (const int i : IndexRange(1, segments_num - 2)) {
180 /* Divisor can have values: 1, 2 or 3. */
181 const int divisor = is_periodic ?
182 3 :
183 std::min(3, int(-std::abs(i - mid_offset) + mid_offset + 1.0f));
184 const float3 &p1 = nurbs_positions[i];
185 const float3 &p2 = nurbs_positions[i + 1];
186 const float3 displacement = (p2 - p1) / divisor;
187 const int num_handles_on_segment = divisor < 3 ? 1 : 2;
188 for (int j : IndexRange(1, num_handles_on_segment)) {
189 handle_positions.append(p1 + (displacement * j));
190 }
191 }
192 }
193
194 const int last_index = nurbs_positions_num - 1;
195 if (is_periodic) {
196 handle_positions.append(
197 nurbs_positions[last_index - 1] +
198 ((nurbs_positions[last_index] - nurbs_positions[last_index - 1]) / 3));
199 }
200 else {
201 handle_positions.append(nurbs_positions[last_index - 1]);
202 handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
203 }
204 }
205 else {
206 for (const int i : IndexRange(nurbs_positions_num)) {
207 if (i % 3 == 1) {
208 continue;
209 }
210 handle_positions.append(nurbs_positions[i]);
211 }
212 if (nurbs_positions_num % 3 == 1) {
213 handle_positions.pop_last();
214 }
215 else if (nurbs_positions_num % 3 == 2) {
216 const int last_index = nurbs_positions_num - 1;
217 handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
218 }
219 }
220
221 return handle_positions;
222}
223
224static void create_nurbs_to_bezier_positions(const Span<float3> nurbs_positions,
225 const Span<float3> handle_positions,
226 const KnotsMode knots_mode,
227 MutableSpan<float3> bezier_positions)
228{
229 if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
230 for (const int i : bezier_positions.index_range()) {
231 bezier_positions[i] = math::interpolate(
232 handle_positions[i * 2], handle_positions[i * 2 + 1], 0.5f);
233 }
234 }
235 else {
236 /* Every 3rd NURBS position (starting from index 1) should be converted to Bezier position. */
237 scale_input_assign(nurbs_positions, 3, 1, bezier_positions);
238 }
239}
240
241static int to_bezier_size(const CurveType src_type,
242 const bool cyclic,
243 const KnotsMode knots_mode,
244 const int src_size)
245{
246 switch (src_type) {
247 case CURVE_TYPE_NURBS: {
248 if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
249 return cyclic ? src_size : std::max(1, src_size - 2);
250 }
251 return (src_size + 1) / 3;
252 }
253 default:
254 return src_size;
255 }
256}
257
258static int to_nurbs_size(const CurveType src_type, const int src_size)
259{
260 switch (src_type) {
263 return src_size * 3;
264 default:
265 return src_size;
266 }
267}
268
270 const IndexMask &selection,
271 const bke::AttributeFilter &attribute_filter)
272{
273 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
274 const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes();
275 const VArray<int8_t> src_types = src_curves.curve_types();
276 const VArray<bool> src_cyclic = src_curves.cyclic();
277 const Span<float3> src_positions = src_curves.positions();
278 const bke::AttributeAccessor src_attributes = src_curves.attributes();
279 IndexMaskMemory memory;
280 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
281
283 /* Copy vertex groups from source curves to allow copying vertex group attributes. */
285 dst_curves.fill_curve_types(selection, CURVE_TYPE_BEZIER);
286
287 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
288 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
289 selection.foreach_index(GrainSize(1024), [&](const int i) {
290 dst_offsets[i] = to_bezier_size(CurveType(src_types[i]),
291 src_cyclic[i],
292 KnotsMode(src_knot_modes[i]),
293 src_points_by_curve[i].size());
294 });
296 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
297 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
298
299 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
300 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
301 MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
302 MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write();
303 MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write();
304 MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
306 src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, attribute_filter);
307 Set<StringRef> attributes_to_skip = {
308 "position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"};
309 if (!dst_curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
310 attributes_to_skip.add_new("nurbs_weight");
311 }
312
313 auto catmull_rom_to_bezier = [&](const IndexMask &selection) {
315 dst_points_by_curve, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
317 dst_points_by_curve, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
319 src_points_by_curve, dst_points_by_curve, selection, src_positions, dst_positions);
320
321 selection.foreach_index(GrainSize(512), [&](const int i) {
322 const IndexRange src_points = src_points_by_curve[i];
323 const IndexRange dst_points = dst_points_by_curve[i];
324 catmull_rom_to_bezier_handles(src_positions.slice(src_points),
325 src_cyclic[i],
326 dst_handles_l.slice(dst_points),
327 dst_handles_r.slice(dst_points));
328 });
329
330 for (bke::AttributeTransferData &attribute : generic_attributes) {
331 if (attributes_to_skip.contains(attribute.name)) {
332 continue;
333 }
335 src_points_by_curve, dst_points_by_curve, selection, attribute.src, attribute.dst.span);
336 }
337 };
338
339 auto poly_to_bezier = [&](const IndexMask &selection) {
341 src_points_by_curve, dst_points_by_curve, selection, src_positions, dst_positions);
343 dst_points_by_curve, selection, BEZIER_HANDLE_VECTOR, dst_types_l);
345 dst_points_by_curve, selection, BEZIER_HANDLE_VECTOR, dst_types_r);
347 for (bke::AttributeTransferData &attribute : generic_attributes) {
348 if (attributes_to_skip.contains(attribute.name)) {
349 continue;
350 }
352 src_points_by_curve, dst_points_by_curve, selection, attribute.src, attribute.dst.span);
353 }
354 };
355
356 auto bezier_to_bezier = [&](const IndexMask &selection) {
357 const VArraySpan<int8_t> src_types_l = src_curves.handle_types_left();
358 const VArraySpan<int8_t> src_types_r = src_curves.handle_types_right();
359 const Span<float3> src_handles_l = *src_curves.handle_positions_left();
360 const Span<float3> src_handles_r = *src_curves.handle_positions_right();
361
363 src_points_by_curve, dst_points_by_curve, selection, src_positions, dst_positions);
365 src_points_by_curve, dst_points_by_curve, selection, src_handles_l, dst_handles_l);
367 src_points_by_curve, dst_points_by_curve, selection, src_handles_r, dst_handles_r);
369 src_points_by_curve, dst_points_by_curve, selection, src_types_l, dst_types_l);
371 src_points_by_curve, dst_points_by_curve, selection, src_types_r, dst_types_r);
372
374
375 for (bke::AttributeTransferData &attribute : generic_attributes) {
376 if (attributes_to_skip.contains(attribute.name)) {
377 continue;
378 }
380 src_points_by_curve, dst_points_by_curve, selection, attribute.src, attribute.dst.span);
381 }
382 };
383
384 auto nurbs_to_bezier = [&](const IndexMask &selection) {
386 dst_points_by_curve, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
388 dst_points_by_curve, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
389
390 selection.foreach_index(GrainSize(64), [&](const int i) {
391 const IndexRange src_points = src_points_by_curve[i];
392 const IndexRange dst_points = dst_points_by_curve[i];
393 const Span<float3> src_curve_positions = src_positions.slice(src_points);
394 if (dst_points.size() == 1) {
395 const float3 &position = src_positions[src_points.first()];
396 dst_positions[dst_points.first()] = position;
397 dst_handles_l[dst_points.first()] = position;
398 dst_handles_r[dst_points.first()] = position;
399 return;
400 }
401
402 KnotsMode knots_mode = KnotsMode(src_knot_modes[i]);
403 Span<float3> nurbs_positions = src_curve_positions;
404 Vector<float3> nurbs_positions_vector;
405 if (src_cyclic[i] && is_nurbs_to_bezier_one_to_one(knots_mode)) {
406 /* For conversion treat this as periodic closed curve. Extend NURBS hull to first and
407 * second point which will act as a skeleton for placing Bezier handles. */
408 nurbs_positions_vector.extend(src_curve_positions);
409 nurbs_positions_vector.append(src_curve_positions[0]);
410 nurbs_positions_vector.append(src_curve_positions[1]);
411 nurbs_positions = nurbs_positions_vector;
412 knots_mode = NURBS_KNOT_MODE_NORMAL;
413 }
414
415 const Vector<float3> handle_positions = create_nurbs_to_bezier_handles(nurbs_positions,
416 knots_mode);
417
418 scale_input_assign(handle_positions.as_span(), 2, 0, dst_handles_l.slice(dst_points));
419 scale_input_assign(handle_positions.as_span(), 2, 1, dst_handles_r.slice(dst_points));
420
422 nurbs_positions, handle_positions, knots_mode, dst_positions.slice(dst_points));
423 });
424
425 for (bke::AttributeTransferData &attribute : generic_attributes) {
426 if (attributes_to_skip.contains(attribute.name)) {
427 continue;
428 }
429 selection.foreach_index(GrainSize(512), [&](const int i) {
430 const IndexRange src_points = src_points_by_curve[i];
431 const IndexRange dst_points = dst_points_by_curve[i];
432 nurbs_to_bezier_assign(attribute.src.slice(src_points),
433 KnotsMode(src_knot_modes[i]),
434 attribute.dst.span.slice(dst_points));
435 });
436 }
437 };
438
440 src_curves.curve_type_counts(),
441 selection,
442 catmull_rom_to_bezier,
443 poly_to_bezier,
444 bezier_to_bezier,
445 nurbs_to_bezier);
446
447 for (bke::AttributeTransferData &attribute : generic_attributes) {
449 src_points_by_curve, dst_points_by_curve, unselected, attribute.src, attribute.dst.span);
450
451 attribute.dst.finish();
452 }
453 bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves);
454 return dst_curves;
455}
456
458 const IndexMask &selection,
459 const bke::AttributeFilter &attribute_filter)
460{
461 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
462 const VArray<int8_t> src_types = src_curves.curve_types();
463 const VArray<bool> src_cyclic = src_curves.cyclic();
464 const Span<float3> src_positions = src_curves.positions();
465 const bke::AttributeAccessor src_attributes = src_curves.attributes();
466 IndexMaskMemory memory;
467 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
468
470 /* Copy vertex groups from source curves to allow copying vertex group attributes. */
472 dst_curves.fill_curve_types(selection, CURVE_TYPE_NURBS);
473
474 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
475 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
476 selection.foreach_index(GrainSize(1024), [&](const int i) {
477 dst_offsets[i] = to_nurbs_size(CurveType(src_types[i]), src_points_by_curve[i].size());
478 });
480 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
481 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
482
483 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
484 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
486 src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, attribute_filter);
487 const Set<StringRef> attributes_to_skip = {"position",
488 "handle_type_left",
489 "handle_type_right",
490 "handle_right",
491 "handle_left",
492 "nurbs_weight"};
493
494 auto fill_weights_if_necessary = [&](const IndexMask &selection) {
495 if (src_attributes.contains("nurbs_weight")) {
497 dst_points_by_curve, selection, 1.0f, dst_curves.nurbs_weights_for_write());
498 }
499 };
500
501 auto catmull_rom_to_nurbs = [&](const IndexMask &selection) {
504 dst_curves.nurbs_knots_modes_for_write(), NURBS_KNOT_MODE_BEZIER, selection);
505 fill_weights_if_necessary(selection);
506
507 selection.foreach_segment(GrainSize(512), [&](const IndexMaskSegment segment) {
508 for (const int i : segment) {
509 const IndexRange src_points = src_points_by_curve[i];
510 const IndexRange dst_points = dst_points_by_curve[i];
512 src_positions.slice(src_points), src_cyclic[i], dst_positions.slice(dst_points));
513 }
514 });
515
516 for (bke::AttributeTransferData &attribute : generic_attributes) {
517 if (attributes_to_skip.contains(attribute.name)) {
518 continue;
519 }
520 selection.foreach_index(GrainSize(512), [&](const int i) {
521 const IndexRange src_points = src_points_by_curve[i];
522 const IndexRange dst_points = dst_points_by_curve[i];
523 bezier_generic_to_nurbs(attribute.src.slice(src_points),
524 attribute.dst.span.slice(dst_points));
525 });
526 }
527 };
528
529 auto poly_to_nurbs = [&](const IndexMask &selection) {
532 src_points_by_curve, dst_points_by_curve, selection, src_positions, dst_positions);
533 fill_weights_if_necessary(selection);
534
535 /* Avoid using "Endpoint" knots modes for cyclic curves, since it adds a sharp point at the
536 * start/end. */
537 if (src_cyclic.is_single()) {
541 selection);
542 }
543 else {
544 VArraySpan<bool> cyclic{src_cyclic};
545 MutableSpan<int8_t> knots_modes = dst_curves.nurbs_knots_modes_for_write();
546 selection.foreach_index(GrainSize(1024), [&](const int i) {
547 knots_modes[i] = cyclic[i] ? NURBS_KNOT_MODE_NORMAL : NURBS_KNOT_MODE_ENDPOINT;
548 });
549 }
550
551 for (bke::AttributeTransferData &attribute : generic_attributes) {
552 if (attributes_to_skip.contains(attribute.name)) {
553 continue;
554 }
556 src_points_by_curve, dst_points_by_curve, selection, attribute.src, attribute.dst.span);
557 }
558 };
559
560 auto bezier_to_nurbs = [&](const IndexMask &selection) {
561 const Span<float3> src_handles_l = *src_curves.handle_positions_left();
562 const Span<float3> src_handles_r = *src_curves.handle_positions_right();
563
566 dst_curves.nurbs_knots_modes_for_write(), NURBS_KNOT_MODE_BEZIER, selection);
567 fill_weights_if_necessary(selection);
568
569 selection.foreach_index(GrainSize(512), [&](const int i) {
570 const IndexRange src_points = src_points_by_curve[i];
571 const IndexRange dst_points = dst_points_by_curve[i];
572 bezier_positions_to_nurbs(src_positions.slice(src_points),
573 src_handles_l.slice(src_points),
574 src_handles_r.slice(src_points),
575 dst_positions.slice(dst_points));
576 });
577
578 for (bke::AttributeTransferData &attribute : generic_attributes) {
579 if (attributes_to_skip.contains(attribute.name)) {
580 continue;
581 }
582 selection.foreach_index(GrainSize(512), [&](const int i) {
583 const IndexRange src_points = src_points_by_curve[i];
584 const IndexRange dst_points = dst_points_by_curve[i];
585 bezier_generic_to_nurbs(attribute.src.slice(src_points),
586 attribute.dst.span.slice(dst_points));
587 });
588 }
589 };
590
591 auto nurbs_to_nurbs = [&](const IndexMask &selection) {
593 src_points_by_curve, dst_points_by_curve, selection, src_positions, dst_positions);
594
595 if (const std::optional<Span<float>> nurbs_weights = src_curves.nurbs_weights()) {
596 array_utils::copy_group_to_group(src_points_by_curve,
597 dst_points_by_curve,
598 selection,
599 *nurbs_weights,
600 dst_curves.nurbs_weights_for_write());
601 }
602
603 for (bke::AttributeTransferData &attribute : generic_attributes) {
604 if (attributes_to_skip.contains(attribute.name)) {
605 continue;
606 }
608 src_points_by_curve, dst_points_by_curve, selection, attribute.src, attribute.dst.span);
609 }
610 };
611
613 src_curves.curve_type_counts(),
614 selection,
615 catmull_rom_to_nurbs,
616 poly_to_nurbs,
617 bezier_to_nurbs,
618 nurbs_to_nurbs);
619
620 for (bke::AttributeTransferData &attribute : generic_attributes) {
622 src_points_by_curve, dst_points_by_curve, unselected, attribute.src, attribute.dst.span);
623
624 attribute.dst.finish();
625 }
626 bke::curves::nurbs::copy_custom_knots(src_curves, IndexMask(), dst_curves);
627 return dst_curves;
628}
629
631 const IndexMask &selection,
632 const CurveType dst_type)
633{
634 bke::CurvesGeometry dst_curves(src_curves);
635 dst_curves.fill_curve_types(selection, dst_type);
637 return dst_curves;
638}
639
641 const bke::CurvesGeometry &src_curves,
642 const IndexMask &selection,
643 const CurveType dst_type,
644 const bke::AttributeFilter &attribute_filter,
646{
647 const bool use_bezier_handles = (dst_type == CURVE_TYPE_CATMULL_ROM) ?
648 options.convert_bezier_handles_to_catmull_rom_points :
649 options.convert_bezier_handles_to_poly_points;
650 if (!use_bezier_handles || !src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
651 return convert_curves_trivial(src_curves, selection, dst_type);
652 }
653
654 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
655 const VArray<int8_t> src_types = src_curves.curve_types();
656 const VArray<bool> src_cyclic = src_curves.cyclic();
657 const Span<float3> src_positions = src_curves.positions();
658 const bke::AttributeAccessor src_attributes = src_curves.attributes();
659 IndexMaskMemory memory;
660 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
661
663 /* Copy vertex groups from source curves to allow copying vertex group attributes. */
665 dst_curves.fill_curve_types(selection, dst_type);
666
667 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
668 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
669 selection.foreach_index(GrainSize(1024), [&](const int i) {
670 const IndexRange src_points = src_points_by_curve[i];
671 const CurveType src_curve_type = CurveType(src_types[i]);
672 int &size = dst_offsets[i];
673 if (src_curve_type == CURVE_TYPE_BEZIER) {
674 size = src_points.size() * 3;
675 }
676 else {
677 size = src_points.size();
678 }
679 });
681 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
682 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
683
684 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
685 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
687 src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, attribute_filter);
688 const Set<StringRef> attributes_to_skip = {"position",
689 "handle_type_left",
690 "handle_type_right",
691 "handle_right",
692 "handle_left",
693 "nurbs_weight"};
694
695 auto convert_from_catmull_rom_or_poly_or_nurbs = [&](const IndexMask &selection) {
697 src_points_by_curve, dst_points_by_curve, selection, src_positions, dst_positions);
698 for (bke::AttributeTransferData &attribute : generic_attributes) {
699 if (attributes_to_skip.contains(attribute.name)) {
700 continue;
701 }
703 src_points_by_curve, dst_points_by_curve, selection, attribute.src, attribute.dst.span);
704 }
705 };
706
707 auto convert_from_bezier = [&](const IndexMask &selection) {
708 const Span<float3> src_left_handles = *src_curves.handle_positions_left();
709 const Span<float3> src_right_handles = *src_curves.handle_positions_right();
710
711 /* Transfer positions. */
712 selection.foreach_index([&](const int curve_i) {
713 const IndexRange src_points = src_points_by_curve[curve_i];
714 const IndexRange dst_points = dst_points_by_curve[curve_i];
715 for (const int i : src_points.index_range()) {
716 const int src_point_i = src_points[i];
717 const int dst_points_start = dst_points.start() + 3 * i;
718 dst_positions[dst_points_start + 0] = src_left_handles[src_point_i];
719 dst_positions[dst_points_start + 1] = src_positions[src_point_i];
720 dst_positions[dst_points_start + 2] = src_right_handles[src_point_i];
721 }
722 });
723 /* Transfer attributes. The handles the same attribute values as their corresponding control
724 * point. */
725 for (bke::AttributeTransferData &attribute : generic_attributes) {
726 if (attributes_to_skip.contains(attribute.name)) {
727 continue;
728 }
729 const CPPType &cpp_type = attribute.src.type();
730 selection.foreach_index([&](const int curve_i) {
731 const IndexRange src_points = src_points_by_curve[curve_i];
732 const IndexRange dst_points = dst_points_by_curve[curve_i];
733 for (const int i : src_points.index_range()) {
734 const int src_point_i = src_points[i];
735 const int dst_points_start = dst_points.start() + 3 * i;
736 const void *src_value = attribute.src[src_point_i];
737 cpp_type.fill_assign_n(src_value, attribute.dst.span[dst_points_start], 3);
738 }
739 });
740 }
741 };
742
744 src_curves.curve_type_counts(),
745 selection,
746 convert_from_catmull_rom_or_poly_or_nurbs,
747 convert_from_catmull_rom_or_poly_or_nurbs,
748 convert_from_bezier,
749 convert_from_catmull_rom_or_poly_or_nurbs);
750
751 for (bke::AttributeTransferData &attribute : generic_attributes) {
753 src_points_by_curve, dst_points_by_curve, unselected, attribute.src, attribute.dst.span);
754
755 attribute.dst.finish();
756 }
757 bke::curves::nurbs::copy_custom_knots(src_curves, selection, dst_curves);
758 return dst_curves;
759}
760
766 const bke::CurvesGeometry &src_curves,
767 const IndexMask &selection,
769{
770 const VArray<int8_t> src_curve_types = src_curves.curve_types();
771 IndexMaskMemory memory;
773 selection, GrainSize(4096), memory, [&](const int curve_i) {
774 const CurveType type = CurveType(src_curve_types[curve_i]);
775 if (!options.keep_bezier_shape_as_nurbs && type == CURVE_TYPE_BEZIER) {
776 return true;
777 }
778 if (!options.keep_catmull_rom_shape_as_nurbs && type == CURVE_TYPE_CATMULL_ROM) {
779 return true;
780 }
781 return false;
782 });
783 return convert_curves_trivial(src_curves, mask, CURVE_TYPE_POLY);
784}
785
787 const IndexMask &selection,
788 const CurveType dst_type,
789 const bke::AttributeFilter &attribute_filter,
791{
792 switch (dst_type) {
794 case CURVE_TYPE_POLY:
796 src_curves, selection, dst_type, attribute_filter, options);
798 return convert_curves_to_bezier(src_curves, selection, attribute_filter);
799 case CURVE_TYPE_NURBS: {
800 if (!options.keep_bezier_shape_as_nurbs || !options.keep_catmull_rom_shape_as_nurbs) {
801 const bke::CurvesGeometry tmp_src_curves =
803 src_curves, selection, options);
804 return convert_curves_to_nurbs(tmp_src_curves, selection, attribute_filter);
805 }
806 return convert_curves_to_nurbs(src_curves, selection, attribute_filter);
807 }
808 }
810 return {};
811}
812
813} // namespace blender::geometry
@ ATTR_DOMAIN_MASK_POINT
Low-level operations for curves.
Low-level operations for curves.
support for deformation groups and hooks.
void BKE_defgroup_copy_list(ListBase *outbase, const ListBase *inbase)
Definition deform.cc:73
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define ELEM(...)
@ BEZIER_HANDLE_ALIGN
@ BEZIER_HANDLE_VECTOR
KnotsMode
@ NURBS_KNOT_MODE_ENDPOINT
@ NURBS_KNOT_MODE_NORMAL
@ NURBS_KNOT_MODE_BEZIER
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
void fill_assign_n(const void *value, void *dst, int64_t n) const
const CPPType & type() const
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
constexpr int64_t first() const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t size() const
constexpr int64_t start() const
constexpr IndexRange index_range() const
constexpr IndexRange drop_front(int64_t n) const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr T & first() const
Definition BLI_span.hh:679
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
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
void add_new(const Key &key)
Definition BLI_set.hh:233
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
void append(const T &value)
void extend(Span< T > array)
Span< T > as_span() const
bool contains(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
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()
MutableSpan< int8_t > nurbs_knots_modes_for_write()
std::optional< Span< float > > nurbs_weights() const
MutableSpan< int8_t > nurbs_orders_for_write()
VArray< int8_t > nurbs_knots_modes() const
std::optional< Span< float3 > > handle_positions_left() const
Span< float3 > positions() const
bool has_curve_with_type(CurveType type) const
std::optional< Span< float3 > > handle_positions_right() const
MutableSpan< float > nurbs_weights_for_write()
void resize(int points_num, int curves_num)
void fill_curve_types(CurveType type)
AttributeAccessor attributes() const
MutableSpan< int > offsets_for_write()
VArray< int8_t > curve_types() const
VArray< bool > cyclic() const
MutableSpan< int8_t > handle_types_left_for_write()
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
void foreach_segment(Fn &&fn) const
nullptr float
CCL_NAMESPACE_BEGIN struct Options options
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void copy_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
void copy_custom_knots(const bke::CurvesGeometry &src_curves, const IndexMask &exclude_curves, bke::CurvesGeometry &dst_curves)
bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves)
void fill_points(OffsetIndices< int > points_by_curve, const IndexMask &curve_selection, GPointer value, GMutableSpan dst)
void foreach_curve_by_type(const VArray< int8_t > &types, const std::array< int, CURVE_TYPES_NUM > &type_counts, const IndexMask &selection, FunctionRef< void(IndexMask)> catmull_rom_fn, FunctionRef< void(IndexMask)> poly_fn, FunctionRef< void(IndexMask)> bezier_fn, FunctionRef< void(IndexMask)> nurbs_fn)
Vector< AttributeTransferData > retrieve_attributes_for_transfer(const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, AttrDomainMask domain_mask, const AttributeFilter &attribute_filter={})
static bke::CurvesGeometry convert_bezier_or_catmull_rom_to_poly_before_conversion_to_nurbs(const bke::CurvesGeometry &src_curves, const IndexMask &selection, const ConvertCurvesOptions &options)
static bool is_nurbs_to_bezier_one_to_one(const KnotsMode knots_mode)
static void create_nurbs_to_bezier_positions(const Span< float3 > nurbs_positions, const Span< float3 > handle_positions, const KnotsMode knots_mode, MutableSpan< float3 > bezier_positions)
static void bezier_positions_to_nurbs(const Span< float3 > src_positions, const Span< float3 > src_handles_l, const Span< float3 > src_handles_r, MutableSpan< float3 > dst_positions)
static void nurbs_to_bezier_assign(const Span< T > src, const MutableSpan< T > dst, const KnotsMode knots_mode)
static int to_nurbs_size(const CurveType src_type, const int src_size)
static void catmull_rom_to_bezier_handles(const Span< float3 > src_positions, const bool cyclic, MutableSpan< float3 > dst_handles_l, MutableSpan< float3 > dst_handles_r)
static void bezier_generic_to_nurbs(const Span< T > src, MutableSpan< T > dst)
static bke::CurvesGeometry convert_curves_trivial(const bke::CurvesGeometry &src_curves, const IndexMask &selection, const CurveType dst_type)
static void catmull_rom_to_nurbs_positions(const Span< float3 > src_positions, const bool cyclic, MutableSpan< float3 > dst_positions)
static Vector< float3 > create_nurbs_to_bezier_handles(const Span< float3 > nurbs_positions, const KnotsMode knots_mode)
static void scale_input_assign(const Span< T > src, const int scale, const int offset, MutableSpan< T > dst)
static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &src_curves, const IndexMask &selection, const bke::AttributeFilter &attribute_filter)
static bke::CurvesGeometry convert_curves_to_catmull_rom_or_poly(const bke::CurvesGeometry &src_curves, const IndexMask &selection, const CurveType dst_type, const bke::AttributeFilter &attribute_filter, const ConvertCurvesOptions &options)
static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &src_curves, const IndexMask &selection, const bke::AttributeFilter &attribute_filter)
static int to_bezier_size(const CurveType src_type, const bool cyclic, const KnotsMode knots_mode, const int src_size)
bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves, const IndexMask &selection, CurveType dst_type, const bke::AttributeFilter &attribute_filter, const ConvertCurvesOptions &options={})
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
T interpolate(const T &a, const T &b, const FactorT &t)
void copy_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask, MutableSpan< int > sizes)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
VecBase< float, 3 > float3
ListBase vertex_group_names
i
Definition text_draw.cc:230