Blender V4.3
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
10#include "BLI_array_utils.hh"
11#include "BLI_task.hh"
12
13#include "GEO_set_curve_type.hh"
14
15namespace blender::geometry {
16
23static bool is_nurbs_to_bezier_one_to_one(const KnotsMode knots_mode)
24{
26 return true;
27 }
28 return false;
29}
30
31template<typename T>
32static void scale_input_assign(const Span<T> src,
33 const int scale,
34 const int offset,
36{
37 for (const int i : dst.index_range()) {
38 dst[i] = src[i * scale + offset];
39 }
40}
41
46template<typename T> static void bezier_generic_to_nurbs(const Span<T> src, MutableSpan<T> dst)
47{
48 for (const int i : src.index_range()) {
49 dst[i * 3] = src[i];
50 dst[i * 3 + 1] = src[i];
51 dst[i * 3 + 2] = src[i];
52 }
53}
54
55static void bezier_generic_to_nurbs(const GSpan src, GMutableSpan dst)
56{
58 using T = decltype(dummy);
59 bezier_generic_to_nurbs(src.typed<T>(), dst.typed<T>());
60 });
61}
62
63static void bezier_positions_to_nurbs(const Span<float3> src_positions,
64 const Span<float3> src_handles_l,
65 const Span<float3> src_handles_r,
66 MutableSpan<float3> dst_positions)
67{
68 for (const int i : src_positions.index_range()) {
69 dst_positions[i * 3] = src_handles_l[i];
70 dst_positions[i * 3 + 1] = src_positions[i];
71 dst_positions[i * 3 + 2] = src_handles_r[i];
72 }
73}
74
75static void catmull_rom_to_bezier_handles(const Span<float3> src_positions,
76 const bool cyclic,
77 MutableSpan<float3> dst_handles_l,
78 MutableSpan<float3> dst_handles_r)
79{
80 /* Catmull Rom curves are the same as Bezier curves with automatically defined handle positions.
81 * This constant defines the portion of the distance between the next/previous points to use for
82 * the length of the handles. */
83 constexpr float handle_scale = 1.0f / 6.0f;
84
85 if (src_positions.size() == 1) {
86 dst_handles_l.first() = src_positions.first();
87 dst_handles_r.first() = src_positions.first();
88 return;
89 }
90
91 const float3 first_offset = cyclic ? src_positions[1] - src_positions.last() :
92 src_positions[1] - src_positions[0];
93 dst_handles_r.first() = src_positions.first() + first_offset * handle_scale;
94 dst_handles_l.first() = src_positions.first() - first_offset * handle_scale;
95
96 const float3 last_offset = cyclic ? src_positions.first() - src_positions.last(1) :
97 src_positions.last() - src_positions.last(1);
98 dst_handles_l.last() = src_positions.last() - last_offset * handle_scale;
99 dst_handles_r.last() = src_positions.last() + last_offset * handle_scale;
100
101 for (const int i : src_positions.index_range().drop_front(1).drop_back(1)) {
102 const float3 left_offset = src_positions[i - 1] - src_positions[i + 1];
103 dst_handles_l[i] = src_positions[i] + left_offset * handle_scale;
104
105 const float3 right_offset = src_positions[i + 1] - src_positions[i - 1];
106 dst_handles_r[i] = src_positions[i] + right_offset * handle_scale;
107 }
108}
109
110static void catmull_rom_to_nurbs_positions(const Span<float3> src_positions,
111 const bool cyclic,
112 MutableSpan<float3> dst_positions)
113{
114 /* Convert the Catmull Rom position data to Bezier handles in order to reuse the Bezier to
115 * NURBS positions assignment. If this becomes a bottleneck, this step could be avoided. */
116 Array<float3, 32> bezier_handles_l(src_positions.size());
117 Array<float3, 32> bezier_handles_r(src_positions.size());
118 catmull_rom_to_bezier_handles(src_positions, cyclic, bezier_handles_l, bezier_handles_r);
119 bezier_positions_to_nurbs(src_positions, bezier_handles_l, bezier_handles_r, dst_positions);
120}
121
122template<typename T>
123static void nurbs_to_bezier_assign(const Span<T> src,
124 const MutableSpan<T> dst,
125 const KnotsMode knots_mode)
126{
127 switch (knots_mode) {
129 for (const int i : dst.index_range()) {
130 dst[i] = src[(i + 1) % src.size()];
131 }
132 break;
134 for (const int i : dst.index_range().drop_back(1).drop_front(1)) {
135 dst[i] = src[i + 1];
136 }
137 dst.first() = src.first();
138 dst.last() = src.last();
139 break;
140 default:
141 /* Every 3rd NURBS position (starting from index 1) should have its attributes transferred.
142 */
143 scale_input_assign<T>(src, 3, 1, dst);
144 }
145}
146
147static void nurbs_to_bezier_assign(const GSpan src, const KnotsMode knots_mode, GMutableSpan dst)
148{
150 using T = decltype(dummy);
151 nurbs_to_bezier_assign(src.typed<T>(), dst.typed<T>(), knots_mode);
152 });
153}
154
156 const KnotsMode knots_mode)
157{
158 const int nurbs_positions_num = nurbs_positions.size();
159 Vector<float3> handle_positions;
160
161 if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
162 const bool is_periodic = knots_mode == NURBS_KNOT_MODE_NORMAL;
163 if (is_periodic) {
164 handle_positions.append(nurbs_positions[1] +
165 ((nurbs_positions[0] - nurbs_positions[1]) / 3));
166 }
167 else {
168 handle_positions.append(2 * nurbs_positions[0] - nurbs_positions[1]);
169 handle_positions.append(nurbs_positions[1]);
170 }
171
172 /* Place Bezier handles on interior NURBS hull segments. Those handles can be either placed on
173 * endpoints, midpoints or 1/3 of the distance of a hull segment. */
174 const int segments_num = nurbs_positions_num - 1;
175 const bool ignore_interior_segment = segments_num == 3 && is_periodic == false;
176 if (ignore_interior_segment == false) {
177 const float mid_offset = float(segments_num - 1) / 2.0f;
178 for (const int i : IndexRange(1, segments_num - 2)) {
179 /* Divisor can have values: 1, 2 or 3. */
180 const int divisor = is_periodic ?
181 3 :
182 std::min(3, int(-std::abs(i - mid_offset) + mid_offset + 1.0f));
183 const float3 &p1 = nurbs_positions[i];
184 const float3 &p2 = nurbs_positions[i + 1];
185 const float3 displacement = (p2 - p1) / divisor;
186 const int num_handles_on_segment = divisor < 3 ? 1 : 2;
187 for (int j : IndexRange(1, num_handles_on_segment)) {
188 handle_positions.append(p1 + (displacement * j));
189 }
190 }
191 }
192
193 const int last_index = nurbs_positions_num - 1;
194 if (is_periodic) {
195 handle_positions.append(
196 nurbs_positions[last_index - 1] +
197 ((nurbs_positions[last_index] - nurbs_positions[last_index - 1]) / 3));
198 }
199 else {
200 handle_positions.append(nurbs_positions[last_index - 1]);
201 handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
202 }
203 }
204 else {
205 for (const int i : IndexRange(nurbs_positions_num)) {
206 if (i % 3 == 1) {
207 continue;
208 }
209 handle_positions.append(nurbs_positions[i]);
210 }
211 if (nurbs_positions_num % 3 == 1) {
212 handle_positions.pop_last();
213 }
214 else if (nurbs_positions_num % 3 == 2) {
215 const int last_index = nurbs_positions_num - 1;
216 handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
217 }
218 }
219
220 return handle_positions;
221}
222
223static void create_nurbs_to_bezier_positions(const Span<float3> nurbs_positions,
224 const Span<float3> handle_positions,
225 const KnotsMode knots_mode,
226 MutableSpan<float3> bezier_positions)
227{
228 if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
229 for (const int i : bezier_positions.index_range()) {
230 bezier_positions[i] = math::interpolate(
231 handle_positions[i * 2], handle_positions[i * 2 + 1], 0.5f);
232 }
233 }
234 else {
235 /* Every 3rd NURBS position (starting from index 1) should be converted to Bezier position. */
236 scale_input_assign(nurbs_positions, 3, 1, bezier_positions);
237 }
238}
239
240static int to_bezier_size(const CurveType src_type,
241 const bool cyclic,
242 const KnotsMode knots_mode,
243 const int src_size)
244{
245 switch (src_type) {
246 case CURVE_TYPE_NURBS: {
247 if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
248 return cyclic ? src_size : std::max(1, src_size - 2);
249 }
250 return (src_size + 1) / 3;
251 }
252 default:
253 return src_size;
254 }
255}
256
257static int to_nurbs_size(const CurveType src_type, const int src_size)
258{
259 switch (src_type) {
262 return src_size * 3;
263 default:
264 return src_size;
265 }
266}
267
269 const IndexMask &selection,
270 const bke::AttributeFilter &attribute_filter)
271{
272 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
273 const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes();
274 const VArray<int8_t> src_types = src_curves.curve_types();
275 const VArray<bool> src_cyclic = src_curves.cyclic();
276 const Span<float3> src_positions = src_curves.positions();
277 const bke::AttributeAccessor src_attributes = src_curves.attributes();
278 IndexMaskMemory memory;
279 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
280
282 dst_curves.fill_curve_types(selection, CURVE_TYPE_BEZIER);
283
284 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
285 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
286 selection.foreach_index(GrainSize(1024), [&](const int i) {
287 dst_offsets[i] = to_bezier_size(CurveType(src_types[i]),
288 src_cyclic[i],
289 KnotsMode(src_knot_modes[i]),
290 src_points_by_curve[i].size());
291 });
293 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
294 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
295
296 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
297 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
298 MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
299 MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write();
300 MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write();
301 MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
302 Set<std::string> attributes_to_skip = {
303 "position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"};
304 if (!dst_curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
305 attributes_to_skip.add_new("nurbs_weight");
306 }
308 src_attributes,
309 dst_attributes,
311 bke::attribute_filter_with_skip_ref(attribute_filter, attributes_to_skip));
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) {
332 src_points_by_curve, dst_points_by_curve, selection, attribute.src, attribute.dst.span);
333 }
334 };
335
336 auto poly_to_bezier = [&](const IndexMask &selection) {
338 src_points_by_curve, dst_points_by_curve, selection, src_positions, dst_positions);
340 dst_points_by_curve, selection, BEZIER_HANDLE_VECTOR, dst_types_l);
342 dst_points_by_curve, selection, BEZIER_HANDLE_VECTOR, dst_types_r);
344 for (bke::AttributeTransferData &attribute : generic_attributes) {
346 src_points_by_curve, dst_points_by_curve, selection, attribute.src, attribute.dst.span);
347 }
348 };
349
350 auto bezier_to_bezier = [&](const IndexMask &selection) {
351 const VArraySpan<int8_t> src_types_l = src_curves.handle_types_left();
352 const VArraySpan<int8_t> src_types_r = src_curves.handle_types_right();
353 const Span<float3> src_handles_l = src_curves.handle_positions_left();
354 const Span<float3> src_handles_r = src_curves.handle_positions_right();
355
357 src_points_by_curve, dst_points_by_curve, selection, src_positions, dst_positions);
359 src_points_by_curve, dst_points_by_curve, selection, src_handles_l, dst_handles_l);
361 src_points_by_curve, dst_points_by_curve, selection, src_handles_r, dst_handles_r);
363 src_points_by_curve, dst_points_by_curve, selection, src_types_l, dst_types_l);
365 src_points_by_curve, dst_points_by_curve, selection, src_types_r, dst_types_r);
366
368
369 for (bke::AttributeTransferData &attribute : generic_attributes) {
371 src_points_by_curve, dst_points_by_curve, selection, attribute.src, attribute.dst.span);
372 }
373 };
374
375 auto nurbs_to_bezier = [&](const IndexMask &selection) {
377 dst_points_by_curve, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
379 dst_points_by_curve, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
380
381 selection.foreach_index(GrainSize(64), [&](const int i) {
382 const IndexRange src_points = src_points_by_curve[i];
383 const IndexRange dst_points = dst_points_by_curve[i];
384 const Span<float3> src_curve_positions = src_positions.slice(src_points);
385 if (dst_points.size() == 1) {
386 const float3 &position = src_positions[src_points.first()];
387 dst_positions[dst_points.first()] = position;
388 dst_handles_l[dst_points.first()] = position;
389 dst_handles_r[dst_points.first()] = position;
390 return;
391 }
392
393 KnotsMode knots_mode = KnotsMode(src_knot_modes[i]);
394 Span<float3> nurbs_positions = src_curve_positions;
395 Vector<float3> nurbs_positions_vector;
396 if (src_cyclic[i] && is_nurbs_to_bezier_one_to_one(knots_mode)) {
397 /* For conversion treat this as periodic closed curve. Extend NURBS hull to first and
398 * second point which will act as a skeleton for placing Bezier handles. */
399 nurbs_positions_vector.extend(src_curve_positions);
400 nurbs_positions_vector.append(src_curve_positions[0]);
401 nurbs_positions_vector.append(src_curve_positions[1]);
402 nurbs_positions = nurbs_positions_vector;
403 knots_mode = NURBS_KNOT_MODE_NORMAL;
404 }
405
406 const Vector<float3> handle_positions = create_nurbs_to_bezier_handles(nurbs_positions,
407 knots_mode);
408
409 scale_input_assign(handle_positions.as_span(), 2, 0, dst_handles_l.slice(dst_points));
410 scale_input_assign(handle_positions.as_span(), 2, 1, dst_handles_r.slice(dst_points));
411
413 nurbs_positions, handle_positions, knots_mode, dst_positions.slice(dst_points));
414 });
415
416 for (bke::AttributeTransferData &attribute : generic_attributes) {
417 selection.foreach_index(GrainSize(512), [&](const int i) {
418 const IndexRange src_points = src_points_by_curve[i];
419 const IndexRange dst_points = dst_points_by_curve[i];
420 nurbs_to_bezier_assign(attribute.src.slice(src_points),
421 KnotsMode(src_knot_modes[i]),
422 attribute.dst.span.slice(dst_points));
423 });
424 }
425 };
426
428 src_curves.curve_type_counts(),
429 selection,
430 catmull_rom_to_bezier,
431 poly_to_bezier,
432 bezier_to_bezier,
433 nurbs_to_bezier);
434
435 for (bke::AttributeTransferData &attribute : generic_attributes) {
436 attribute.dst.finish();
437 }
438
442 attribute_filter,
443 src_points_by_curve,
444 dst_points_by_curve,
445 unselected,
446 dst_attributes);
447
448 return dst_curves;
449}
450
452 const IndexMask &selection,
453 const bke::AttributeFilter &attribute_filter)
454{
455 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
456 const VArray<int8_t> src_types = src_curves.curve_types();
457 const VArray<bool> src_cyclic = src_curves.cyclic();
458 const Span<float3> src_positions = src_curves.positions();
459 const bke::AttributeAccessor src_attributes = src_curves.attributes();
460 IndexMaskMemory memory;
461 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
462
464 dst_curves.fill_curve_types(selection, CURVE_TYPE_NURBS);
465
466 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
467 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
468 selection.foreach_index(GrainSize(1024), [&](const int i) {
469 dst_offsets[i] = to_nurbs_size(CurveType(src_types[i]), src_points_by_curve[i].size());
470 });
472 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
473 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
474
475 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
476 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
478 src_attributes,
479 dst_attributes,
482 {"position",
483 "handle_type_left",
484 "handle_type_right",
485 "handle_right",
486 "handle_left",
487 "nurbs_weight"}));
488
489 auto fill_weights_if_necessary = [&](const IndexMask &selection) {
490 if (src_attributes.contains("nurbs_weight")) {
492 dst_points_by_curve, selection, 1.0f, dst_curves.nurbs_weights_for_write());
493 }
494 };
495
496 auto catmull_rom_to_nurbs = [&](const IndexMask &selection) {
499 dst_curves.nurbs_knots_modes_for_write(), NURBS_KNOT_MODE_BEZIER, selection);
500 fill_weights_if_necessary(selection);
501
502 selection.foreach_segment(GrainSize(512), [&](const IndexMaskSegment segment) {
503 for (const int i : segment) {
504 const IndexRange src_points = src_points_by_curve[i];
505 const IndexRange dst_points = dst_points_by_curve[i];
507 src_positions.slice(src_points), src_cyclic[i], dst_positions.slice(dst_points));
508 }
509 });
510
511 for (bke::AttributeTransferData &attribute : generic_attributes) {
512 selection.foreach_index(GrainSize(512), [&](const int i) {
513 const IndexRange src_points = src_points_by_curve[i];
514 const IndexRange dst_points = dst_points_by_curve[i];
515 bezier_generic_to_nurbs(attribute.src.slice(src_points),
516 attribute.dst.span.slice(dst_points));
517 });
518 }
519 };
520
521 auto poly_to_nurbs = [&](const IndexMask &selection) {
524 src_points_by_curve, dst_points_by_curve, selection, src_positions, dst_positions);
525 fill_weights_if_necessary(selection);
526
527 /* Avoid using "Endpoint" knots modes for cyclic curves, since it adds a sharp point at the
528 * start/end. */
529 if (src_cyclic.is_single()) {
533 selection);
534 }
535 else {
536 VArraySpan<bool> cyclic{src_cyclic};
537 MutableSpan<int8_t> knots_modes = dst_curves.nurbs_knots_modes_for_write();
538 selection.foreach_index(GrainSize(1024), [&](const int i) {
539 knots_modes[i] = cyclic[i] ? NURBS_KNOT_MODE_NORMAL : NURBS_KNOT_MODE_ENDPOINT;
540 });
541 }
542
543 for (bke::AttributeTransferData &attribute : generic_attributes) {
545 src_points_by_curve, dst_points_by_curve, selection, attribute.src, attribute.dst.span);
546 }
547 };
548
549 auto bezier_to_nurbs = [&](const IndexMask &selection) {
550 const Span<float3> src_handles_l = src_curves.handle_positions_left();
551 const Span<float3> src_handles_r = src_curves.handle_positions_right();
552
555 dst_curves.nurbs_knots_modes_for_write(), NURBS_KNOT_MODE_BEZIER, selection);
556 fill_weights_if_necessary(selection);
557
558 selection.foreach_index(GrainSize(512), [&](const int i) {
559 const IndexRange src_points = src_points_by_curve[i];
560 const IndexRange dst_points = dst_points_by_curve[i];
561 bezier_positions_to_nurbs(src_positions.slice(src_points),
562 src_handles_l.slice(src_points),
563 src_handles_r.slice(src_points),
564 dst_positions.slice(dst_points));
565 });
566
567 for (bke::AttributeTransferData &attribute : generic_attributes) {
568 selection.foreach_index(GrainSize(512), [&](const int i) {
569 const IndexRange src_points = src_points_by_curve[i];
570 const IndexRange dst_points = dst_points_by_curve[i];
571 bezier_generic_to_nurbs(attribute.src.slice(src_points),
572 attribute.dst.span.slice(dst_points));
573 });
574 }
575 };
576
577 auto nurbs_to_nurbs = [&](const IndexMask &selection) {
579 src_points_by_curve, dst_points_by_curve, selection, src_positions, dst_positions);
580
581 if (!src_curves.nurbs_weights().is_empty()) {
582 array_utils::copy_group_to_group(src_points_by_curve,
583 dst_points_by_curve,
584 selection,
585 src_curves.nurbs_weights(),
586 dst_curves.nurbs_weights_for_write());
587 }
588
589 for (bke::AttributeTransferData &attribute : generic_attributes) {
591 src_points_by_curve, dst_points_by_curve, selection, attribute.src, attribute.dst.span);
592 }
593 };
594
596 src_curves.curve_type_counts(),
597 selection,
598 catmull_rom_to_nurbs,
599 poly_to_nurbs,
600 bezier_to_nurbs,
601 nurbs_to_nurbs);
602
603 for (bke::AttributeTransferData &attribute : generic_attributes) {
604 attribute.dst.finish();
605 }
606
610 attribute_filter,
611 src_points_by_curve,
612 dst_points_by_curve,
613 unselected,
614 dst_attributes);
615
616 return dst_curves;
617}
618
620 const IndexMask &selection,
621 const CurveType dst_type)
622{
623 bke::CurvesGeometry dst_curves(src_curves);
624 dst_curves.fill_curve_types(selection, dst_type);
626 return dst_curves;
627}
628
630 const bke::CurvesGeometry &src_curves,
631 const IndexMask &selection,
632 const CurveType dst_type,
633 const bke::AttributeFilter &attribute_filter,
635{
636 const bool use_bezier_handles = (dst_type == CURVE_TYPE_CATMULL_ROM) ?
637 options.convert_bezier_handles_to_catmull_rom_points :
638 options.convert_bezier_handles_to_poly_points;
639 if (!use_bezier_handles || !src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
640 return convert_curves_trivial(src_curves, selection, dst_type);
641 }
642
643 const OffsetIndices src_points_by_curve = src_curves.points_by_curve();
644 const VArray<int8_t> src_types = src_curves.curve_types();
645 const VArray<bool> src_cyclic = src_curves.cyclic();
646 const Span<float3> src_positions = src_curves.positions();
647 const bke::AttributeAccessor src_attributes = src_curves.attributes();
648 IndexMaskMemory memory;
649 const IndexMask unselected = selection.complement(src_curves.curves_range(), memory);
650
652 dst_curves.fill_curve_types(selection, dst_type);
653
654 MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
655 offset_indices::copy_group_sizes(src_points_by_curve, unselected, dst_offsets);
656 selection.foreach_index(GrainSize(1024), [&](const int i) {
657 const IndexRange src_points = src_points_by_curve[i];
658 const CurveType src_curve_type = CurveType(src_types[i]);
659 int &size = dst_offsets[i];
660 if (src_curve_type == CURVE_TYPE_BEZIER) {
661 size = src_points.size() * 3;
662 }
663 else {
664 size = src_points.size();
665 }
666 });
668 dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
669 const OffsetIndices dst_points_by_curve = dst_curves.points_by_curve();
670
671 MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
672 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
674 src_attributes,
675 dst_attributes,
678 {"position",
679 "handle_type_left",
680 "handle_type_right",
681 "handle_right",
682 "handle_left",
683 "nurbs_weight"}));
684
685 auto convert_from_catmull_rom_or_poly_or_nurbs = [&](const IndexMask &selection) {
687 src_points_by_curve, dst_points_by_curve, selection, src_positions, dst_positions);
688 for (bke::AttributeTransferData &attribute : generic_attributes) {
690 src_points_by_curve, dst_points_by_curve, selection, attribute.src, attribute.dst.span);
691 }
692 };
693
694 auto convert_from_bezier = [&](const IndexMask &selection) {
695 const Span<float3> src_left_handles = src_curves.handle_positions_left();
696 const Span<float3> src_right_handles = src_curves.handle_positions_right();
697
698 /* Transfer positions. */
699 selection.foreach_index([&](const int curve_i) {
700 const IndexRange src_points = src_points_by_curve[curve_i];
701 const IndexRange dst_points = dst_points_by_curve[curve_i];
702 for (const int i : src_points.index_range()) {
703 const int src_point_i = src_points[i];
704 const int dst_points_start = dst_points.start() + 3 * i;
705 dst_positions[dst_points_start + 0] = src_left_handles[src_point_i];
706 dst_positions[dst_points_start + 1] = src_positions[src_point_i];
707 dst_positions[dst_points_start + 2] = src_right_handles[src_point_i];
708 }
709 });
710 /* Transfer attributes. The handles the same attribute values as their corresponding control
711 * point. */
712 for (bke::AttributeTransferData &attribute : generic_attributes) {
713 const CPPType &cpp_type = attribute.src.type();
714 selection.foreach_index([&](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 for (const int i : src_points.index_range()) {
718 const int src_point_i = src_points[i];
719 const int dst_points_start = dst_points.start() + 3 * i;
720 const void *src_value = attribute.src[src_point_i];
721 cpp_type.fill_assign_n(src_value, attribute.dst.span[dst_points_start], 3);
722 }
723 });
724 }
725 };
726
728 src_curves.curve_type_counts(),
729 selection,
730 convert_from_catmull_rom_or_poly_or_nurbs,
731 convert_from_catmull_rom_or_poly_or_nurbs,
732 convert_from_bezier,
733 convert_from_catmull_rom_or_poly_or_nurbs);
734
735 for (bke::AttributeTransferData &attribute : generic_attributes) {
736 attribute.dst.finish();
737 }
738
742 attribute_filter,
743 src_points_by_curve,
744 dst_points_by_curve,
745 unselected,
746 dst_attributes);
747
748 return dst_curves;
749}
750
756 const bke::CurvesGeometry &src_curves,
757 const IndexMask &selection,
759{
760 const VArray<int8_t> src_curve_types = src_curves.curve_types();
761 IndexMaskMemory memory;
763 selection, GrainSize(4096), memory, [&](const int curve_i) {
764 const CurveType type = CurveType(src_curve_types[curve_i]);
765 if (!options.keep_bezier_shape_as_nurbs && type == CURVE_TYPE_BEZIER) {
766 return true;
767 }
768 if (!options.keep_catmull_rom_shape_as_nurbs && type == CURVE_TYPE_CATMULL_ROM) {
769 return true;
770 }
771 return false;
772 });
773 return convert_curves_trivial(src_curves, mask, CURVE_TYPE_POLY);
774}
775
777 const IndexMask &selection,
778 const CurveType dst_type,
779 const bke::AttributeFilter &attribute_filter,
781{
782 switch (dst_type) {
784 case CURVE_TYPE_POLY:
786 src_curves, selection, dst_type, attribute_filter, options);
788 return convert_curves_to_bezier(src_curves, selection, attribute_filter);
789 case CURVE_TYPE_NURBS: {
790 if (!options.keep_bezier_shape_as_nurbs || !options.keep_catmull_rom_shape_as_nurbs) {
791 const bke::CurvesGeometry tmp_src_curves =
793 src_curves, selection, options);
794 return convert_curves_to_nurbs(tmp_src_curves, selection, attribute_filter);
795 }
796 return convert_curves_to_nurbs(src_curves, selection, attribute_filter);
797 }
798 }
800 return {};
801}
802
803} // namespace blender::geometry
@ ATTR_DOMAIN_MASK_POINT
Low-level operations for curves.
Low-level operations for curves.
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define ELEM(...)
CurveType
@ CURVE_TYPE_BEZIER
@ CURVE_TYPE_NURBS
@ CURVE_TYPE_POLY
@ CURVE_TYPE_CATMULL_ROM
@ 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
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:574
constexpr T & first() const
Definition BLI_span.hh:680
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:690
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:138
constexpr const T & first() const
Definition BLI_span.hh:316
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 IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
void append(const T &value)
void extend(Span< T > array)
Span< T > as_span() const
bool contains(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
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()
MutableSpan< int8_t > nurbs_orders_for_write()
Span< float > nurbs_weights() const
Span< float3 > handle_positions_left() const
VArray< int8_t > nurbs_knots_modes() const
Span< float3 > positions() 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()
VArray< int8_t > curve_types() const
VArray< bool > cyclic() const
MutableSpan< int8_t > handle_types_left_for_write()
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
CCL_NAMESPACE_BEGIN struct Options options
draw_view in_light_buf[] float
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)
bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves)
void fill_points(OffsetIndices< int > points_by_curve, const IndexMask &curve_selection, GPointer value, GMutableSpan dst)
void foreach_curve_by_type(const VArray< int8_t > &types, const std::array< int, CURVE_TYPES_NUM > &type_counts, const IndexMask &selection, FunctionRef< void(IndexMask)> catmull_rom_fn, FunctionRef< void(IndexMask)> poly_fn, FunctionRef< void(IndexMask)> bezier_fn, FunctionRef< void(IndexMask)> nurbs_fn)
Vector< AttributeTransferData > retrieve_attributes_for_transfer(const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, AttrDomainMask domain_mask, const AttributeFilter &attribute_filter={})
auto attribute_filter_with_skip_ref(AttributeFilter filter, const Span< StringRef > skip)
void copy_attributes_group_to_group(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
static 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)
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[]