Blender V4.3
curves_geometry.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
8
9#include <mutex>
10#include <utility>
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_array_utils.hh"
15#include "BLI_bounds.hh"
16#include "BLI_index_mask.hh"
18#include "BLI_math_matrix.hh"
20#include "BLI_memory_counter.hh"
22#include "BLI_task.hh"
23
24#include "BLO_read_write.hh"
25
26#include "DNA_curves_types.h"
27
28#include "BKE_attribute.hh"
29#include "BKE_attribute_math.hh"
31#include "BKE_curves.hh"
32#include "BKE_curves_utils.hh"
33#include "BKE_customdata.hh"
34#include "BKE_deform.hh"
35
36namespace blender::bke {
37
38static const std::string ATTR_POSITION = "position";
39static const std::string ATTR_RADIUS = "radius";
40static const std::string ATTR_TILT = "tilt";
41static const std::string ATTR_CURVE_TYPE = "curve_type";
42static const std::string ATTR_CYCLIC = "cyclic";
43static const std::string ATTR_RESOLUTION = "resolution";
44static const std::string ATTR_NORMAL_MODE = "normal_mode";
45static const std::string ATTR_HANDLE_TYPE_LEFT = "handle_type_left";
46static const std::string ATTR_HANDLE_TYPE_RIGHT = "handle_type_right";
47static const std::string ATTR_HANDLE_POSITION_LEFT = "handle_left";
48static const std::string ATTR_HANDLE_POSITION_RIGHT = "handle_right";
49static const std::string ATTR_NURBS_ORDER = "nurbs_order";
50static const std::string ATTR_NURBS_WEIGHT = "nurbs_weight";
51static const std::string ATTR_NURBS_KNOTS_MODE = "knots_mode";
52static const std::string ATTR_SURFACE_UV_COORDINATE = "surface_uv_coordinate";
53
54/* -------------------------------------------------------------------- */
57
58CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0) {}
59
60CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
61{
62 this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
63
64 this->point_num = point_num;
65 this->curve_num = curve_num;
69
72
73 if (curve_num > 0) {
74 this->curve_offsets = static_cast<int *>(
75 MEM_malloc_arrayN(this->curve_num + 1, sizeof(int), __func__));
76 this->runtime->curve_offsets_sharing_info = implicit_sharing::info_for_mem_free(
77 this->curve_offsets);
78#ifndef NDEBUG
79 this->offsets_for_write().fill(-1);
80#endif
81 /* Set common values for convenience. */
82 this->curve_offsets[0] = 0;
83 this->curve_offsets[this->curve_num] = this->point_num;
84 }
85 else {
86 this->curve_offsets = nullptr;
87 }
88
89 /* Fill the type counts with the default so they're in a valid state. */
90 this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_num;
91}
92
93CurvesGeometry::CurvesGeometry(const CurvesGeometry &other)
94{
95 this->curve_offsets = other.curve_offsets;
96 if (other.runtime->curve_offsets_sharing_info) {
97 other.runtime->curve_offsets_sharing_info->add_user();
98 }
99
100 CustomData_init_from(&other.point_data, &this->point_data, CD_MASK_ALL, other.point_num);
101 CustomData_init_from(&other.curve_data, &this->curve_data, CD_MASK_ALL, other.curve_num);
102
103 this->point_num = other.point_num;
104 this->curve_num = other.curve_num;
105
108
110
111 this->runtime = MEM_new<CurvesGeometryRuntime>(
112 __func__,
113 CurvesGeometryRuntime{other.runtime->curve_offsets_sharing_info,
114 other.runtime->type_counts,
115 other.runtime->evaluated_offsets_cache,
116 other.runtime->nurbs_basis_cache,
117 other.runtime->evaluated_position_cache,
118 other.runtime->bounds_cache,
119 other.runtime->evaluated_length_cache,
120 other.runtime->evaluated_tangent_cache,
121 other.runtime->evaluated_normal_cache,
122 {},
123 true});
124
125 if (other.runtime->bake_materials) {
126 this->runtime->bake_materials = std::make_unique<bake::BakeMaterialsList>(
127 *other.runtime->bake_materials);
128 }
129}
130
131CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other)
132{
133 if (this == &other) {
134 return *this;
135 }
136 std::destroy_at(this);
137 new (this) CurvesGeometry(other);
138 return *this;
139}
140
141CurvesGeometry::CurvesGeometry(CurvesGeometry &&other)
142{
143 this->curve_offsets = other.curve_offsets;
144 other.curve_offsets = nullptr;
145
146 this->point_data = other.point_data;
147 CustomData_reset(&other.point_data);
148
149 this->curve_data = other.curve_data;
150 CustomData_reset(&other.curve_data);
151
152 this->point_num = other.point_num;
153 other.point_num = 0;
154
155 this->curve_num = other.curve_num;
156 other.curve_num = 0;
157
158 this->vertex_group_names = other.vertex_group_names;
159 BLI_listbase_clear(&other.vertex_group_names);
160
161 this->vertex_group_active_index = other.vertex_group_active_index;
162 other.vertex_group_active_index = 0;
163
164 this->attributes_active_index = other.attributes_active_index;
165 other.attributes_active_index = 0;
166
167 this->runtime = other.runtime;
168 other.runtime = nullptr;
169}
170
171CurvesGeometry &CurvesGeometry::operator=(CurvesGeometry &&other)
172{
173 if (this == &other) {
174 return *this;
175 }
176 std::destroy_at(this);
177 new (this) CurvesGeometry(std::move(other));
178 return *this;
179}
180
181CurvesGeometry::~CurvesGeometry()
182{
183 CustomData_free(&this->point_data, this->point_num);
184 CustomData_free(&this->curve_data, this->curve_num);
186 if (this->runtime) {
188 &this->runtime->curve_offsets_sharing_info);
189 MEM_delete(this->runtime);
190 }
191}
192
194
195/* -------------------------------------------------------------------- */
198
199static int domain_num(const CurvesGeometry &curves, const AttrDomain domain)
200{
201 return domain == AttrDomain::Point ? curves.points_num() : curves.curves_num();
202}
203
205{
206 return domain == AttrDomain::Point ? curves.point_data : curves.curve_data;
207}
208
210{
211 return domain == AttrDomain::Point ? curves.point_data : curves.curve_data;
212}
213
214template<typename T>
216 const AttrDomain domain,
217 const StringRef name,
218 const T default_value)
219{
220 const int num = domain_num(curves, domain);
222 const CustomData &custom_data = domain_custom_data(curves, domain);
223
224 const T *data = (const T *)CustomData_get_layer_named(&custom_data, type, name);
225 if (data != nullptr) {
226 return VArray<T>::ForSpan(Span<T>(data, num));
227 }
228 return VArray<T>::ForSingle(default_value, num);
229}
230
231template<typename T>
233 const AttrDomain domain,
234 const StringRef name)
235{
236 const int num = domain_num(curves, domain);
237 const CustomData &custom_data = domain_custom_data(curves, domain);
239
240 T *data = (T *)CustomData_get_layer_named(&custom_data, type, name);
241 if (data == nullptr) {
242 return {};
243 }
244 return {data, num};
245}
246
247template<typename T>
249 const AttrDomain domain,
250 const StringRef name,
251 const T default_value = T())
252{
253 const int num = domain_num(curves, domain);
254 if (num <= 0) {
255 return {};
256 }
258 CustomData &custom_data = domain_custom_data(curves, domain);
259
260 T *data = (T *)CustomData_get_layer_named_for_write(&custom_data, type, name, num);
261 if (data != nullptr) {
262 return {data, num};
263 }
264 data = (T *)CustomData_add_layer_named(&custom_data, type, CD_SET_DEFAULT, num, name);
265 MutableSpan<T> span = {data, num};
266 if (num > 0 && span.first() != default_value) {
267 span.fill(default_value);
268 }
269 return span;
270}
271
272VArray<int8_t> CurvesGeometry::curve_types() const
273{
276}
277
278MutableSpan<int8_t> CurvesGeometry::curve_types_for_write()
279{
281}
282
283void CurvesGeometry::fill_curve_types(const CurveType type)
284{
285 if (type == CURVE_TYPE_CATMULL_ROM) {
286 /* Avoid creating the attribute for Catmull Rom which is the default when the attribute doesn't
287 * exist anyway. */
288 this->attributes_for_write().remove("curve_type");
289 }
290 else {
291 this->curve_types_for_write().fill(type);
292 }
293 this->runtime->type_counts.fill(0);
294 this->runtime->type_counts[type] = this->curves_num();
295 this->tag_topology_changed();
296}
297
298void CurvesGeometry::fill_curve_types(const IndexMask &selection, const CurveType type)
299{
300 if (selection.size() == this->curves_num()) {
301 this->fill_curve_types(type);
302 return;
303 }
304 if (std::optional<int8_t> single_type = this->curve_types().get_if_single()) {
305 if (single_type == type) {
306 this->fill_curve_types(type);
307 return;
308 }
309 }
310 /* A potential performance optimization is only counting the changed indices. */
312 this->update_curve_types();
313 this->tag_topology_changed();
314}
315
316std::array<int, CURVE_TYPES_NUM> calculate_type_counts(const VArray<int8_t> &types)
317{
318 using CountsType = std::array<int, CURVE_TYPES_NUM>;
319 CountsType counts;
320 counts.fill(0);
321
322 if (types.is_single()) {
323 counts[types.get_internal_single()] = types.size();
324 return counts;
325 }
326
327 Span<int8_t> types_span = types.get_internal_span();
329 types.index_range(),
330 2048,
331 counts,
332 [&](const IndexRange curves_range, const CountsType &init) {
333 CountsType result = init;
334 for (const int curve_index : curves_range) {
335 result[types_span[curve_index]]++;
336 }
337 return result;
338 },
339 [](const CountsType &a, const CountsType &b) {
340 CountsType result = a;
341 for (const int i : IndexRange(CURVE_TYPES_NUM)) {
342 result[i] += b[i];
343 }
344 return result;
345 });
346}
347
348void CurvesGeometry::update_curve_types()
349{
350 this->runtime->type_counts = calculate_type_counts(this->curve_types());
351}
352
353Span<float3> CurvesGeometry::positions() const
354{
356}
357MutableSpan<float3> CurvesGeometry::positions_for_write()
358{
360}
361
362Span<int> CurvesGeometry::offsets() const
363{
364 if (this->curve_num == 0) {
365 return {};
366 }
367 return {this->curve_offsets, this->curve_num + 1};
368}
369MutableSpan<int> CurvesGeometry::offsets_for_write()
370{
371 if (this->curve_num == 0) {
372 return {};
373 }
375 &this->curve_offsets, &this->runtime->curve_offsets_sharing_info, this->curve_num + 1);
376 return {this->curve_offsets, this->curve_num + 1};
377}
378
379VArray<bool> CurvesGeometry::cyclic() const
380{
382}
383MutableSpan<bool> CurvesGeometry::cyclic_for_write()
384{
386}
387
388VArray<int> CurvesGeometry::resolution() const
389{
391}
392MutableSpan<int> CurvesGeometry::resolution_for_write()
393{
395}
396
397VArray<int8_t> CurvesGeometry::normal_mode() const
398{
400}
401MutableSpan<int8_t> CurvesGeometry::normal_mode_for_write()
402{
404}
405
406VArray<float> CurvesGeometry::tilt() const
407{
409}
410MutableSpan<float> CurvesGeometry::tilt_for_write()
411{
413}
414
415VArray<int8_t> CurvesGeometry::handle_types_left() const
416{
418}
419MutableSpan<int8_t> CurvesGeometry::handle_types_left_for_write()
420{
422}
423
424VArray<int8_t> CurvesGeometry::handle_types_right() const
425{
427}
428MutableSpan<int8_t> CurvesGeometry::handle_types_right_for_write()
429{
431}
432
433Span<float3> CurvesGeometry::handle_positions_left() const
434{
436}
437MutableSpan<float3> CurvesGeometry::handle_positions_left_for_write()
438{
440}
441
442Span<float3> CurvesGeometry::handle_positions_right() const
443{
445}
446MutableSpan<float3> CurvesGeometry::handle_positions_right_for_write()
447{
449}
450
451VArray<int8_t> CurvesGeometry::nurbs_orders() const
452{
454}
455MutableSpan<int8_t> CurvesGeometry::nurbs_orders_for_write()
456{
458}
459
460Span<float> CurvesGeometry::nurbs_weights() const
461{
463}
464MutableSpan<float> CurvesGeometry::nurbs_weights_for_write()
465{
467}
468
469VArray<int8_t> CurvesGeometry::nurbs_knots_modes() const
470{
472}
473MutableSpan<int8_t> CurvesGeometry::nurbs_knots_modes_for_write()
474{
476}
477
478Span<float2> CurvesGeometry::surface_uv_coords() const
479{
481}
482
483MutableSpan<float2> CurvesGeometry::surface_uv_coords_for_write()
484{
486}
487
488Span<MDeformVert> CurvesGeometry::deform_verts() const
489{
490 const MDeformVert *dverts = static_cast<const MDeformVert *>(
492 if (dverts == nullptr) {
493 return {};
494 }
495 return {dverts, this->point_num};
496}
497
498MutableSpan<MDeformVert> CurvesGeometry::deform_verts_for_write()
499{
500 MDeformVert *dvert = static_cast<MDeformVert *>(
502 if (dvert != nullptr) {
503 return {dvert, this->point_num};
504 }
505 return {static_cast<MDeformVert *>(CustomData_add_layer(
507 this->point_num};
508}
509
511
512/* -------------------------------------------------------------------- */
515
516template<typename CountFn> void build_offsets(MutableSpan<int> offsets, const CountFn &count_fn)
517{
518 int offset = 0;
519 for (const int i : offsets.drop_back(1).index_range()) {
520 offsets[i] = offset;
521 offset += count_fn(i);
522 }
523 offsets.last() = offset;
524}
525
527 MutableSpan<int> offsets,
528 MutableSpan<int> all_bezier_offsets)
529{
530 const OffsetIndices points_by_curve = curves.points_by_curve();
531 const VArray<int8_t> types = curves.curve_types();
532 const VArray<int> resolution = curves.resolution();
533 const VArray<bool> cyclic = curves.cyclic();
534
535 VArraySpan<int8_t> handle_types_left;
536 VArraySpan<int8_t> handle_types_right;
537 if (curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
538 handle_types_left = curves.handle_types_left();
539 handle_types_right = curves.handle_types_right();
540 }
541
542 const VArray<int8_t> nurbs_orders = curves.nurbs_orders();
543 const VArray<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes();
544
545 build_offsets(offsets, [&](const int curve_index) -> int {
546 const IndexRange points = points_by_curve[curve_index];
547 switch (types[curve_index]) {
550 points.size(), cyclic[curve_index], resolution[curve_index]);
551 case CURVE_TYPE_POLY:
552 return points.size();
553 case CURVE_TYPE_BEZIER: {
554 const IndexRange offsets = curves::per_curve_point_offsets_range(points, curve_index);
555 curves::bezier::calculate_evaluated_offsets(handle_types_left.slice(points),
556 handle_types_right.slice(points),
557 cyclic[curve_index],
558 resolution[curve_index],
559 all_bezier_offsets.slice(offsets));
560 return all_bezier_offsets[offsets.last()];
561 }
562 case CURVE_TYPE_NURBS:
564 nurbs_orders[curve_index],
565 cyclic[curve_index],
566 resolution[curve_index],
567 KnotsMode(nurbs_knots_modes[curve_index]));
568 }
570 return 0;
571 });
572}
573
574OffsetIndices<int> CurvesGeometry::evaluated_points_by_curve() const
575{
576 const CurvesGeometryRuntime &runtime = *this->runtime;
577 if (this->is_single_type(CURVE_TYPE_POLY)) {
578 /* When all the curves are poly curves, the evaluated offsets are the same as the control
579 * point offsets, so it's possible to completely avoid building a new offsets array. */
580 runtime.evaluated_offsets_cache.ensure([&](CurvesGeometryRuntime::EvaluatedOffsets &r_data) {
583 });
584 return this->points_by_curve();
585 }
586
587 runtime.evaluated_offsets_cache.ensure([&](CurvesGeometryRuntime::EvaluatedOffsets &r_data) {
588 r_data.evaluated_offsets.resize(this->curves_num() + 1);
589
591 r_data.all_bezier_offsets.resize(this->points_num() + this->curves_num());
592 }
593 else {
595 }
596
598 });
599
600 return OffsetIndices<int>(runtime.evaluated_offsets_cache.data().evaluated_offsets);
601}
602
603IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
604 IndexMaskMemory &memory) const
605{
606 return this->indices_for_curve_type(type, this->curves_range(), memory);
607}
608
609IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
610 const IndexMask &selection,
611 IndexMaskMemory &memory) const
612{
614 this->curve_types(), this->curve_type_counts(), type, selection, memory);
615}
616
617Array<int> CurvesGeometry::point_to_curve_map() const
618{
619 Array<int> map(this->points_num());
621 return map;
622}
623
624void CurvesGeometry::ensure_nurbs_basis_cache() const
625{
626 const CurvesGeometryRuntime &runtime = *this->runtime;
627 runtime.nurbs_basis_cache.ensure([&](Vector<curves::nurbs::BasisCache> &r_data) {
628 IndexMaskMemory memory;
629 const IndexMask nurbs_mask = this->indices_for_curve_type(CURVE_TYPE_NURBS, memory);
630 if (nurbs_mask.is_empty()) {
631 r_data.clear_and_shrink();
632 return;
633 }
634
635 r_data.resize(this->curves_num());
636
637 const OffsetIndices<int> points_by_curve = this->points_by_curve();
638 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
639 const VArray<bool> cyclic = this->cyclic();
640 const VArray<int8_t> orders = this->nurbs_orders();
641 const VArray<int8_t> knots_modes = this->nurbs_knots_modes();
642
643 nurbs_mask.foreach_segment(GrainSize(64), [&](const IndexMaskSegment segment) {
644 Vector<float, 32> knots;
645 for (const int curve_index : segment) {
646 const IndexRange points = points_by_curve[curve_index];
647 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
648
649 const int8_t order = orders[curve_index];
650 const bool is_cyclic = cyclic[curve_index];
651 const KnotsMode mode = KnotsMode(knots_modes[curve_index]);
652
653 if (!curves::nurbs::check_valid_num_and_order(points.size(), order, is_cyclic, mode)) {
654 r_data[curve_index].invalid = true;
655 continue;
656 }
657
658 knots.reinitialize(curves::nurbs::knots_num(points.size(), order, is_cyclic));
659 curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots);
660 curves::nurbs::calculate_basis_cache(
661 points.size(), evaluated_points.size(), order, is_cyclic, knots, r_data[curve_index]);
662 }
663 });
664 });
665}
666
667Span<float3> CurvesGeometry::evaluated_positions() const
668{
669 const CurvesGeometryRuntime &runtime = *this->runtime;
670 if (this->is_single_type(CURVE_TYPE_POLY)) {
671 runtime.evaluated_position_cache.ensure(
672 [&](Vector<float3> &r_data) { r_data.clear_and_shrink(); });
673 return this->positions();
674 }
675 this->ensure_nurbs_basis_cache();
676 runtime.evaluated_position_cache.ensure([&](Vector<float3> &r_data) {
677 r_data.resize(this->evaluated_points_num());
679
680 const OffsetIndices<int> points_by_curve = this->points_by_curve();
681 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
682 const Span<float3> positions = this->positions();
683
684 auto evaluate_catmull = [&](const IndexMask &selection) {
685 const VArray<bool> cyclic = this->cyclic();
686 const VArray<int> resolution = this->resolution();
687 selection.foreach_index(GrainSize(128), [&](const int curve_index) {
688 const IndexRange points = points_by_curve[curve_index];
689 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
691 cyclic[curve_index],
692 resolution[curve_index],
693 evaluated_positions.slice(evaluated_points));
694 });
695 };
696 auto evaluate_poly = [&](const IndexMask &selection) {
699 };
700 auto evaluate_bezier = [&](const IndexMask &selection) {
701 const Span<float3> handle_positions_left = this->handle_positions_left();
702 const Span<float3> handle_positions_right = this->handle_positions_right();
703 if (handle_positions_left.is_empty() || handle_positions_right.is_empty()) {
705 return;
706 }
707 const Span<int> all_bezier_offsets =
708 runtime.evaluated_offsets_cache.data().all_bezier_offsets;
709 selection.foreach_index(GrainSize(128), [&](const int curve_index) {
710 const IndexRange points = points_by_curve[curve_index];
711 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
712 const IndexRange offsets = curves::per_curve_point_offsets_range(points, curve_index);
714 handle_positions_left.slice(points),
715 handle_positions_right.slice(points),
716 all_bezier_offsets.slice(offsets),
717 evaluated_positions.slice(evaluated_points));
718 });
719 };
720 auto evaluate_nurbs = [&](const IndexMask &selection) {
721 this->ensure_nurbs_basis_cache();
722 const VArray<int8_t> nurbs_orders = this->nurbs_orders();
723 const Span<float> nurbs_weights = this->nurbs_weights();
724 const Span<curves::nurbs::BasisCache> nurbs_basis_cache = runtime.nurbs_basis_cache.data();
725 selection.foreach_index(GrainSize(128), [&](const int curve_index) {
726 const IndexRange points = points_by_curve[curve_index];
727 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
728 curves::nurbs::interpolate_to_evaluated(nurbs_basis_cache[curve_index],
729 nurbs_orders[curve_index],
730 nurbs_weights.slice_safe(points),
731 positions.slice(points),
732 evaluated_positions.slice(evaluated_points));
733 });
734 };
736 this->curve_type_counts(),
737 this->curves_range(),
738 evaluate_catmull,
739 evaluate_poly,
740 evaluate_bezier,
741 evaluate_nurbs);
742 });
743 return runtime.evaluated_position_cache.data();
744}
745
746Span<float3> CurvesGeometry::evaluated_tangents() const
747{
748 const CurvesGeometryRuntime &runtime = *this->runtime;
749 runtime.evaluated_tangent_cache.ensure([&](Vector<float3> &r_data) {
750 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
751 const Span<float3> evaluated_positions = this->evaluated_positions();
752 const VArray<bool> cyclic = this->cyclic();
753
754 r_data.resize(this->evaluated_points_num());
755 MutableSpan<float3> tangents = r_data;
756
758 for (const int curve_index : curves_range) {
759 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
761 cyclic[curve_index],
762 tangents.slice(evaluated_points));
763 }
764 });
765
766 /* Correct the first and last tangents of non-cyclic Bezier curves so that they align with
767 * the inner handles. This is a separate loop to avoid the cost when Bezier type curves are
768 * not used. */
769 IndexMaskMemory memory;
770 const IndexMask bezier_mask = this->indices_for_curve_type(CURVE_TYPE_BEZIER, memory);
771 if (!bezier_mask.is_empty()) {
772 const OffsetIndices<int> points_by_curve = this->points_by_curve();
773 const Span<float3> positions = this->positions();
774 const Span<float3> handles_left = this->handle_positions_left();
775 const Span<float3> handles_right = this->handle_positions_right();
776
777 bezier_mask.foreach_index(GrainSize(1024), [&](const int curve_index) {
778 if (cyclic[curve_index]) {
779 return;
780 }
781 const IndexRange points = points_by_curve[curve_index];
782 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
783
784 const float epsilon = 1e-6f;
786 handles_right[points.first()], positions[points.first()], epsilon))
787 {
788 tangents[evaluated_points.first()] = math::normalize(handles_right[points.first()] -
789 positions[points.first()]);
790 }
792 handles_left[points.last()], positions[points.last()], epsilon))
793 {
794 tangents[evaluated_points.last()] = math::normalize(positions[points.last()] -
795 handles_left[points.last()]);
796 }
797 });
798 }
799 });
800 return runtime.evaluated_tangent_cache.data();
801}
802
804 const Span<float3> axes,
805 const Span<float> angles)
806{
807 for (const int i : directions.index_range()) {
808 const float3 axis = axes[i];
809 if (UNLIKELY(math::is_zero(axis))) {
810 continue;
811 }
812 directions[i] = math::rotate_direction_around_axis(directions[i], axis, angles[i]);
813 }
814}
815
817{
818 for (const int i : data.index_range()) {
819 data[i] = math::normalize(data[i]);
820 }
821}
822
834
835static void evaluate_generic_data_for_curve(const EvalData &eval_data,
836 const int curve_index,
837 const GSpan src,
838 GMutableSpan dst)
839{
840 const IndexRange points = eval_data.points_by_curve[curve_index];
841 switch (eval_data.types[curve_index]) {
844 src, eval_data.cyclic[curve_index], eval_data.resolution[curve_index], dst);
845 break;
846 case CURVE_TYPE_POLY:
847 dst.copy_from(src);
848 break;
849 case CURVE_TYPE_BEZIER: {
850 const IndexRange offsets = curves::per_curve_point_offsets_range(points, curve_index);
852 src, eval_data.all_bezier_evaluated_offsets.slice(offsets), dst);
853 break;
854 }
855 case CURVE_TYPE_NURBS:
857 eval_data.nurbs_orders[curve_index],
858 eval_data.nurbs_weights.slice_safe(points),
859 src,
860 dst);
861 break;
862 }
863}
864
865Span<float3> CurvesGeometry::evaluated_normals() const
866{
867 const CurvesGeometryRuntime &runtime = *this->runtime;
868 this->ensure_nurbs_basis_cache();
869 runtime.evaluated_normal_cache.ensure([&](Vector<float3> &r_data) {
870 const OffsetIndices<int> points_by_curve = this->points_by_curve();
871 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
872 const VArray<int8_t> types = this->curve_types();
873 const VArray<bool> cyclic = this->cyclic();
874 const VArray<int8_t> normal_mode = this->normal_mode();
875 const Span<float3> evaluated_tangents = this->evaluated_tangents();
876 const AttributeAccessor attributes = this->attributes();
877 const EvalData eval_data{
879 types,
880 cyclic,
881 this->resolution(),
882 runtime.evaluated_offsets_cache.data().all_bezier_offsets,
883 runtime.nurbs_basis_cache.data(),
884 this->nurbs_orders(),
885 this->nurbs_weights(),
886 };
887 const VArray<float> tilt = this->tilt();
888 VArraySpan<float> tilt_span;
889 const bool use_tilt = !(tilt.is_single() && tilt.get_internal_single() == 0.0f);
890 if (use_tilt) {
891 tilt_span = tilt;
892 }
893 VArraySpan<float3> custom_normal_span;
894 if (const VArray<float3> custom_normal = *attributes.lookup<float3>("custom_normal",
896 {
897 custom_normal_span = custom_normal;
898 }
899
900 r_data.resize(this->evaluated_points_num());
902
904 /* Reuse a buffer for the evaluated tilts. */
905 Vector<float> evaluated_tilts;
906
907 for (const int curve_index : curves_range) {
908 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
909 switch (NormalMode(normal_mode[curve_index])) {
910 case NORMAL_MODE_Z_UP:
912 evaluated_normals.slice(evaluated_points));
913 break;
916 cyclic[curve_index],
917 evaluated_normals.slice(evaluated_points));
918 break;
919 case NORMAL_MODE_FREE:
920 if (custom_normal_span.is_empty()) {
922 evaluated_normals.slice(evaluated_points));
923 }
924 else {
925 const Span<float3> src = custom_normal_span.slice(points_by_curve[curve_index]);
927 evaluated_points_by_curve[curve_index]);
928 evaluate_generic_data_for_curve(eval_data, curve_index, src, dst);
929 normalize_span(dst);
930 }
931 break;
932 }
933
934 /* If the "tilt" attribute exists, rotate the normals around the tangents by the
935 * evaluated angles. We can avoid copying the tilts to evaluate them for poly curves. */
936 if (use_tilt) {
937 const IndexRange points = points_by_curve[curve_index];
938 if (types[curve_index] == CURVE_TYPE_POLY) {
939 rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
940 evaluated_tangents.slice(evaluated_points),
941 tilt_span.slice(points));
942 }
943 else {
944 evaluated_tilts.reinitialize(evaluated_points.size());
946 curve_index,
947 tilt_span.slice(points),
948 evaluated_tilts.as_mutable_span());
949 rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
950 evaluated_tangents.slice(evaluated_points),
951 evaluated_tilts.as_span());
952 }
953 }
954 }
955 });
956 });
957 return this->runtime->evaluated_normal_cache.data();
958}
959
960void CurvesGeometry::interpolate_to_evaluated(const int curve_index,
961 const GSpan src,
962 GMutableSpan dst) const
963{
964 const CurvesGeometryRuntime &runtime = *this->runtime;
965 const EvalData eval_data{
966 this->points_by_curve(),
967 this->curve_types(),
968 this->cyclic(),
969 this->resolution(),
970 runtime.evaluated_offsets_cache.data().all_bezier_offsets,
971 runtime.nurbs_basis_cache.data(),
972 this->nurbs_orders(),
973 this->nurbs_weights(),
974 };
975 BLI_assert(src.size() == this->points_by_curve()[curve_index].size());
976 BLI_assert(dst.size() == this->evaluated_points_by_curve()[curve_index].size());
977 evaluate_generic_data_for_curve(eval_data, curve_index, src, dst);
978}
979
980void CurvesGeometry::interpolate_to_evaluated(const GSpan src, GMutableSpan dst) const
981{
982 const CurvesGeometryRuntime &runtime = *this->runtime;
983 const OffsetIndices points_by_curve = this->points_by_curve();
984 const EvalData eval_data{
986 this->curve_types(),
987 this->cyclic(),
988 this->resolution(),
989 runtime.evaluated_offsets_cache.data().all_bezier_offsets,
990 runtime.nurbs_basis_cache.data(),
991 this->nurbs_orders(),
992 this->nurbs_weights(),
993 };
994 const OffsetIndices evaluated_points_by_curve = this->evaluated_points_by_curve();
995
997 for (const int curve_index : curves_range) {
998 const IndexRange points = points_by_curve[curve_index];
999 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
1001 eval_data, curve_index, src.slice(points), dst.slice(evaluated_points));
1002 }
1003 });
1004}
1005
1006void CurvesGeometry::ensure_evaluated_lengths() const
1007{
1008 const CurvesGeometryRuntime &runtime = *this->runtime;
1009 runtime.evaluated_length_cache.ensure([&](Vector<float> &r_data) {
1010 /* Use an extra length value for the final cyclic segment for a consistent size
1011 * (see comment on #evaluated_length_cache). */
1012 const int total_num = this->evaluated_points_num() + this->curves_num();
1013 r_data.resize(total_num);
1014 MutableSpan<float> evaluated_lengths = r_data;
1015
1016 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
1017 const Span<float3> evaluated_positions = this->evaluated_positions();
1018 const VArray<bool> curves_cyclic = this->cyclic();
1019
1021 for (const int curve_index : curves_range) {
1022 const bool cyclic = curves_cyclic[curve_index];
1023 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
1024 const IndexRange lengths_range = this->lengths_range_for_curve(curve_index, cyclic);
1026 cyclic,
1027 evaluated_lengths.slice(lengths_range));
1028 }
1029 });
1030 });
1031}
1032
1033void CurvesGeometry::ensure_can_interpolate_to_evaluated() const
1034{
1035 this->evaluated_points_by_curve();
1036 this->ensure_nurbs_basis_cache();
1037}
1038
1040
1041/* -------------------------------------------------------------------- */
1044
1045void CurvesGeometry::resize(const int points_num, const int curves_num)
1046{
1047 if (points_num != this->point_num) {
1048 CustomData_realloc(&this->point_data, this->points_num(), points_num);
1049 this->point_num = points_num;
1050 }
1051 if (curves_num != this->curve_num) {
1052 CustomData_realloc(&this->curve_data, this->curves_num(), curves_num);
1054 &this->runtime->curve_offsets_sharing_info,
1055 this->curve_num == 0 ? 0 : (this->curve_num + 1),
1056 curves_num + 1);
1057 /* Set common values for convenience. */
1058 this->curve_offsets[0] = 0;
1059 this->curve_offsets[curves_num] = this->point_num;
1060 this->curve_num = curves_num;
1061 }
1062 this->tag_topology_changed();
1063}
1064
1065void CurvesGeometry::tag_positions_changed()
1066{
1067 this->runtime->evaluated_position_cache.tag_dirty();
1068 this->runtime->evaluated_tangent_cache.tag_dirty();
1069 this->runtime->evaluated_normal_cache.tag_dirty();
1070 this->runtime->evaluated_length_cache.tag_dirty();
1071 this->runtime->bounds_cache.tag_dirty();
1072}
1073void CurvesGeometry::tag_topology_changed()
1074{
1075 this->tag_positions_changed();
1076 this->runtime->evaluated_offsets_cache.tag_dirty();
1077 this->runtime->nurbs_basis_cache.tag_dirty();
1078 this->runtime->check_type_counts = true;
1079}
1080void CurvesGeometry::tag_normals_changed()
1081{
1082 this->runtime->evaluated_normal_cache.tag_dirty();
1083}
1084void CurvesGeometry::tag_radii_changed() {}
1085
1086static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
1087{
1088 threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
1089 for (float3 &position : positions.slice(range)) {
1090 position += translation;
1091 }
1092 });
1093}
1094
1095static void transform_positions(MutableSpan<float3> positions, const float4x4 &matrix)
1096{
1097 threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
1098 for (float3 &position : positions.slice(range)) {
1099 position = math::transform_point(matrix, position);
1100 }
1101 });
1102}
1103
1105{
1106 const float3x3 normal_transform = math::transpose(math::invert(float3x3(matrix)));
1107 threading::parallel_for(normals.index_range(), 1024, [&](const IndexRange range) {
1108 for (float3 &normal : normals.slice(range)) {
1109 normal = normal_transform * normal;
1110 }
1111 });
1112}
1113
1114void CurvesGeometry::calculate_bezier_auto_handles()
1115{
1117 return;
1118 }
1119 if (this->handle_positions_left().is_empty() || this->handle_positions_right().is_empty()) {
1120 return;
1121 }
1122 const OffsetIndices points_by_curve = this->points_by_curve();
1123 const VArray<int8_t> types = this->curve_types();
1124 const VArray<bool> cyclic = this->cyclic();
1125 const VArraySpan<int8_t> types_left{this->handle_types_left()};
1126 const VArraySpan<int8_t> types_right{this->handle_types_right()};
1127 const Span<float3> positions = this->positions();
1129 MutableSpan<float3> positions_right = this->handle_positions_right_for_write();
1130
1131 threading::parallel_for(this->curves_range(), 128, [&](IndexRange range) {
1132 for (const int i_curve : range) {
1133 if (types[i_curve] == CURVE_TYPE_BEZIER) {
1134 const IndexRange points = points_by_curve[i_curve];
1136 types_left.slice(points),
1137 types_right.slice(points),
1138 positions.slice(points),
1139 positions_left.slice(points),
1140 positions_right.slice(points));
1141 }
1142 }
1143 });
1144}
1145
1146void CurvesGeometry::translate(const float3 &translation)
1147{
1148 if (math::is_zero(translation)) {
1149 return;
1150 }
1151
1152 std::optional<Bounds<float3>> bounds;
1153 if (this->runtime->bounds_cache.is_cached()) {
1154 bounds = this->runtime->bounds_cache.data();
1155 }
1156
1157 translate_positions(this->positions_for_write(), translation);
1158 if (!this->handle_positions_left().is_empty()) {
1160 }
1161 if (!this->handle_positions_right().is_empty()) {
1163 }
1164 this->tag_positions_changed();
1165
1166 if (bounds) {
1167 bounds->min += translation;
1168 bounds->max += translation;
1169 this->runtime->bounds_cache.ensure([&](blender::Bounds<float3> &r_data) { r_data = *bounds; });
1170 }
1171}
1172
1173void CurvesGeometry::transform(const float4x4 &matrix)
1174{
1176 if (!this->handle_positions_left().is_empty()) {
1178 }
1179 if (!this->handle_positions_right().is_empty()) {
1181 }
1183 if (SpanAttributeWriter normals = attributes.lookup_for_write_span<float3>("custom_normal")) {
1184 transform_normals(normals.span, matrix);
1185 normals.finish();
1186 }
1187 this->tag_positions_changed();
1188}
1189
1190std::optional<Bounds<float3>> CurvesGeometry::bounds_min_max() const
1191{
1192 if (this->points_num() == 0) {
1193 return std::nullopt;
1194 }
1195 this->runtime->bounds_cache.ensure(
1196 [&](Bounds<float3> &r_bounds) { r_bounds = *bounds::min_max(this->evaluated_positions()); });
1197 return this->runtime->bounds_cache.data();
1198}
1199
1200void CurvesGeometry::count_memory(MemoryCounter &memory) const
1201{
1202 memory.add_shared(this->runtime->curve_offsets_sharing_info, this->offsets().size_in_bytes());
1203 CustomData_count_memory(this->point_data, this->point_num, memory);
1204 CustomData_count_memory(this->curve_data, this->curve_num, memory);
1205}
1206
1208 const IndexMask &points_to_copy,
1209 const AttributeFilter &attribute_filter)
1210{
1211 const Array<int> point_to_curve_map = curves.point_to_curve_map();
1212 Array<int> curve_point_counts(curves.curves_num(), 0);
1213 points_to_copy.foreach_index(
1214 [&](const int64_t point_i) { curve_point_counts[point_to_curve_map[point_i]]++; });
1215
1216 IndexMaskMemory memory;
1217 const IndexMask curves_to_copy = IndexMask::from_predicate(
1218 curves.curves_range(), GrainSize(4096), memory, [&](const int64_t i) {
1219 return curve_point_counts[i] > 0;
1220 });
1221
1222 CurvesGeometry dst_curves(points_to_copy.size(), curves_to_copy.size());
1223
1224 BKE_defgroup_copy_list(&dst_curves.vertex_group_names, &curves.vertex_group_names);
1225
1227 dst_curves.curves_num() > 1024,
1228 [&]() {
1229 if (curves_to_copy.is_empty()) {
1230 return;
1231 }
1232 MutableSpan<int> new_curve_offsets = dst_curves.offsets_for_write();
1234 curve_point_counts.as_span(), curves_to_copy, new_curve_offsets.drop_back(1));
1236 },
1237 [&]() {
1238 gather_attributes(curves.attributes(),
1239 AttrDomain::Point,
1240 AttrDomain::Point,
1241 attribute_filter,
1242 points_to_copy,
1243 dst_curves.attributes_for_write());
1244 gather_attributes(curves.attributes(),
1245 AttrDomain::Curve,
1246 AttrDomain::Curve,
1247 attribute_filter,
1248 curves_to_copy,
1249 dst_curves.attributes_for_write());
1250 });
1251
1252 if (dst_curves.curves_num() == curves.curves_num()) {
1253 dst_curves.runtime->type_counts = curves.runtime->type_counts;
1254 }
1255 else {
1256 dst_curves.remove_attributes_based_on_types();
1257 }
1258
1259 return dst_curves;
1260}
1261
1262void CurvesGeometry::remove_points(const IndexMask &points_to_delete,
1263 const AttributeFilter &attribute_filter)
1264{
1265 if (points_to_delete.is_empty()) {
1266 return;
1267 }
1268 if (points_to_delete.size() == this->points_num()) {
1269 *this = {};
1270 return;
1271 }
1272 IndexMaskMemory memory;
1273 const IndexMask points_to_copy = points_to_delete.complement(this->points_range(), memory);
1274 *this = curves_copy_point_selection(*this, points_to_copy, attribute_filter);
1275}
1276
1278 const IndexMask &curves_to_copy,
1279 const AttributeFilter &attribute_filter)
1280{
1281 const OffsetIndices points_by_curve = curves.points_by_curve();
1282 CurvesGeometry dst_curves(0, curves_to_copy.size());
1283 const OffsetIndices dst_points_by_curve = offset_indices::gather_selected_offsets(
1284 points_by_curve, curves_to_copy, dst_curves.offsets_for_write());
1285 dst_curves.resize(dst_points_by_curve.total_size(), dst_curves.curves_num());
1286
1287 BKE_defgroup_copy_list(&dst_curves.vertex_group_names, &curves.vertex_group_names);
1288
1289 const AttributeAccessor src_attributes = curves.attributes();
1290 MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
1291
1292 gather_attributes_group_to_group(src_attributes,
1295 attribute_filter,
1296 points_by_curve,
1297 dst_points_by_curve,
1298 curves_to_copy,
1299 dst_attributes);
1300
1301 gather_attributes(src_attributes,
1304 attribute_filter,
1305 curves_to_copy,
1306 dst_attributes);
1307
1308 dst_curves.update_curve_types();
1310
1311 return dst_curves;
1312}
1313
1314void CurvesGeometry::remove_curves(const IndexMask &curves_to_delete,
1315 const AttributeFilter &attribute_filter)
1316{
1317 if (curves_to_delete.is_empty()) {
1318 return;
1319 }
1320 if (curves_to_delete.size() == this->curves_num()) {
1321 *this = {};
1322 return;
1323 }
1324 IndexMaskMemory memory;
1325 const IndexMask curves_to_copy = curves_to_delete.complement(this->curves_range(), memory);
1326 *this = curves_copy_curve_selection(*this, curves_to_copy, attribute_filter);
1327}
1328
1329template<typename T>
1331 const IndexMask &curve_selection,
1333{
1334 const OffsetIndices points_by_curve = curves.points_by_curve();
1335 curve_selection.foreach_index(
1336 GrainSize(256), [&](const int curve_i) { data.slice(points_by_curve[curve_i]).reverse(); });
1337}
1338
1339template<typename T>
1341 const IndexMask &curve_selection,
1342 MutableSpan<T> data_a,
1343 MutableSpan<T> data_b)
1344{
1345 const OffsetIndices points_by_curve = curves.points_by_curve();
1346 curve_selection.foreach_index(GrainSize(256), [&](const int curve_i) {
1347 const IndexRange points = points_by_curve[curve_i];
1348 MutableSpan<T> a = data_a.slice(points);
1349 MutableSpan<T> b = data_b.slice(points);
1350 for (const int i : IndexRange(points.size() / 2)) {
1351 const int end_index = points.size() - 1 - i;
1352 std::swap(a[end_index], b[i]);
1353 std::swap(b[end_index], a[i]);
1354 }
1355 if (points.size() % 2) {
1356 const int64_t middle_index = points.size() / 2;
1357 std::swap(a[middle_index], b[middle_index]);
1358 }
1359 });
1360}
1361
1362void CurvesGeometry::reverse_curves(const IndexMask &curves_to_reverse)
1363{
1364 Set<StringRef> bezier_handle_names{{ATTR_HANDLE_POSITION_LEFT,
1368
1370
1371 attributes.foreach_attribute([&](const AttributeIter &iter) {
1372 if (iter.domain != AttrDomain::Point) {
1373 return;
1374 }
1375 if (iter.data_type == CD_PROP_STRING) {
1376 return;
1377 }
1378 if (bezier_handle_names.contains(iter.name)) {
1379 return;
1380 }
1381
1382 GSpanAttributeWriter attribute = attributes.lookup_for_write_span(iter.name);
1383 attribute_math::convert_to_static_type(attribute.span.type(), [&](auto dummy) {
1384 using T = decltype(dummy);
1385 reverse_curve_point_data<T>(*this, curves_to_reverse, attribute.span.typed<T>());
1386 });
1387 attribute.finish();
1388 return;
1389 });
1390
1391 /* In order to maintain the shape of Bezier curves, handle attributes must reverse, but also the
1392 * values for the left and right must swap. Use a utility to swap and reverse at the same time,
1393 * to avoid loading the attribute twice. Generally we can expect the right layer to exist when
1394 * the left does, but there's no need to count on it, so check for both attributes. */
1395
1396 if (attributes.contains(ATTR_HANDLE_POSITION_LEFT) &&
1398 {
1400 curves_to_reverse,
1403 }
1406 curves_to_reverse,
1409 }
1410
1411 this->tag_topology_changed();
1412}
1413
1414void CurvesGeometry::remove_attributes_based_on_types()
1415{
1422 }
1427 }
1430 }
1431}
1432
1433CurvesGeometry curves_new_no_attributes(int point_num, int curve_num)
1434{
1435 CurvesGeometry curves(0, curve_num);
1436 curves.point_num = point_num;
1437 CustomData_free_layer_named(&curves.point_data, "position", 0);
1438 return curves;
1439}
1440
1442
1443/* -------------------------------------------------------------------- */
1446
1454template<typename T>
1456 const VArray<T> &old_values,
1457 MutableSpan<T> r_values)
1458{
1459 attribute_math::DefaultMixer<T> mixer(r_values);
1460
1461 const OffsetIndices points_by_curve = curves.points_by_curve();
1462 threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) {
1463 for (const int i_curve : range) {
1464 for (const int i_point : points_by_curve[i_curve]) {
1465 mixer.mix_in(i_curve, old_values[i_point]);
1466 }
1467 }
1468 mixer.finalize(range);
1469 });
1470}
1471
1479template<>
1481 const VArray<bool> &old_values,
1482 MutableSpan<bool> r_values)
1483{
1484 const OffsetIndices points_by_curve = curves.points_by_curve();
1485 r_values.fill(true);
1486 for (const int i_curve : IndexRange(curves.curves_num())) {
1487 for (const int i_point : points_by_curve[i_curve]) {
1488 if (!old_values[i_point]) {
1489 r_values[i_curve] = false;
1490 break;
1491 }
1492 }
1493 }
1494}
1495
1497 const GVArray &varray)
1498{
1499 GVArray new_varray;
1500 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
1501 using T = decltype(dummy);
1502 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
1503 Array<T> values(curves.curves_num());
1504 adapt_curve_domain_point_to_curve_impl<T>(curves, varray.typed<T>(), values);
1505 new_varray = VArray<T>::ForContainer(std::move(values));
1506 }
1507 });
1508 return new_varray;
1509}
1510
1518template<typename T>
1520 const VArray<T> &old_values,
1521 MutableSpan<T> r_values)
1522{
1523 const OffsetIndices points_by_curve = curves.points_by_curve();
1524 for (const int i_curve : IndexRange(curves.curves_num())) {
1525 r_values.slice(points_by_curve[i_curve]).fill(old_values[i_curve]);
1526 }
1527}
1528
1530 const GVArray &varray)
1531{
1532 GVArray new_varray;
1533 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
1534 using T = decltype(dummy);
1535 Array<T> values(curves.points_num());
1536 adapt_curve_domain_curve_to_point_impl<T>(curves, varray.typed<T>(), values);
1537 new_varray = VArray<T>::ForContainer(std::move(values));
1538 });
1539 return new_varray;
1540}
1541
1542GVArray CurvesGeometry::adapt_domain(const GVArray &varray,
1543 const AttrDomain from,
1544 const AttrDomain to) const
1545{
1546 if (!varray) {
1547 return {};
1548 }
1549 if (varray.is_empty()) {
1550 return {};
1551 }
1552 if (from == to) {
1553 return varray;
1554 }
1555 if (varray.is_single()) {
1556 BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), value);
1557 varray.get_internal_single(value);
1558 return GVArray::ForSingle(varray.type(), this->attributes().domain_size(to), value);
1559 }
1560
1561 if (from == AttrDomain::Point && to == AttrDomain::Curve) {
1562 return adapt_curve_domain_point_to_curve(*this, varray);
1563 }
1564 if (from == AttrDomain::Curve && to == AttrDomain::Point) {
1565 return adapt_curve_domain_curve_to_point(*this, varray);
1566 }
1567
1569 return {};
1570}
1571
1573
1574/* -------------------------------------------------------------------- */
1577
1578void CurvesGeometry::blend_read(BlendDataReader &reader)
1579{
1580 this->runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__);
1581
1582 CustomData_blend_read(&reader, &this->point_data, this->point_num);
1583 CustomData_blend_read(&reader, &this->curve_data, this->curve_num);
1584
1585 if (this->curve_offsets) {
1587 &reader, &this->curve_offsets, [&]() {
1588 BLO_read_int32_array(&reader, this->curve_num + 1, &this->curve_offsets);
1590 });
1591 }
1592
1594
1595 /* Recalculate curve type count cache that isn't saved in files. */
1596 this->update_curve_types();
1597}
1598
1599CurvesGeometry::BlendWriteData CurvesGeometry::blend_write_prepare()
1600{
1601 CurvesGeometry::BlendWriteData write_data;
1602 CustomData_blend_write_prepare(this->point_data, write_data.point_layers);
1603 CustomData_blend_write_prepare(this->curve_data, write_data.curve_layers);
1604 return write_data;
1605}
1606
1607void CurvesGeometry::blend_write(BlendWriter &writer,
1608 ID &id,
1609 const CurvesGeometry::BlendWriteData &write_data)
1610{
1612 &writer, &this->point_data, write_data.point_layers, this->point_num, CD_MASK_ALL, &id);
1614 &writer, &this->curve_data, write_data.curve_layers, this->curve_num, CD_MASK_ALL, &id);
1615
1616 if (this->curve_offsets) {
1618 &writer,
1619 this->curve_offsets,
1620 sizeof(int) * (this->curve_num + 1),
1621 this->runtime->curve_offsets_sharing_info,
1622 [&]() { BLO_write_int32_array(&writer, this->curve_num + 1, this->curve_offsets); });
1623 }
1624
1626}
1627
1629
1630} // namespace blender::bke
Low-level operations for curves.
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
void CustomData_blend_write_prepare(CustomData &data, blender::Vector< CustomDataLayer, 16 > &layers_to_write, const blender::Set< std::string > &skip_names={})
void CustomData_count_memory(const CustomData &data, int totelem, blender::MemoryCounter &memory)
void CustomData_realloc(CustomData *data, int old_size, int new_size, eCDAllocType alloctype=CD_CONSTRUCT)
void * CustomData_get_layer_named_for_write(CustomData *data, eCustomDataType type, blender::StringRef name, int totelem)
bool CustomData_free_layer_named(CustomData *data, blender::StringRef name, const int totelem)
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
@ CD_SET_DEFAULT
const void * CustomData_get_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
void * CustomData_add_layer_named(CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem, blender::StringRef name)
void CustomData_reset(CustomData *data)
void CustomData_blend_write(BlendWriter *writer, CustomData *data, blender::Span< CustomDataLayer > layers_to_write, int count, eCustomDataMask cddata_mask, ID *id)
void CustomData_init_from(const CustomData *source, CustomData *dest, eCustomDataMask mask, int totelem)
void CustomData_free(CustomData *data, int totelem)
void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count)
void * CustomData_get_layer_for_write(CustomData *data, eCustomDataType type, int totelem)
void * CustomData_add_layer(CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem)
support for deformation groups and hooks.
void BKE_defbase_blend_write(BlendWriter *writer, const ListBase *defbase)
Definition deform.cc:1635
void BKE_defgroup_copy_list(ListBase *outbase, const ListBase *inbase)
Definition deform.cc:76
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
#define UNLIKELY(x)
void BLO_read_int32_array(BlendDataReader *reader, int array_size, int32_t **ptr_p)
Definition readfile.cc:4947
#define BLO_read_struct_list(reader, struct_name, list)
void BLO_write_shared(BlendWriter *writer, const void *data, size_t approximate_size_in_bytes, const blender::ImplicitSharingInfo *sharing_info, blender::FunctionRef< void()> write_fn)
const blender::ImplicitSharingInfo * BLO_read_shared(BlendDataReader *reader, T **data_ptr, blender::FunctionRef< const blender::ImplicitSharingInfo *()> read_fn)
CurveType
@ CURVE_TYPE_BEZIER
@ CURVE_TYPE_NURBS
@ CURVE_TYPE_POLY
@ CURVE_TYPE_CATMULL_ROM
NormalMode
@ NORMAL_MODE_MINIMUM_TWIST
@ NORMAL_MODE_FREE
@ NORMAL_MODE_Z_UP
#define CURVE_TYPES_NUM
KnotsMode
#define CD_MASK_ALL
@ CD_MDEFORMVERT
@ CD_PROP_STRING
Read Guarded memory(de)allocation.
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 void translate_positions(MutableSpan< float3 > positions, const float3 &translation)
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr Span slice_safe(const int64_t start, const int64_t size) const
Definition BLI_span.hh:155
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
Span< T > as_span() const
Definition BLI_array.hh:232
static const CPPType & get()
void copy_from(GSpan values)
GMutableSpan slice(const int64_t start, int64_t size) const
GSpan slice(const int64_t start, int64_t size) const
int64_t size() const
void get_internal_single(void *r_value) const
static GVArray ForSingle(const CPPType &type, int64_t size, const void *value)
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:619
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
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
bool contains(const Key &key) const
Definition BLI_set.hh:291
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr bool is_empty() const
Definition BLI_span.hh:261
static VArray ForSingle(T value, const int64_t size)
static VArray ForSpan(Span< T > values)
void resize(const int64_t new_size)
MutableSpan< T > as_mutable_span()
void reinitialize(const int64_t new_size)
Span< T > as_span() const
void clear_and_shrink()
SharedCache< Vector< curves::nurbs::BasisCache > > nurbs_basis_cache
Definition BKE_curves.hh:90
SharedCache< Vector< float > > evaluated_length_cache
SharedCache< Vector< float3 > > evaluated_tangent_cache
SharedCache< EvaluatedOffsets > evaluated_offsets_cache
Definition BKE_curves.hh:88
SharedCache< Vector< float3 > > evaluated_normal_cache
SharedCache< Bounds< float3 > > bounds_cache
SharedCache< Vector< float3 > > evaluated_position_cache
Definition BKE_curves.hh:96
const ImplicitSharingInfo * curve_offsets_sharing_info
Definition BKE_curves.hh:72
VArray< int8_t > handle_types_left() const
MutableSpan< float3 > positions_for_write()
OffsetIndices< int > points_by_curve() const
VArray< int8_t > normal_mode() const
MutableSpan< int8_t > handle_types_right_for_write()
VArray< int8_t > handle_types_right() const
IndexRange curves_range() const
MutableSpan< int8_t > curve_types_for_write()
const std::array< int, CURVE_TYPES_NUM > & curve_type_counts() const
MutableSpan< float3 > handle_positions_left_for_write()
MutableAttributeAccessor attributes_for_write()
MutableSpan< float3 > handle_positions_right_for_write()
VArray< float > tilt() const
Span< float3 > evaluated_tangents() const
Span< float > nurbs_weights() const
Span< float3 > handle_positions_left() const
VArray< int > resolution() const
IndexRange points_range() const
Span< float3 > evaluated_normals() const
Span< float3 > positions() const
OffsetIndices< int > evaluated_points_by_curve() const
bool has_curve_with_type(CurveType type) const
void resize(int points_num, int curves_num)
Span< float3 > handle_positions_right() const
void fill_curve_types(CurveType type)
IndexMask indices_for_curve_type(CurveType type, IndexMaskMemory &memory) const
bool is_single_type(CurveType type) const
MutableSpan< int > offsets_for_write()
Span< float3 > evaluated_positions() const
VArray< int8_t > curve_types() const
VArray< bool > cyclic() const
VArray< int8_t > nurbs_orders() const
MutableSpan< int8_t > handle_types_left_for_write()
bool remove(const StringRef attribute_id)
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void foreach_index(Fn &&fn) const
void foreach_segment(Fn &&fn) const
void add_shared(const ImplicitSharingInfo *sharing_info, const FunctionRef< void(MemoryCounter &shared_memory)> count_fn)
local_group_size(16, 16) .push_constant(Type b
static bool is_cyclic(const Nurb *nu)
static float normals[][3]
static void transform_positions(const Span< blender::float3 > src, const blender::float4x4 &transform, blender::MutableSpan< blender::float3 > dst)
static char ** types
Definition makesdna.cc:71
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
#define T
void copy_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
typename DefaultMixerStruct< T >::type DefaultMixer
void calculate_auto_handles(bool cyclic, Span< int8_t > types_left, Span< int8_t > types_right, Span< float3 > positions, MutableSpan< float3 > positions_left, MutableSpan< float3 > positions_right)
void calculate_evaluated_offsets(Span< int8_t > handle_types_left, Span< int8_t > handle_types_right, bool cyclic, int resolution, MutableSpan< int > evaluated_offsets)
void calculate_evaluated_positions(Span< float3 > positions, Span< float3 > handles_left, Span< float3 > handles_right, OffsetIndices< int > evaluated_offsets, MutableSpan< float3 > evaluated_positions)
void interpolate_to_evaluated(GSpan src, OffsetIndices< int > evaluated_offsets, GMutableSpan dst)
int calculate_evaluated_num(int points_num, bool cyclic, int resolution)
void interpolate_to_evaluated(GSpan src, bool cyclic, int resolution, GMutableSpan dst)
int calculate_evaluated_num(int points_num, int8_t order, bool cyclic, int resolution, KnotsMode knots_mode)
void interpolate_to_evaluated(const BasisCache &basis_cache, int8_t order, Span< float > control_weights, GSpan src, GMutableSpan dst)
void calculate_normals_z_up(Span< float3 > tangents, MutableSpan< float3 > normals)
void calculate_normals_minimum(Span< float3 > tangents, bool cyclic, MutableSpan< float3 > normals)
void calculate_tangents(Span< float3 > positions, bool is_cyclic, MutableSpan< float3 > tangents)
Definition curve_poly.cc:43
IndexRange per_curve_point_offsets_range(const IndexRange points, const int curve_index)
IndexMask indices_for_type(const VArray< int8_t > &types, const std::array< int, CURVE_TYPES_NUM > &type_counts, const CurveType type, const IndexMask &selection, IndexMaskMemory &memory)
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)
static void normalize_span(MutableSpan< float3 > data)
static CustomData & domain_custom_data(CurvesGeometry &curves, const AttrDomain domain)
CurvesGeometry curves_copy_curve_selection(const CurvesGeometry &curves, const IndexMask &curves_to_copy, const AttributeFilter &attribute_filter)
CurvesGeometry curves_copy_point_selection(const CurvesGeometry &curves, const IndexMask &points_to_copy, const AttributeFilter &attribute_filter)
static const std::string ATTR_HANDLE_POSITION_RIGHT
static MutableSpan< T > get_mutable_attribute(CurvesGeometry &curves, const AttrDomain domain, const StringRef name, const T default_value=T())
static VArray< T > get_varray_attribute(const CurvesGeometry &curves, const AttrDomain domain, const StringRef name, const T default_value)
static const std::string ATTR_NURBS_WEIGHT
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
static const std::string ATTR_HANDLE_TYPE_RIGHT
static const std::string ATTR_NURBS_KNOTS_MODE
static const std::string ATTR_RADIUS
static GVArray adapt_curve_domain_point_to_curve(const CurvesGeometry &curves, const GVArray &varray)
static const std::string ATTR_NORMAL_MODE
void gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
static const std::string ATTR_POSITION
static const std::string ATTR_HANDLE_TYPE_LEFT
CurvesGeometry curves_new_no_attributes(int point_num, int curve_num)
static const std::string ATTR_CYCLIC
static const std::string ATTR_SURFACE_UV_COORDINATE
std::array< int, CURVE_TYPES_NUM > calculate_type_counts(const VArray< int8_t > &types)
static void evaluate_generic_data_for_curve(const EvalData &eval_data, const int curve_index, const GSpan src, GMutableSpan dst)
static void transform_normals(MutableSpan< float3 > normals, const float4x4 &matrix)
static const std::string ATTR_RESOLUTION
static const std::string ATTR_TILT
static const std::string ATTR_CURVE_TYPE
static Span< T > get_span_attribute(const CurvesGeometry &curves, const AttrDomain domain, const StringRef name)
static const std::string ATTR_HANDLE_POSITION_LEFT
void build_offsets(MutableSpan< int > offsets, const CountFn &count_fn)
static void reverse_curve_point_data(const CurvesGeometry &curves, const IndexMask &curve_selection, MutableSpan< T > data)
void gather_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 int domain_num(const CurvesGeometry &curves, const AttrDomain domain)
static const std::string ATTR_NURBS_ORDER
static void reverse_swap_curve_point_data(const CurvesGeometry &curves, const IndexMask &curve_selection, MutableSpan< T > data_a, MutableSpan< T > data_b)
static GVArray adapt_curve_domain_curve_to_point(const CurvesGeometry &curves, const GVArray &varray)
static void adapt_curve_domain_curve_to_point_impl(const CurvesGeometry &curves, const VArray< T > &old_values, MutableSpan< T > r_values)
static void rotate_directions_around_axes(MutableSpan< float3 > directions, const Span< float3 > axes, const Span< float > angles)
static void calculate_evaluated_offsets(const CurvesGeometry &curves, MutableSpan< int > offsets, MutableSpan< int > all_bezier_offsets)
static void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves, const VArray< T > &old_values, MutableSpan< T > r_values)
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:46
void resize_trivial_array(T **data, const ImplicitSharingInfo **sharing_info, int64_t old_size, int64_t new_size)
const ImplicitSharingInfo * info_for_mem_free(void *data)
void free_shared_data(T **data, const ImplicitSharingInfo **sharing_info)
void make_trivial_data_mutable(T **data, const ImplicitSharingInfo **sharing_info, const int64_t size)
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
void accumulate_lengths(const Span< T > values, const bool cyclic, MutableSpan< float > lengths)
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
bool is_zero(const T &a)
CartesianBasis invert(const CartesianBasis &basis)
float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, float angle)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
bool almost_equal_relative(const VecBase< T, Size > &a, const VecBase< T, Size > &b, const T &epsilon_factor)
void build_reverse_map(OffsetIndices< int > offsets, MutableSpan< int > r_map)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
OffsetIndices< int > gather_selected_offsets(OffsetIndices< int > src_offsets, const IndexMask &selection, int start_offset, MutableSpan< int > dst_offsets)
void parallel_invoke(Functions &&...functions)
Definition BLI_task.hh:199
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:153
MatBase< float, 4, 4 > float4x4
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
static void init(bNodeTree *, bNode *node)
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
__int64 int64_t
Definition stdint.h:89
signed char int8_t
Definition stdint.h:75
CustomData point_data
CurvesGeometryRuntimeHandle * runtime
ListBase vertex_group_names
CustomData curve_data
Definition DNA_ID.h:413
const VArray< bool > & cyclic
const Span< float > nurbs_weights
const Span< int > all_bezier_evaluated_offsets
const VArray< int > & resolution
const Span< curves::nurbs::BasisCache > nurbs_basis_cache
const VArray< int8_t > & nurbs_orders
const VArray< int8_t > & types
const OffsetIndices< int > points_by_curve