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
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
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) {
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
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
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr bool is_empty() const
Definition BLI_span.hh:261
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: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
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()
CCL_NAMESPACE_BEGIN struct Options options
draw_view in_light_buf[] float
ccl_device_inline float4 mask(const int4 mask, const float4 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)
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)
VecBase< float, 3 > float3
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[]