Blender V4.5
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 <utility>
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_array_utils.hh"
14#include "BLI_bounds.hh"
15#include "BLI_index_mask.hh"
17#include "BLI_listbase.h"
18#include "BLI_math_matrix.hh"
20#include "BLI_memory_counter.hh"
21#include "BLI_resource_scope.hh"
22#include "BLI_task.hh"
23
24#include "BLO_read_write.hh"
25
26#include "DNA_curves_types.h"
27#include "DNA_material_types.h"
28
29#include "BKE_attribute.hh"
31#include "BKE_attribute_math.hh"
35#include "BKE_curves.hh"
36#include "BKE_curves_utils.hh"
37#include "BKE_customdata.hh"
38#include "BKE_deform.hh"
39
40namespace blender::bke {
41
42constexpr StringRef ATTR_POSITION = "position";
43constexpr StringRef ATTR_RADIUS = "radius";
44constexpr StringRef ATTR_TILT = "tilt";
45constexpr StringRef ATTR_CURVE_TYPE = "curve_type";
46constexpr StringRef ATTR_CYCLIC = "cyclic";
47constexpr StringRef ATTR_RESOLUTION = "resolution";
48constexpr StringRef ATTR_NORMAL_MODE = "normal_mode";
49constexpr StringRef ATTR_HANDLE_TYPE_LEFT = "handle_type_left";
50constexpr StringRef ATTR_HANDLE_TYPE_RIGHT = "handle_type_right";
51constexpr StringRef ATTR_HANDLE_POSITION_LEFT = "handle_left";
52constexpr StringRef ATTR_HANDLE_POSITION_RIGHT = "handle_right";
53constexpr StringRef ATTR_NURBS_ORDER = "nurbs_order";
54constexpr StringRef ATTR_NURBS_WEIGHT = "nurbs_weight";
55constexpr StringRef ATTR_NURBS_KNOTS_MODE = "knots_mode";
56constexpr StringRef ATTR_SURFACE_UV_COORDINATE = "surface_uv_coordinate";
57
58/* -------------------------------------------------------------------- */
61
62CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0) {}
63
64CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
65{
66 this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
67
68 this->point_num = point_num;
69 this->curve_num = curve_num;
74
77
78 this->custom_knots = nullptr;
79 this->custom_knot_num = 0;
80
81 if (curve_num > 0) {
82 this->curve_offsets = MEM_malloc_arrayN<int>(size_t(this->curve_num) + 1, __func__);
83 this->runtime->curve_offsets_sharing_info = implicit_sharing::info_for_mem_free(
84 this->curve_offsets);
85#ifndef NDEBUG
86 this->offsets_for_write().fill(-1);
87#endif
88 /* Set common values for convenience. */
89 this->curve_offsets[0] = 0;
90 this->curve_offsets[this->curve_num] = this->point_num;
91 }
92 else {
93 this->curve_offsets = nullptr;
94 }
95
96 /* Fill the type counts with the default so they're in a valid state. */
97 this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_num;
98}
99
100CurvesGeometry::CurvesGeometry(const CurvesGeometry &other)
101{
102 this->curve_offsets = other.curve_offsets;
103 if (other.runtime->curve_offsets_sharing_info) {
104 other.runtime->curve_offsets_sharing_info->add_user();
105 }
106
107 this->custom_knots = other.custom_knots;
108 this->custom_knot_num = other.custom_knot_num;
109 if (other.runtime->custom_knots_sharing_info) {
110 other.runtime->custom_knots_sharing_info->add_user();
111 }
112
113 CustomData_init_from(&other.point_data, &this->point_data, CD_MASK_ALL, other.point_num);
114 CustomData_init_from(&other.curve_data, &this->curve_data, CD_MASK_ALL, other.curve_num);
115
116 new (&this->attribute_storage.wrap()) AttributeStorage(other.attribute_storage.wrap());
117
118 this->point_num = other.point_num;
119 this->curve_num = other.curve_num;
120
123
125
126 this->runtime = MEM_new<CurvesGeometryRuntime>(
127 __func__,
128 CurvesGeometryRuntime{other.runtime->curve_offsets_sharing_info,
129 other.runtime->custom_knots_sharing_info,
130 other.runtime->type_counts,
131 other.runtime->evaluated_offsets_cache,
132 other.runtime->nurbs_basis_cache,
133 other.runtime->evaluated_position_cache,
134 other.runtime->bounds_cache,
135 other.runtime->bounds_with_radius_cache,
136 other.runtime->evaluated_length_cache,
137 other.runtime->evaluated_tangent_cache,
138 other.runtime->evaluated_normal_cache,
139 other.runtime->max_material_index_cache,
140 other.runtime->custom_knot_offsets_cache,
141 {},
142 true});
143
144 if (other.runtime->bake_materials) {
145 this->runtime->bake_materials = std::make_unique<bake::BakeMaterialsList>(
146 *other.runtime->bake_materials);
147 }
148}
149
150CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other)
151{
152 if (this == &other) {
153 return *this;
154 }
155 std::destroy_at(this);
156 new (this) CurvesGeometry(other);
157 return *this;
158}
159
160CurvesGeometry::CurvesGeometry(CurvesGeometry &&other)
161{
162 this->curve_offsets = other.curve_offsets;
163 other.curve_offsets = nullptr;
164
165 this->custom_knots = other.custom_knots;
166 other.custom_knots = nullptr;
167
168 this->custom_knot_num = other.custom_knot_num;
169 other.custom_knot_num = 0;
170
171 this->point_data = other.point_data;
172 CustomData_reset(&other.point_data);
173
174 this->curve_data = other.curve_data;
175 CustomData_reset(&other.curve_data);
176
177 new (&this->attribute_storage.wrap())
178 AttributeStorage(std::move(other.attribute_storage.wrap()));
179
180 this->point_num = other.point_num;
181 other.point_num = 0;
182
183 this->curve_num = other.curve_num;
184 other.curve_num = 0;
185
186 this->vertex_group_names = other.vertex_group_names;
187 BLI_listbase_clear(&other.vertex_group_names);
188
189 this->vertex_group_active_index = other.vertex_group_active_index;
190 other.vertex_group_active_index = 0;
191
192 this->attributes_active_index = other.attributes_active_index;
193 other.attributes_active_index = 0;
194
195 this->runtime = other.runtime;
196 other.runtime = nullptr;
197}
198
199CurvesGeometry &CurvesGeometry::operator=(CurvesGeometry &&other)
200{
201 if (this == &other) {
202 return *this;
203 }
204 std::destroy_at(this);
205 new (this) CurvesGeometry(std::move(other));
206 return *this;
207}
208
209CurvesGeometry::~CurvesGeometry()
210{
213 this->attribute_storage.wrap().~AttributeStorage();
215 if (this->runtime) {
217 &this->runtime->curve_offsets_sharing_info);
219 &this->runtime->custom_knots_sharing_info);
220 MEM_delete(this->runtime);
221 }
222}
223
225
226/* -------------------------------------------------------------------- */
229
230static int domain_num(const CurvesGeometry &curves, const AttrDomain domain)
231{
232 return domain == AttrDomain::Point ? curves.points_num() : curves.curves_num();
233}
234
236{
237 return domain == AttrDomain::Point ? curves.point_data : curves.curve_data;
238}
239
241{
242 return domain == AttrDomain::Point ? curves.point_data : curves.curve_data;
243}
244
245template<typename T>
247 const AttrDomain domain,
248 const StringRef name,
249 const T default_value)
250{
251 const int num = domain_num(curves, domain);
253 const CustomData &custom_data = domain_custom_data(curves, domain);
254
255 const T *data = (const T *)CustomData_get_layer_named(&custom_data, type, name);
256 if (data != nullptr) {
258 }
259 return VArray<T>::ForSingle(default_value, num);
260}
261
262template<typename T>
264 const AttrDomain domain,
265 const StringRef name)
266{
267 const int num = domain_num(curves, domain);
268 const CustomData &custom_data = domain_custom_data(curves, domain);
270
271 T *data = (T *)CustomData_get_layer_named(&custom_data, type, name);
272 if (data == nullptr) {
273 return {};
274 }
275 return {data, num};
276}
277
278template<typename T>
280 const AttrDomain domain,
281 const StringRef name,
282 const T default_value = T())
283{
284 const int num = domain_num(curves, domain);
285 if (num <= 0) {
286 return {};
287 }
289 CustomData &custom_data = domain_custom_data(curves, domain);
290
291 T *data = (T *)CustomData_get_layer_named_for_write(&custom_data, type, name, num);
292 if (data != nullptr) {
293 return {data, num};
294 }
295 data = (T *)CustomData_add_layer_named(&custom_data, type, CD_SET_DEFAULT, num, name);
296 MutableSpan<T> span = {data, num};
297 if (num > 0 && span.first() != default_value) {
298 span.fill(default_value);
299 }
300 return span;
301}
302
303VArray<int8_t> CurvesGeometry::curve_types() const
304{
307}
308
309MutableSpan<int8_t> CurvesGeometry::curve_types_for_write()
310{
312}
313
314void CurvesGeometry::fill_curve_types(const CurveType type)
315{
316 if (type == CURVE_TYPE_CATMULL_ROM) {
317 /* Avoid creating the attribute for Catmull Rom which is the default when the attribute doesn't
318 * exist anyway. */
319 this->attributes_for_write().remove("curve_type");
320 }
321 else {
322 this->curve_types_for_write().fill(type);
323 }
324 this->runtime->type_counts.fill(0);
325 this->runtime->type_counts[type] = this->curves_num();
326 this->tag_topology_changed();
327}
328
329void CurvesGeometry::fill_curve_types(const IndexMask &selection, const CurveType type)
330{
331 if (selection.size() == this->curves_num()) {
332 this->fill_curve_types(type);
333 return;
334 }
335 if (std::optional<int8_t> single_type = this->curve_types().get_if_single()) {
336 if (single_type == type) {
337 this->fill_curve_types(type);
338 return;
339 }
340 }
341 /* A potential performance optimization is only counting the changed indices. */
343 this->update_curve_types();
344 this->tag_topology_changed();
345}
346
347std::array<int, CURVE_TYPES_NUM> calculate_type_counts(const VArray<int8_t> &types)
348{
349 using CountsType = std::array<int, CURVE_TYPES_NUM>;
350 CountsType counts;
351 counts.fill(0);
352
353 if (types.is_single()) {
354 counts[types.get_internal_single()] = types.size();
355 return counts;
356 }
357
358 Span<int8_t> types_span = types.get_internal_span();
360 types.index_range(),
361 2048,
362 counts,
363 [&](const IndexRange curves_range, const CountsType &init) {
364 CountsType result = init;
365 for (const int curve_index : curves_range) {
366 result[types_span[curve_index]]++;
367 }
368 return result;
369 },
370 [](const CountsType &a, const CountsType &b) {
371 CountsType result = a;
372 for (const int i : IndexRange(CURVE_TYPES_NUM)) {
373 result[i] += b[i];
374 }
375 return result;
376 });
377}
378
379void CurvesGeometry::update_curve_types()
380{
381 this->runtime->type_counts = calculate_type_counts(this->curve_types());
382}
383
384Span<float3> CurvesGeometry::positions() const
385{
387}
388MutableSpan<float3> CurvesGeometry::positions_for_write()
389{
391}
392
393VArray<float> CurvesGeometry::radius() const
394{
396}
397MutableSpan<float> CurvesGeometry::radius_for_write()
398{
400}
401
402Span<int> CurvesGeometry::offsets() const
403{
404 if (this->curve_num == 0) {
405 return {};
406 }
407 return {this->curve_offsets, this->curve_num + 1};
408}
409MutableSpan<int> CurvesGeometry::offsets_for_write()
410{
411 if (this->curve_num == 0) {
412 return {};
413 }
415 &this->curve_offsets, &this->runtime->curve_offsets_sharing_info, this->curve_num + 1);
416 return {this->curve_offsets, this->curve_num + 1};
417}
418
419VArray<bool> CurvesGeometry::cyclic() const
420{
422}
423MutableSpan<bool> CurvesGeometry::cyclic_for_write()
424{
426}
427
428VArray<int> CurvesGeometry::resolution() const
429{
431}
432MutableSpan<int> CurvesGeometry::resolution_for_write()
433{
435}
436
437VArray<int8_t> CurvesGeometry::normal_mode() const
438{
440}
441MutableSpan<int8_t> CurvesGeometry::normal_mode_for_write()
442{
444}
445
446VArray<float> CurvesGeometry::tilt() const
447{
449}
450MutableSpan<float> CurvesGeometry::tilt_for_write()
451{
453}
454
455VArray<int8_t> CurvesGeometry::handle_types_left() const
456{
458}
459MutableSpan<int8_t> CurvesGeometry::handle_types_left_for_write()
460{
462}
463
464VArray<int8_t> CurvesGeometry::handle_types_right() const
465{
467}
468MutableSpan<int8_t> CurvesGeometry::handle_types_right_for_write()
469{
471}
472
473Span<float3> CurvesGeometry::handle_positions_left() const
474{
476}
477MutableSpan<float3> CurvesGeometry::handle_positions_left_for_write()
478{
480}
481
482Span<float3> CurvesGeometry::handle_positions_right() const
483{
485}
486MutableSpan<float3> CurvesGeometry::handle_positions_right_for_write()
487{
489}
490
491VArray<int8_t> CurvesGeometry::nurbs_orders() const
492{
494}
495MutableSpan<int8_t> CurvesGeometry::nurbs_orders_for_write()
496{
498}
499
500Span<float> CurvesGeometry::nurbs_weights() const
501{
503}
504MutableSpan<float> CurvesGeometry::nurbs_weights_for_write()
505{
507}
508
509VArray<int8_t> CurvesGeometry::nurbs_knots_modes() const
510{
512}
513MutableSpan<int8_t> CurvesGeometry::nurbs_knots_modes_for_write()
514{
516}
517
518Span<float2> CurvesGeometry::surface_uv_coords() const
519{
521}
522
523MutableSpan<float2> CurvesGeometry::surface_uv_coords_for_write()
524{
526}
527
528Span<float> CurvesGeometry::nurbs_custom_knots() const
529{
530 if (this->custom_knot_num == 0) {
531 return {};
532 }
533 return {this->custom_knots, this->custom_knot_num};
534}
535
536MutableSpan<float> CurvesGeometry::nurbs_custom_knots_for_write()
537{
538 if (this->custom_knot_num == 0) {
539 return {};
540 }
542 &this->custom_knots, &this->runtime->custom_knots_sharing_info, this->custom_knot_num);
543 return {this->custom_knots, this->custom_knot_num};
544}
545
546IndexMask CurvesGeometry::nurbs_custom_knot_curves(IndexMaskMemory &memory) const
547{
548 const VArray<int8_t> curve_types = this->curve_types();
549 const VArray<int8_t> knot_modes = this->nurbs_knots_modes();
551 this->curves_range(), GrainSize(4096), memory, [&](const int64_t curve) {
552 return curve_types[curve] == CURVE_TYPE_NURBS &&
553 knot_modes[curve] == NURBS_KNOT_MODE_CUSTOM;
554 });
555}
556
557OffsetIndices<int> CurvesGeometry::nurbs_custom_knots_by_curve() const
558{
559 const CurvesGeometryRuntime &runtime = *this->runtime;
560 if (this->is_empty()) {
561 return {};
562 }
563 runtime.custom_knot_offsets_cache.ensure([&](Vector<int> &r_data) {
564 r_data.resize(this->curve_num + 1, 0);
565
566 const OffsetIndices points_by_curve = this->points_by_curve();
567 const VArray<int8_t> knot_modes = this->nurbs_knots_modes();
568 const VArray<int8_t> orders = this->nurbs_orders();
569
570 int knot_count = 0;
571 for (const int curve : this->curves_range()) {
572 knot_count += knot_modes[curve] == NURBS_KNOT_MODE_CUSTOM ?
573 points_by_curve[curve].size() + orders[curve] :
574 0;
575 r_data[curve + 1] = knot_count;
576 }
577 });
578 return OffsetIndices<int>(runtime.custom_knot_offsets_cache.data());
579}
580
581void CurvesGeometry::nurbs_custom_knots_update_size()
582{
583 this->runtime->custom_knot_offsets_cache.tag_dirty();
584 const OffsetIndices<int> knots_by_curve = this->nurbs_custom_knots_by_curve();
585 const int knots_num = knots_by_curve.total_size();
586 if (this->custom_knot_num != knots_num) {
588 &this->runtime->custom_knots_sharing_info,
589 this->custom_knot_num,
590 knots_num);
591 this->custom_knot_num = knots_num;
592 }
593}
594
595void CurvesGeometry::nurbs_custom_knots_resize(int knots_num)
596{
598 &this->runtime->custom_knots_sharing_info,
599 this->custom_knot_num,
600 knots_num);
601 this->custom_knot_num = knots_num;
602 this->runtime->custom_knot_offsets_cache.tag_dirty();
603}
604
605Span<MDeformVert> CurvesGeometry::deform_verts() const
606{
607 const MDeformVert *dverts = static_cast<const MDeformVert *>(
609 if (dverts == nullptr) {
610 return {};
611 }
612 return {dverts, this->point_num};
613}
614
615MutableSpan<MDeformVert> CurvesGeometry::deform_verts_for_write()
616{
617 MDeformVert *dvert = static_cast<MDeformVert *>(
619 if (dvert != nullptr) {
620 return {dvert, this->point_num};
621 }
622 return {static_cast<MDeformVert *>(CustomData_add_layer(
624 this->point_num};
625}
626
628
629/* -------------------------------------------------------------------- */
632
633template<typename CountFn> void build_offsets(MutableSpan<int> offsets, const CountFn &count_fn)
634{
635 int offset = 0;
636 for (const int i : offsets.drop_back(1).index_range()) {
637 offsets[i] = offset;
638 offset += count_fn(i);
639 }
640 offsets.last() = offset;
641}
642
644 MutableSpan<int> offsets,
645 MutableSpan<int> all_bezier_offsets)
646{
647 const OffsetIndices points_by_curve = curves.points_by_curve();
648 const VArray<int8_t> types = curves.curve_types();
649 const VArray<int> resolution = curves.resolution();
650 const VArray<bool> cyclic = curves.cyclic();
651
652 VArraySpan<int8_t> handle_types_left;
653 VArraySpan<int8_t> handle_types_right;
654 if (curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
655 handle_types_left = curves.handle_types_left();
656 handle_types_right = curves.handle_types_right();
657 }
658
659 const VArray<int8_t> nurbs_orders = curves.nurbs_orders();
660 const VArray<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes();
661
662 build_offsets(offsets, [&](const int curve_index) -> int {
663 const IndexRange points = points_by_curve[curve_index];
664 switch (types[curve_index]) {
667 points.size(), cyclic[curve_index], resolution[curve_index]);
668 case CURVE_TYPE_POLY:
669 return points.size();
670 case CURVE_TYPE_BEZIER: {
671 const IndexRange offsets = curves::per_curve_point_offsets_range(points, curve_index);
672 curves::bezier::calculate_evaluated_offsets(handle_types_left.slice(points),
673 handle_types_right.slice(points),
674 cyclic[curve_index],
675 resolution[curve_index],
676 all_bezier_offsets.slice(offsets));
677 return all_bezier_offsets[offsets.last()];
678 }
679 case CURVE_TYPE_NURBS:
681 nurbs_orders[curve_index],
682 cyclic[curve_index],
683 resolution[curve_index],
684 KnotsMode(nurbs_knots_modes[curve_index]));
685 }
687 return 0;
688 });
689}
690
691OffsetIndices<int> CurvesGeometry::evaluated_points_by_curve() const
692{
693 const CurvesGeometryRuntime &runtime = *this->runtime;
694 if (this->is_single_type(CURVE_TYPE_POLY)) {
695 /* When all the curves are poly curves, the evaluated offsets are the same as the control
696 * point offsets, so it's possible to completely avoid building a new offsets array. */
697 runtime.evaluated_offsets_cache.ensure([&](CurvesGeometryRuntime::EvaluatedOffsets &r_data) {
700 });
701 return this->points_by_curve();
702 }
703
704 runtime.evaluated_offsets_cache.ensure([&](CurvesGeometryRuntime::EvaluatedOffsets &r_data) {
705 r_data.evaluated_offsets.resize(this->curves_num() + 1);
706
708 r_data.all_bezier_offsets.resize(this->points_num() + this->curves_num());
709 }
710 else {
712 }
713
715 });
716
717 return OffsetIndices<int>(runtime.evaluated_offsets_cache.data().evaluated_offsets);
718}
719
720IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
721 IndexMaskMemory &memory) const
722{
723 return this->indices_for_curve_type(type, this->curves_range(), memory);
724}
725
726IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
727 const IndexMask &selection,
728 IndexMaskMemory &memory) const
729{
731 this->curve_types(), this->curve_type_counts(), type, selection, memory);
732}
733
734Array<int> CurvesGeometry::point_to_curve_map() const
735{
736 Array<int> map(this->points_num());
738 return map;
739}
740
741void CurvesGeometry::ensure_nurbs_basis_cache() const
742{
743 const CurvesGeometryRuntime &runtime = *this->runtime;
744 runtime.nurbs_basis_cache.ensure([&](Vector<curves::nurbs::BasisCache> &r_data) {
745 IndexMaskMemory memory;
746 const IndexMask nurbs_mask = this->indices_for_curve_type(CURVE_TYPE_NURBS, memory);
747 if (nurbs_mask.is_empty()) {
748 r_data.clear_and_shrink();
749 return;
750 }
751
752 r_data.resize(this->curves_num());
753
754 const OffsetIndices<int> points_by_curve = this->points_by_curve();
755 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
756 const OffsetIndices<int> custom_knots_by_curve = this->nurbs_custom_knots_by_curve();
757 const VArray<bool> cyclic = this->cyclic();
758 const VArray<int8_t> orders = this->nurbs_orders();
759 const VArray<int8_t> knots_modes = this->nurbs_knots_modes();
760 const Span<float> custom_knots = this->nurbs_custom_knots();
761
762 nurbs_mask.foreach_segment(GrainSize(64), [&](const IndexMaskSegment segment) {
763 Vector<float, 32> knots;
764 for (const int curve_index : segment) {
765 const IndexRange points = points_by_curve[curve_index];
766 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
767
768 const int8_t order = orders[curve_index];
769 const bool is_cyclic = cyclic[curve_index];
770 const KnotsMode mode = KnotsMode(knots_modes[curve_index]);
771
772 if (!curves::nurbs::check_valid_num_and_order(points.size(), order, is_cyclic, mode)) {
773 r_data[curve_index].invalid = true;
774 continue;
775 }
776 const int knots_num = curves::nurbs::knots_num(points.size(), order, is_cyclic);
777 knots.reinitialize(knots_num);
778 curves::nurbs::load_curve_knots(mode,
779 points.size(),
780 order,
781 is_cyclic,
782 custom_knots_by_curve[curve_index],
783 custom_knots,
784 knots);
785
786 curves::nurbs::calculate_basis_cache(
787 points.size(), evaluated_points.size(), order, is_cyclic, knots, r_data[curve_index]);
788 }
789 });
790 });
791}
792
793Span<float3> CurvesGeometry::evaluated_positions() const
794{
795 const CurvesGeometryRuntime &runtime = *this->runtime;
796 if (this->is_single_type(CURVE_TYPE_POLY)) {
797 runtime.evaluated_position_cache.ensure(
798 [&](Vector<float3> &r_data) { r_data.clear_and_shrink(); });
799 return this->positions();
800 }
801 this->ensure_nurbs_basis_cache();
802 runtime.evaluated_position_cache.ensure([&](Vector<float3> &r_data) {
803 r_data.resize(this->evaluated_points_num());
805
806 const OffsetIndices<int> points_by_curve = this->points_by_curve();
807 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
808 const Span<float3> positions = this->positions();
809
810 auto evaluate_catmull = [&](const IndexMask &selection) {
811 const VArray<bool> cyclic = this->cyclic();
812 const VArray<int> resolution = this->resolution();
813 selection.foreach_index(GrainSize(128), [&](const int curve_index) {
814 const IndexRange points = points_by_curve[curve_index];
815 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
817 cyclic[curve_index],
818 resolution[curve_index],
819 evaluated_positions.slice(evaluated_points));
820 });
821 };
822 auto evaluate_poly = [&](const IndexMask &selection) {
825 };
826 auto evaluate_bezier = [&](const IndexMask &selection) {
827 const Span<float3> handle_positions_left = this->handle_positions_left();
828 const Span<float3> handle_positions_right = this->handle_positions_right();
829 if (handle_positions_left.is_empty() || handle_positions_right.is_empty()) {
831 return;
832 }
833 const Span<int> all_bezier_offsets =
834 runtime.evaluated_offsets_cache.data().all_bezier_offsets;
835 selection.foreach_index(GrainSize(128), [&](const int curve_index) {
836 const IndexRange points = points_by_curve[curve_index];
837 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
838 const IndexRange offsets = curves::per_curve_point_offsets_range(points, curve_index);
840 handle_positions_left.slice(points),
841 handle_positions_right.slice(points),
842 all_bezier_offsets.slice(offsets),
843 evaluated_positions.slice(evaluated_points));
844 });
845 };
846 auto evaluate_nurbs = [&](const IndexMask &selection) {
847 this->ensure_nurbs_basis_cache();
848 const VArray<int8_t> nurbs_orders = this->nurbs_orders();
849 const Span<float> nurbs_weights = this->nurbs_weights();
850 const Span<curves::nurbs::BasisCache> nurbs_basis_cache = runtime.nurbs_basis_cache.data();
851 selection.foreach_index(GrainSize(128), [&](const int curve_index) {
852 const IndexRange points = points_by_curve[curve_index];
853 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
854 curves::nurbs::interpolate_to_evaluated(nurbs_basis_cache[curve_index],
855 nurbs_orders[curve_index],
856 nurbs_weights.slice_safe(points),
857 positions.slice(points),
858 evaluated_positions.slice(evaluated_points));
859 });
860 };
862 this->curve_type_counts(),
863 this->curves_range(),
864 evaluate_catmull,
865 evaluate_poly,
866 evaluate_bezier,
867 evaluate_nurbs);
868 });
869 return runtime.evaluated_position_cache.data();
870}
871
872Span<float3> CurvesGeometry::evaluated_tangents() const
873{
874 const CurvesGeometryRuntime &runtime = *this->runtime;
875 runtime.evaluated_tangent_cache.ensure([&](Vector<float3> &r_data) {
876 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
877 const Span<float3> evaluated_positions = this->evaluated_positions();
878 const VArray<bool> cyclic = this->cyclic();
879
880 r_data.resize(this->evaluated_points_num());
881 MutableSpan<float3> tangents = r_data;
882
884 for (const int curve_index : curves_range) {
885 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
887 cyclic[curve_index],
888 tangents.slice(evaluated_points));
889 }
890 });
891
892 /* Correct the first and last tangents of non-cyclic Bezier curves so that they align with
893 * the inner handles. This is a separate loop to avoid the cost when Bezier type curves are
894 * not used. */
895 IndexMaskMemory memory;
896 const IndexMask bezier_mask = this->indices_for_curve_type(CURVE_TYPE_BEZIER, memory);
897 if (!bezier_mask.is_empty()) {
898 const OffsetIndices<int> points_by_curve = this->points_by_curve();
899 const Span<float3> positions = this->positions();
900 const Span<float3> handles_left = this->handle_positions_left();
901 const Span<float3> handles_right = this->handle_positions_right();
902
903 bezier_mask.foreach_index(GrainSize(1024), [&](const int curve_index) {
904 if (cyclic[curve_index]) {
905 return;
906 }
907 const IndexRange points = points_by_curve[curve_index];
908 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
909
910 const float epsilon = 1e-6f;
912 handles_right[points.first()], positions[points.first()], epsilon))
913 {
914 tangents[evaluated_points.first()] = math::normalize(handles_right[points.first()] -
915 positions[points.first()]);
916 }
918 handles_left[points.last()], positions[points.last()], epsilon))
919 {
920 tangents[evaluated_points.last()] = math::normalize(positions[points.last()] -
921 handles_left[points.last()]);
922 }
923 });
924 }
925 });
926 return runtime.evaluated_tangent_cache.data();
927}
928
930 const Span<float3> axes,
931 const Span<float> angles)
932{
933 for (const int i : directions.index_range()) {
934 const float3 axis = axes[i];
935 if (UNLIKELY(math::is_zero(axis))) {
936 continue;
937 }
938 directions[i] = math::rotate_direction_around_axis(directions[i], axis, angles[i]);
939 }
940}
941
943{
944 for (const int i : data.index_range()) {
946 }
947}
948
960
961static void evaluate_generic_data_for_curve(const EvalData &eval_data,
962 const int curve_index,
963 const GSpan src,
964 GMutableSpan dst)
965{
966 const IndexRange points = eval_data.points_by_curve[curve_index];
967 switch (eval_data.types[curve_index]) {
970 src, eval_data.cyclic[curve_index], eval_data.resolution[curve_index], dst);
971 break;
972 case CURVE_TYPE_POLY:
973 dst.copy_from(src);
974 break;
975 case CURVE_TYPE_BEZIER: {
976 const IndexRange offsets = curves::per_curve_point_offsets_range(points, curve_index);
978 src, eval_data.all_bezier_evaluated_offsets.slice(offsets), dst);
979 break;
980 }
981 case CURVE_TYPE_NURBS:
983 eval_data.nurbs_orders[curve_index],
984 eval_data.nurbs_weights.slice_safe(points),
985 src,
986 dst);
987 break;
988 }
989}
990
991Span<float3> CurvesGeometry::evaluated_normals() const
992{
993 const CurvesGeometryRuntime &runtime = *this->runtime;
994 this->ensure_nurbs_basis_cache();
995 runtime.evaluated_normal_cache.ensure([&](Vector<float3> &r_data) {
996 const OffsetIndices<int> points_by_curve = this->points_by_curve();
997 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
998 const VArray<int8_t> types = this->curve_types();
999 const VArray<bool> cyclic = this->cyclic();
1000 const VArray<int8_t> normal_mode = this->normal_mode();
1001 const Span<float3> evaluated_tangents = this->evaluated_tangents();
1002 const AttributeAccessor attributes = this->attributes();
1003 const EvalData eval_data{
1005 types,
1006 cyclic,
1007 this->resolution(),
1008 runtime.evaluated_offsets_cache.data().all_bezier_offsets,
1009 runtime.nurbs_basis_cache.data(),
1010 this->nurbs_orders(),
1011 this->nurbs_weights(),
1012 };
1013 const VArray<float> tilt = this->tilt();
1014 VArraySpan<float> tilt_span;
1015 const bool use_tilt = !(tilt.is_single() && tilt.get_internal_single() == 0.0f);
1016 if (use_tilt) {
1017 tilt_span = tilt;
1018 }
1019 VArraySpan<float3> custom_normal_span;
1020 if (const VArray<float3> custom_normal = *attributes.lookup<float3>("custom_normal",
1022 {
1023 custom_normal_span = custom_normal;
1024 }
1025
1026 r_data.resize(this->evaluated_points_num());
1028
1030 /* Reuse a buffer for the evaluated tilts. */
1031 Vector<float> evaluated_tilts;
1032
1033 for (const int curve_index : curves_range) {
1034 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
1035 switch (NormalMode(normal_mode[curve_index])) {
1036 case NORMAL_MODE_Z_UP:
1038 evaluated_normals.slice(evaluated_points));
1039 break;
1042 cyclic[curve_index],
1043 evaluated_normals.slice(evaluated_points));
1044 break;
1045 case NORMAL_MODE_FREE:
1046 if (custom_normal_span.is_empty()) {
1048 evaluated_normals.slice(evaluated_points));
1049 }
1050 else {
1051 const Span<float3> src = custom_normal_span.slice(points_by_curve[curve_index]);
1053 evaluated_points_by_curve[curve_index]);
1054 evaluate_generic_data_for_curve(eval_data, curve_index, src, dst);
1055 normalize_span(dst);
1056 }
1057 break;
1058 }
1059
1060 /* If the "tilt" attribute exists, rotate the normals around the tangents by the
1061 * evaluated angles. We can avoid copying the tilts to evaluate them for poly curves. */
1062 if (use_tilt) {
1063 const IndexRange points = points_by_curve[curve_index];
1064 if (types[curve_index] == CURVE_TYPE_POLY) {
1065 rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
1066 evaluated_tangents.slice(evaluated_points),
1067 tilt_span.slice(points));
1068 }
1069 else {
1070 evaluated_tilts.reinitialize(evaluated_points.size());
1072 curve_index,
1073 tilt_span.slice(points),
1074 evaluated_tilts.as_mutable_span());
1075 rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
1076 evaluated_tangents.slice(evaluated_points),
1077 evaluated_tilts.as_span());
1078 }
1079 }
1080 }
1081 });
1082 });
1083 return this->runtime->evaluated_normal_cache.data();
1084}
1085
1086void CurvesGeometry::interpolate_to_evaluated(const int curve_index,
1087 const GSpan src,
1088 GMutableSpan dst) const
1089{
1090 const CurvesGeometryRuntime &runtime = *this->runtime;
1091 const EvalData eval_data{
1092 this->points_by_curve(),
1093 this->curve_types(),
1094 this->cyclic(),
1095 this->resolution(),
1096 runtime.evaluated_offsets_cache.data().all_bezier_offsets,
1097 runtime.nurbs_basis_cache.data(),
1098 this->nurbs_orders(),
1099 this->nurbs_weights(),
1100 };
1101 BLI_assert(src.size() == this->points_by_curve()[curve_index].size());
1102 BLI_assert(dst.size() == this->evaluated_points_by_curve()[curve_index].size());
1103 evaluate_generic_data_for_curve(eval_data, curve_index, src, dst);
1104}
1105
1106void CurvesGeometry::interpolate_to_evaluated(const GSpan src, GMutableSpan dst) const
1107{
1108 const CurvesGeometryRuntime &runtime = *this->runtime;
1109 const OffsetIndices points_by_curve = this->points_by_curve();
1110 const EvalData eval_data{
1112 this->curve_types(),
1113 this->cyclic(),
1114 this->resolution(),
1115 runtime.evaluated_offsets_cache.data().all_bezier_offsets,
1116 runtime.nurbs_basis_cache.data(),
1117 this->nurbs_orders(),
1118 this->nurbs_weights(),
1119 };
1120 const OffsetIndices evaluated_points_by_curve = this->evaluated_points_by_curve();
1121
1123 for (const int curve_index : curves_range) {
1124 const IndexRange points = points_by_curve[curve_index];
1125 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
1127 eval_data, curve_index, src.slice(points), dst.slice(evaluated_points));
1128 }
1129 });
1130}
1131
1132void CurvesGeometry::ensure_evaluated_lengths() const
1133{
1134 const CurvesGeometryRuntime &runtime = *this->runtime;
1135 runtime.evaluated_length_cache.ensure([&](Vector<float> &r_data) {
1136 /* Use an extra length value for the final cyclic segment for a consistent size
1137 * (see comment on #evaluated_length_cache). */
1138 const int total_num = this->evaluated_points_num() + this->curves_num();
1139 r_data.resize(total_num);
1140 MutableSpan<float> evaluated_lengths = r_data;
1141
1142 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
1143 const Span<float3> evaluated_positions = this->evaluated_positions();
1144 const VArray<bool> curves_cyclic = this->cyclic();
1145
1147 for (const int curve_index : curves_range) {
1148 const bool cyclic = curves_cyclic[curve_index];
1149 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
1150 const IndexRange lengths_range = this->lengths_range_for_curve(curve_index, cyclic);
1152 cyclic,
1153 evaluated_lengths.slice(lengths_range));
1154 }
1155 });
1156 });
1157}
1158
1159void CurvesGeometry::ensure_can_interpolate_to_evaluated() const
1160{
1161 this->evaluated_points_by_curve();
1162 this->ensure_nurbs_basis_cache();
1163}
1164
1166
1167/* -------------------------------------------------------------------- */
1170
1171void CurvesGeometry::resize(const int points_num, const int curves_num)
1172{
1173 BLI_assert(curves_num >= 0 && points_num >= 0);
1174 if (points_num != this->point_num) {
1175 CustomData_realloc(&this->point_data, this->points_num(), points_num);
1176 this->point_num = points_num;
1177 }
1178 if (curves_num != this->curve_num) {
1179 CustomData_realloc(&this->curve_data, this->curves_num(), curves_num);
1181 &this->runtime->curve_offsets_sharing_info,
1182 this->curve_num == 0 ? 0 : (this->curve_num + 1),
1183 curves_num == 0 ? 0 : (curves_num + 1));
1184 if (curves_num > 0) {
1185 /* Set common values for convenience. */
1186 this->curve_offsets[0] = 0;
1187 this->curve_offsets[curves_num] = this->point_num;
1188 }
1189 this->curve_num = curves_num;
1190 }
1191 this->tag_topology_changed();
1192}
1193
1194void CurvesGeometry::tag_positions_changed()
1195{
1196 this->runtime->evaluated_position_cache.tag_dirty();
1197 this->runtime->evaluated_tangent_cache.tag_dirty();
1198 this->runtime->evaluated_normal_cache.tag_dirty();
1199 this->runtime->evaluated_length_cache.tag_dirty();
1200 this->runtime->bounds_cache.tag_dirty();
1201 this->runtime->bounds_with_radius_cache.tag_dirty();
1202}
1203void CurvesGeometry::tag_topology_changed()
1204{
1205 this->runtime->custom_knot_offsets_cache.tag_dirty();
1206 this->tag_positions_changed();
1207 this->runtime->evaluated_offsets_cache.tag_dirty();
1208 this->runtime->nurbs_basis_cache.tag_dirty();
1209 this->runtime->max_material_index_cache.tag_dirty();
1210 this->runtime->check_type_counts = true;
1211}
1212void CurvesGeometry::tag_normals_changed()
1213{
1214 this->runtime->evaluated_normal_cache.tag_dirty();
1215}
1216void CurvesGeometry::tag_radii_changed()
1217{
1218 this->runtime->bounds_with_radius_cache.tag_dirty();
1219}
1220void CurvesGeometry::tag_material_index_changed()
1221{
1222 this->runtime->max_material_index_cache.tag_dirty();
1223}
1224
1225static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
1226{
1227 threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
1228 for (float3 &position : positions.slice(range)) {
1229 position += translation;
1230 }
1231 });
1232}
1233
1234static void transform_positions(MutableSpan<float3> positions, const float4x4 &matrix)
1235{
1236 threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
1237 for (float3 &position : positions.slice(range)) {
1238 position = math::transform_point(matrix, position);
1239 }
1240 });
1241}
1242
1243void CurvesGeometry::calculate_bezier_auto_handles()
1244{
1246 return;
1247 }
1249 return;
1250 }
1251 const OffsetIndices points_by_curve = this->points_by_curve();
1252 const VArray<int8_t> types = this->curve_types();
1253 const VArray<bool> cyclic = this->cyclic();
1254 const VArraySpan<int8_t> types_left{this->handle_types_left()};
1255 const VArraySpan<int8_t> types_right{this->handle_types_right()};
1256 const Span<float3> positions = this->positions();
1258 MutableSpan<float3> positions_right = this->handle_positions_right_for_write();
1259
1260 threading::parallel_for(this->curves_range(), 128, [&](IndexRange range) {
1261 for (const int i_curve : range) {
1262 if (types[i_curve] == CURVE_TYPE_BEZIER) {
1263 const IndexRange points = points_by_curve[i_curve];
1265 types_left.slice(points),
1266 types_right.slice(points),
1267 positions.slice(points),
1268 positions_left.slice(points),
1269 positions_right.slice(points));
1270 }
1271 }
1272 });
1273}
1274
1275void CurvesGeometry::translate(const float3 &translation)
1276{
1277 if (math::is_zero(translation)) {
1278 return;
1279 }
1280
1281 std::optional<Bounds<float3>> bounds;
1282 if (this->runtime->bounds_cache.is_cached()) {
1283 bounds = this->runtime->bounds_cache.data();
1284 }
1285
1286 translate_positions(this->positions_for_write(), translation);
1287 if (!this->handle_positions_left().is_empty()) {
1289 }
1290 if (!this->handle_positions_right().is_empty()) {
1292 }
1293 this->tag_positions_changed();
1294
1295 if (bounds) {
1296 bounds->min += translation;
1297 bounds->max += translation;
1298 this->runtime->bounds_cache.ensure([&](blender::Bounds<float3> &r_data) { r_data = *bounds; });
1299 }
1300}
1301
1302void CurvesGeometry::transform(const float4x4 &matrix)
1303{
1305 if (!this->handle_positions_left().is_empty()) {
1307 }
1308 if (!this->handle_positions_right().is_empty()) {
1310 }
1313 this->tag_positions_changed();
1314}
1315
1316std::optional<Bounds<float3>> CurvesGeometry::bounds_min_max(const bool use_radius) const
1317{
1318 if (this->is_empty()) {
1319 return std::nullopt;
1320 }
1321 if (use_radius) {
1322 this->runtime->bounds_with_radius_cache.ensure([&](Bounds<float3> &r_bounds) {
1323 const VArray<float> radius = this->radius();
1324 if (const std::optional radius_single = radius.get_if_single()) {
1325 r_bounds = *this->bounds_min_max(false);
1326 r_bounds.pad(*radius_single);
1327 return;
1328 }
1329 const Span radius_span = radius.get_internal_span();
1330 if (this->is_single_type(CURVE_TYPE_POLY)) {
1331 r_bounds = *bounds::min_max_with_radii(this->positions(), radius_span);
1332 return;
1333 }
1334 Array<float> radii_eval(this->evaluated_points_num());
1336 this->interpolate_to_evaluated(radius_span, radii_eval.as_mutable_span());
1337 r_bounds = *bounds::min_max_with_radii(this->evaluated_positions(), radii_eval.as_span());
1338 });
1339 }
1340 else {
1341 this->runtime->bounds_cache.ensure([&](Bounds<float3> &r_bounds) {
1342 r_bounds = *bounds::min_max(this->evaluated_positions());
1343 });
1344 }
1345 return use_radius ? this->runtime->bounds_with_radius_cache.data() :
1346 this->runtime->bounds_cache.data();
1347}
1348
1349std::optional<int> CurvesGeometry::material_index_max() const
1350{
1351 this->runtime->max_material_index_cache.ensure([&](std::optional<int> &r_max_material_index) {
1352 r_max_material_index = blender::bounds::max<int>(
1353 this->attributes()
1354 .lookup_or_default<int>("material_index", blender::bke::AttrDomain::Curve, 0)
1355 .varray);
1356 if (r_max_material_index.has_value()) {
1357 r_max_material_index = std::clamp(*r_max_material_index, 0, MAXMAT);
1358 }
1359 });
1360 return this->runtime->max_material_index_cache.data();
1361}
1362
1363void CurvesGeometry::count_memory(MemoryCounter &memory) const
1364{
1365 memory.add_shared(this->runtime->curve_offsets_sharing_info, this->offsets().size_in_bytes());
1366 memory.add_shared(this->runtime->custom_knots_sharing_info,
1367 this->nurbs_custom_knots().size_in_bytes());
1368 CustomData_count_memory(this->point_data, this->point_num, memory);
1369 CustomData_count_memory(this->curve_data, this->curve_num, memory);
1370}
1371
1373 const IndexMask &points_to_copy,
1374 const Span<int> curve_point_counts,
1375 CurvesGeometry &dst_curves)
1376{
1377 const OffsetIndices points_by_curve = curves.points_by_curve();
1378 const VArray<int8_t> orders = curves.nurbs_orders();
1379 const VArray<bool> cyclic = curves.cyclic();
1380
1381 IndexMaskMemory memory;
1382 const IndexMask custom_knot_curves = curves.nurbs_custom_knot_curves(memory);
1383 const IndexMask custom_knot_points = bke::curves::curve_to_point_selection(
1384 points_by_curve, custom_knot_curves, memory);
1385 const IndexMask custom_knot_points_to_copy = IndexMask::from_intersection(
1386 points_to_copy, custom_knot_points, memory);
1387
1388 int dst_knot_count = 0;
1389 custom_knot_curves.foreach_index([&](const int64_t curve) {
1390 dst_knot_count += curves::nurbs::knots_num(
1391 curve_point_counts[curve], orders[curve], cyclic[curve]);
1392 });
1393 const OffsetIndices<int> src_knots_by_curve = curves.nurbs_custom_knots_by_curve();
1394 const Span<float> src_knots = curves.nurbs_custom_knots();
1395
1396 Vector<float> new_knots;
1397 new_knots.reserve(dst_knot_count);
1398
1400 custom_knot_points_to_copy,
1401 points_by_curve,
1402 [&](int curve, IndexRange points, Span<IndexRange> ranges_to_copy) {
1403 const IndexRange src_range = src_knots_by_curve[curve];
1404 const int order = orders[curve];
1405 const int leading_spans = order / 2;
1406 const int point_to_knot = -points.start() + src_range.start();
1407 const int point_to_span = point_to_knot + leading_spans;
1408
1409 const int first_knot = ranges_to_copy.first().start() + point_to_knot;
1410 new_knots.extend(
1411 src_knots.slice(IndexRange::from_begin_size(first_knot, leading_spans + 1)));
1412 float last_knot = new_knots.last();
1413 for (IndexRange range : ranges_to_copy) {
1414 for (const int spans_left_knot : range.shift(point_to_span)) {
1415 last_knot += src_knots[spans_left_knot + 1] - src_knots[spans_left_knot];
1416 new_knots.append(last_knot);
1417 }
1418 }
1419 const int last_spans_left_knot = ranges_to_copy.last().last() + point_to_span + 1;
1420 last_knot += src_knots[last_spans_left_knot + 1] - src_knots[last_spans_left_knot];
1421 new_knots.append(last_knot);
1422 });
1423 dst_curves.nurbs_custom_knots_update_size();
1424 dst_curves.nurbs_custom_knots_for_write().copy_from(new_knots);
1425}
1426
1428 const IndexMask &points_to_copy,
1429 const AttributeFilter &attribute_filter)
1430{
1431 const Array<int> point_to_curve_map = curves.point_to_curve_map();
1432 Array<int> curve_point_counts(curves.curves_num(), 0);
1433 points_to_copy.foreach_index(
1434 [&](const int64_t point_i) { curve_point_counts[point_to_curve_map[point_i]]++; });
1435
1436 IndexMaskMemory memory;
1437 const IndexMask curves_to_copy = IndexMask::from_predicate(
1438 curves.curves_range(), GrainSize(4096), memory, [&](const int64_t i) {
1439 return curve_point_counts[i] > 0;
1440 });
1441
1442 CurvesGeometry dst_curves(points_to_copy.size(), curves_to_copy.size());
1443
1444 BKE_defgroup_copy_list(&dst_curves.vertex_group_names, &curves.vertex_group_names);
1445
1447 dst_curves.curves_num() > 1024,
1448 [&]() {
1449 if (curves_to_copy.is_empty()) {
1450 return;
1451 }
1452 MutableSpan<int> new_curve_offsets = dst_curves.offsets_for_write();
1454 curve_point_counts.as_span(), curves_to_copy, new_curve_offsets.drop_back(1));
1456 },
1457 [&]() {
1458 gather_attributes(curves.attributes(),
1459 AttrDomain::Point,
1460 AttrDomain::Point,
1461 attribute_filter,
1462 points_to_copy,
1463 dst_curves.attributes_for_write());
1464 gather_attributes(curves.attributes(),
1465 AttrDomain::Curve,
1466 AttrDomain::Curve,
1467 attribute_filter,
1468 curves_to_copy,
1469 dst_curves.attributes_for_write());
1470 });
1471
1472 if (curves.nurbs_has_custom_knots()) {
1473 copy_point_selection_custom_knots(curves, points_to_copy, curve_point_counts, dst_curves);
1474 }
1475
1476 if (dst_curves.curves_num() == curves.curves_num()) {
1477 dst_curves.runtime->type_counts = curves.runtime->type_counts;
1478 }
1479 else {
1480 dst_curves.remove_attributes_based_on_types();
1481 }
1482
1483 return dst_curves;
1484}
1485
1486void CurvesGeometry::remove_points(const IndexMask &points_to_delete,
1487 const AttributeFilter &attribute_filter)
1488{
1489 if (points_to_delete.is_empty()) {
1490 return;
1491 }
1492 if (points_to_delete.size() == this->points_num()) {
1493 this->resize(0, 0);
1494 this->update_curve_types();
1495 return;
1496 }
1497 IndexMaskMemory memory;
1498 const IndexMask points_to_copy = points_to_delete.complement(this->points_range(), memory);
1499 *this = curves_copy_point_selection(*this, points_to_copy, attribute_filter);
1500}
1501
1503 const IndexMask &curves_to_copy,
1504 CurvesGeometry &dst_curves)
1505{
1506 IndexMaskMemory memory;
1507 const IndexMask custom_knot_curves = curves.nurbs_custom_knot_curves(memory);
1508 const IndexMask custom_knot_curves_to_copy = IndexMask::from_intersection(
1509 curves_to_copy, custom_knot_curves, memory);
1510
1511 Array<int> dst_knot_offsets_data(custom_knot_curves_to_copy.size() + 1, 0);
1512
1513 const OffsetIndices<int> src_knots_by_curve = curves.nurbs_custom_knots_by_curve();
1515 src_knots_by_curve, custom_knot_curves_to_copy, dst_knot_offsets_data);
1516
1517 dst_curves.nurbs_custom_knots_update_size();
1518 array_utils::gather_group_to_group(src_knots_by_curve,
1519 dst_knots_by_curve,
1520 custom_knot_curves_to_copy,
1521 curves.nurbs_custom_knots(),
1522 dst_curves.nurbs_custom_knots_for_write());
1523}
1524
1526 const IndexMask &curves_to_copy,
1527 const AttributeFilter &attribute_filter)
1528{
1529 const OffsetIndices points_by_curve = curves.points_by_curve();
1530 CurvesGeometry dst_curves(0, curves_to_copy.size());
1531 const OffsetIndices dst_points_by_curve = offset_indices::gather_selected_offsets(
1532 points_by_curve, curves_to_copy, dst_curves.offsets_for_write());
1533 dst_curves.resize(dst_points_by_curve.total_size(), dst_curves.curves_num());
1534
1535 BKE_defgroup_copy_list(&dst_curves.vertex_group_names, &curves.vertex_group_names);
1536
1537 const AttributeAccessor src_attributes = curves.attributes();
1538 MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
1539
1540 gather_attributes_group_to_group(src_attributes,
1543 attribute_filter,
1544 points_by_curve,
1545 dst_points_by_curve,
1546 curves_to_copy,
1547 dst_attributes);
1548
1549 gather_attributes(src_attributes,
1552 attribute_filter,
1553 curves_to_copy,
1554 dst_attributes);
1555
1556 if (curves.nurbs_has_custom_knots()) {
1557 copy_curve_selection_custom_knots(curves, curves_to_copy, dst_curves);
1558 }
1559
1560 dst_curves.update_curve_types();
1562
1563 return dst_curves;
1564}
1565
1566void CurvesGeometry::remove_curves(const IndexMask &curves_to_delete,
1567 const AttributeFilter &attribute_filter)
1568{
1569 if (curves_to_delete.is_empty()) {
1570 return;
1571 }
1572 if (curves_to_delete.size() == this->curves_num()) {
1573 this->resize(0, 0);
1574 this->update_curve_types();
1575 return;
1576 }
1577 IndexMaskMemory memory;
1578 const IndexMask curves_to_copy = curves_to_delete.complement(this->curves_range(), memory);
1579 *this = curves_copy_curve_selection(*this, curves_to_copy, attribute_filter);
1580}
1581
1583{
1584 const float last = custom_knots.last();
1585 custom_knots.reverse();
1586 for (float &knot_value : custom_knots) {
1587 knot_value = last - knot_value;
1588 }
1589}
1590
1591template<typename T>
1593 const IndexMask &curve_selection,
1595{
1596 const OffsetIndices points_by_curve = curves.points_by_curve();
1597 curve_selection.foreach_index(
1598 GrainSize(256), [&](const int curve_i) { data.slice(points_by_curve[curve_i]).reverse(); });
1599}
1600
1601template<typename T>
1603 const IndexMask &curve_selection,
1604 MutableSpan<T> data_a,
1605 MutableSpan<T> data_b)
1606{
1607 const OffsetIndices points_by_curve = curves.points_by_curve();
1608 curve_selection.foreach_index(GrainSize(256), [&](const int curve_i) {
1609 const IndexRange points = points_by_curve[curve_i];
1610 MutableSpan<T> a = data_a.slice(points);
1611 MutableSpan<T> b = data_b.slice(points);
1612 for (const int i : IndexRange(points.size() / 2)) {
1613 const int end_index = points.size() - 1 - i;
1614 std::swap(a[end_index], b[i]);
1615 std::swap(b[end_index], a[i]);
1616 }
1617 if (points.size() % 2) {
1618 const int64_t middle_index = points.size() / 2;
1619 std::swap(a[middle_index], b[middle_index]);
1620 }
1621 });
1622}
1623
1624void CurvesGeometry::reverse_curves(const IndexMask &curves_to_reverse)
1625{
1626 Set<StringRef> bezier_handle_names{{ATTR_HANDLE_POSITION_LEFT,
1630
1632
1633 attributes.foreach_attribute([&](const AttributeIter &iter) {
1634 if (iter.domain != AttrDomain::Point) {
1635 return;
1636 }
1637 if (iter.data_type == CD_PROP_STRING) {
1638 return;
1639 }
1640 if (bezier_handle_names.contains(iter.name)) {
1641 return;
1642 }
1643
1644 GSpanAttributeWriter attribute = attributes.lookup_for_write_span(iter.name);
1645 attribute_math::convert_to_static_type(attribute.span.type(), [&](auto dummy) {
1646 using T = decltype(dummy);
1647 reverse_curve_point_data<T>(*this, curves_to_reverse, attribute.span.typed<T>());
1648 });
1649 attribute.finish();
1650 return;
1651 });
1652
1653 if (this->nurbs_has_custom_knots()) {
1654 const OffsetIndices custom_knots_by_curve = this->nurbs_custom_knots_by_curve();
1656 curves_to_reverse.foreach_index(GrainSize(256), [&](const int64_t curve) {
1657 const IndexRange curve_knots = custom_knots_by_curve[curve];
1658 if (!custom_knots.is_empty()) {
1659 reverse_custom_knots(custom_knots.slice(curve_knots));
1660 }
1661 });
1662 }
1663
1664 /* In order to maintain the shape of Bezier curves, handle attributes must reverse, but also the
1665 * values for the left and right must swap. Use a utility to swap and reverse at the same time,
1666 * to avoid loading the attribute twice. Generally we can expect the right layer to exist when
1667 * the left does, but there's no need to count on it, so check for both attributes. */
1668
1669 if (attributes.contains(ATTR_HANDLE_POSITION_LEFT) &&
1671 {
1673 curves_to_reverse,
1676 }
1679 curves_to_reverse,
1682 }
1683
1684 this->tag_topology_changed();
1685}
1686
1687void CurvesGeometry::remove_attributes_based_on_types()
1688{
1695 }
1700 }
1703 }
1704}
1705
1706CurvesGeometry curves_new_no_attributes(int point_num, int curve_num)
1707{
1708 CurvesGeometry curves(0, curve_num);
1709 curves.point_num = point_num;
1710 CustomData_free_layer_named(&curves.point_data, "position");
1711 return curves;
1712}
1713
1715
1716/* -------------------------------------------------------------------- */
1719
1727template<typename T>
1729 const VArray<T> &old_values,
1730 MutableSpan<T> r_values)
1731{
1732 attribute_math::DefaultMixer<T> mixer(r_values);
1733
1734 const OffsetIndices points_by_curve = curves.points_by_curve();
1735 threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) {
1736 for (const int i_curve : range) {
1737 for (const int i_point : points_by_curve[i_curve]) {
1738 mixer.mix_in(i_curve, old_values[i_point]);
1739 }
1740 }
1741 mixer.finalize(range);
1742 });
1743}
1744
1752template<>
1754 const VArray<bool> &old_values,
1755 MutableSpan<bool> r_values)
1756{
1757 const OffsetIndices points_by_curve = curves.points_by_curve();
1758 r_values.fill(true);
1759 for (const int i_curve : IndexRange(curves.curves_num())) {
1760 for (const int i_point : points_by_curve[i_curve]) {
1761 if (!old_values[i_point]) {
1762 r_values[i_curve] = false;
1763 break;
1764 }
1765 }
1766 }
1767}
1768
1770 const GVArray &varray)
1771{
1772 GVArray new_varray;
1773 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
1774 using T = decltype(dummy);
1775 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
1776 Array<T> values(curves.curves_num());
1777 adapt_curve_domain_point_to_curve_impl<T>(curves, varray.typed<T>(), values);
1778 new_varray = VArray<T>::ForContainer(std::move(values));
1779 }
1780 });
1781 return new_varray;
1782}
1783
1791template<typename T>
1793 const VArray<T> &old_values,
1794 MutableSpan<T> r_values)
1795{
1796 const OffsetIndices points_by_curve = curves.points_by_curve();
1797 for (const int i_curve : IndexRange(curves.curves_num())) {
1798 r_values.slice(points_by_curve[i_curve]).fill(old_values[i_curve]);
1799 }
1800}
1801
1803 const GVArray &varray)
1804{
1805 GVArray new_varray;
1806 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
1807 using T = decltype(dummy);
1808 Array<T> values(curves.points_num());
1809 adapt_curve_domain_curve_to_point_impl<T>(curves, varray.typed<T>(), values);
1810 new_varray = VArray<T>::ForContainer(std::move(values));
1811 });
1812 return new_varray;
1813}
1814
1815GVArray CurvesGeometry::adapt_domain(const GVArray &varray,
1816 const AttrDomain from,
1817 const AttrDomain to) const
1818{
1819 if (!varray) {
1820 return {};
1821 }
1822 if (varray.is_empty()) {
1823 return {};
1824 }
1825 if (from == to) {
1826 return varray;
1827 }
1828 if (varray.is_single()) {
1829 BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), value);
1830 varray.get_internal_single(value);
1831 return GVArray::ForSingle(varray.type(), this->attributes().domain_size(to), value);
1832 }
1833
1834 if (from == AttrDomain::Point && to == AttrDomain::Curve) {
1835 return adapt_curve_domain_point_to_curve(*this, varray);
1836 }
1837 if (from == AttrDomain::Curve && to == AttrDomain::Point) {
1838 return adapt_curve_domain_curve_to_point(*this, varray);
1839 }
1840
1842 return {};
1843}
1844
1845AttributeAccessor CurvesGeometry::attributes() const
1846{
1848}
1849
1850MutableAttributeAccessor CurvesGeometry::attributes_for_write()
1851{
1853}
1854
1856
1857/* -------------------------------------------------------------------- */
1860
1861void CurvesGeometry::blend_read(BlendDataReader &reader)
1862{
1863 this->runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__);
1864
1865 CustomData_blend_read(&reader, &this->point_data, this->point_num);
1866 CustomData_blend_read(&reader, &this->curve_data, this->curve_num);
1867 this->attribute_storage.wrap().blend_read(reader);
1868
1869 if (this->curve_offsets) {
1871 &reader, &this->curve_offsets, [&]() {
1872 BLO_read_int32_array(&reader, this->curve_num + 1, &this->curve_offsets);
1874 });
1875 }
1876
1877 /* Forward compatibility. To be removed when runtime format changes. */
1879
1881
1882 if (this->custom_knot_num) {
1884 &reader, &this->custom_knots, [&]() {
1885 BLO_read_float_array(&reader, this->custom_knot_num, &this->custom_knots);
1886 return implicit_sharing::info_for_mem_free(this->custom_knots);
1887 });
1888 }
1889
1890 /* Recalculate curve type count cache that isn't saved in files. */
1891 this->update_curve_types();
1892}
1893
1894CurvesGeometry::BlendWriteData::BlendWriteData(ResourceScope &scope)
1895 : scope(scope),
1896 point_layers(scope.construct<Vector<CustomDataLayer, 16>>()),
1897 curve_layers(scope.construct<Vector<CustomDataLayer, 16>>()),
1899{
1900}
1901
1902void CurvesGeometry::blend_write_prepare(CurvesGeometry::BlendWriteData &write_data)
1903{
1905 {{AttrDomain::Point, &write_data.point_layers},
1906 {AttrDomain::Curve, &write_data.curve_layers}},
1907 write_data.attribute_data);
1910 this->points_num(),
1911 write_data.point_layers,
1912 write_data.attribute_data);
1915 this->curves_num(),
1916 write_data.curve_layers,
1917 write_data.attribute_data);
1918 if (write_data.attribute_data.attributes.is_empty()) {
1919 this->attribute_storage.dna_attributes = nullptr;
1921 }
1922 else {
1923 this->attribute_storage.dna_attributes = write_data.attribute_data.attributes.data();
1924 this->attribute_storage.dna_attributes_num = write_data.attribute_data.attributes.size();
1925 }
1926}
1927
1928void CurvesGeometry::blend_write(BlendWriter &writer,
1929 ID &id,
1930 const CurvesGeometry::BlendWriteData &write_data)
1931{
1933 &writer, &this->point_data, write_data.point_layers, this->point_num, CD_MASK_ALL, &id);
1935 &writer, &this->curve_data, write_data.curve_layers, this->curve_num, CD_MASK_ALL, &id);
1936 this->attribute_storage.wrap().blend_write(writer, write_data.attribute_data);
1937
1938 if (this->curve_offsets) {
1940 &writer,
1941 this->curve_offsets,
1942 sizeof(int) * (this->curve_num + 1),
1943 this->runtime->curve_offsets_sharing_info,
1944 [&]() { BLO_write_int32_array(&writer, this->curve_num + 1, this->curve_offsets); });
1945 }
1946
1948
1949 if (this->custom_knot_num) {
1951 &writer,
1952 this->custom_knots,
1953 sizeof(float) * this->custom_knot_num,
1954 this->runtime->custom_knots_sharing_info,
1955 [&]() { BLO_write_float_array(&writer, this->custom_knot_num, this->custom_knots); });
1956 }
1957}
1958
1960
1961} // 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::bke::AttrDomain domain, int domain_size, blender::Vector< CustomDataLayer, 16 > &layers_to_write, blender::bke::AttributeStorage::BlendWriteData &write_data)
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)
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_free(CustomData *data)
void CustomData_init_from(const CustomData *source, CustomData *dest, eCustomDataMask mask, 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)
bool CustomData_free_layer_named(CustomData *data, blender::StringRef name)
support for deformation groups and hooks.
void BKE_defbase_blend_write(BlendWriter *writer, const ListBase *defbase)
Definition deform.cc:1632
void BKE_defgroup_copy_list(ListBase *outbase, const ListBase *inbase)
Definition deform.cc:71
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
ATTR_WARN_UNUSED_RESULT const size_t num
#define UNLIKELY(x)
void BLO_read_float_array(BlendDataReader *reader, int64_t array_size, float **ptr_p)
Definition readfile.cc:5326
void BLO_read_int32_array(BlendDataReader *reader, int64_t array_size, int32_t **ptr_p)
Definition readfile.cc:5306
#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
@ NURBS_KNOT_MODE_CUSTOM
@ CD_MDEFORMVERT
@ CD_PROP_STRING
#define MAXMAT
Read Guarded memory(de)allocation.
BMesh const char void * data
long long int int64_t
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
constexpr Span slice_safe(const int64_t start, const int64_t size) const
Definition BLI_span.hh:154
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
static const CPPType & get()
void copy_from(GSpan values)
GMutableSpan slice(const int64_t start, int64_t size) const
const CPPType & type() 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)
static IndexMask from_intersection(const IndexMask &mask_a, const IndexMask &mask_b, IndexMaskMemory &memory)
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
static constexpr IndexRange from_begin_size(const int64_t begin, const int64_t size)
constexpr int64_t start() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr MutableSpan drop_back(const int64_t n) const
Definition BLI_span.hh:618
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr void reverse() const
Definition BLI_span.hh:650
constexpr T & first() const
Definition BLI_span.hh:679
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:689
bool contains(const Key &key) const
Definition BLI_set.hh:310
void ensure(FunctionRef< void(T &data)> compute_cache)
const T & data() const
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr bool is_empty() const
Definition BLI_span.hh:260
static VArray ForSingle(T value, const int64_t size)
static VArray ForSpan(Span< T > values)
void append(const T &value)
const T & last(const int64_t n=0) const
void resize(const int64_t new_size)
MutableSpan< T > as_mutable_span()
void reserve(const int64_t min_capacity)
void extend(Span< T > array)
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:97
SharedCache< Vector< float > > evaluated_length_cache
SharedCache< Bounds< float3 > > bounds_with_radius_cache
SharedCache< Vector< int > > custom_knot_offsets_cache
SharedCache< Vector< float3 > > evaluated_tangent_cache
SharedCache< EvaluatedOffsets > evaluated_offsets_cache
Definition BKE_curves.hh:95
SharedCache< Vector< float3 > > evaluated_normal_cache
const ImplicitSharingInfo * custom_knots_sharing_info
Definition BKE_curves.hh:79
SharedCache< std::optional< int > > max_material_index_cache
SharedCache< Bounds< float3 > > bounds_cache
SharedCache< Vector< float3 > > evaluated_position_cache
const ImplicitSharingInfo * curve_offsets_sharing_info
Definition BKE_curves.hh:76
VArray< int8_t > handle_types_left() const
MutableSpan< float3 > positions_for_write()
MutableSpan< float > nurbs_custom_knots_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
void ensure_can_interpolate_to_evaluated() const
IndexRange curves_range() const
MutableSpan< int8_t > curve_types_for_write()
const std::array< int, CURVE_TYPES_NUM > & curve_type_counts() const
MutableSpan< float3 > handle_positions_left_for_write()
MutableAttributeAccessor attributes_for_write()
MutableSpan< float3 > handle_positions_right_for_write()
VArray< float > radius() const
VArray< float > tilt() const
Span< float3 > evaluated_tangents() const
Span< float > nurbs_weights() const
Span< float3 > handle_positions_left() const
VArray< int > resolution() const
void interpolate_to_evaluated(int curve_index, GSpan src, GMutableSpan dst) const
IndexRange points_range() const
VArray< int8_t > nurbs_knots_modes() 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)
AttributeAccessor attributes() const
IndexMask indices_for_curve_type(CurveType type, IndexMaskMemory &memory) const
bool is_single_type(CurveType type) const
MutableSpan< int > offsets_for_write()
std::optional< Bounds< float3 > > bounds_min_max(bool use_radius=true) const
bool nurbs_has_custom_knots() const
OffsetIndices< int > nurbs_custom_knots_by_curve() const
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)
static bool is_cyclic(const Nurb *nu)
#define this
#define CD_MASK_ALL
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:133
#define T
void copy_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
void gather_group_to_group(const OffsetIndices< int > src_offsets, const OffsetIndices< int > dst_offsets, const IndexMask &selection, const Span< T > src, MutableSpan< T > 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)
int knots_num(int points_num, int8_t order, bool cyclic)
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:54
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 foreach_selected_point_ranges_per_curve(const IndexMask &mask, const OffsetIndices< int > points_by_curve, SelectedCallback selected_fn)
const AttributeAccessorFunctions & get_attribute_accessor_functions()
IndexMask curve_to_point_selection(OffsetIndices< int > points_by_curve, const IndexMask &curve_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)
static void translate_positions(MutableSpan< float3 > positions, const float3 &translation)
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)
constexpr StringRef ATTR_RADIUS
constexpr StringRef ATTR_RESOLUTION
static MutableSpan< T > get_mutable_attribute(CurvesGeometry &curves, const AttrDomain domain, const StringRef name, const T default_value=T())
static void copy_point_selection_custom_knots(const CurvesGeometry &curves, const IndexMask &points_to_copy, const Span< int > curve_point_counts, CurvesGeometry &dst_curves)
static VArray< T > get_varray_attribute(const CurvesGeometry &curves, const AttrDomain domain, const StringRef name, const T default_value)
constexpr StringRef ATTR_CURVE_TYPE
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
static GVArray adapt_curve_domain_point_to_curve(const CurvesGeometry &curves, const GVArray &varray)
constexpr StringRef ATTR_CYCLIC
void gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
CurvesGeometry curves_new_no_attributes(int point_num, int curve_num)
constexpr StringRef ATTR_SURFACE_UV_COORDINATE
constexpr StringRef ATTR_HANDLE_POSITION_RIGHT
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 reverse_custom_knots(MutableSpan< float > custom_knots)
constexpr StringRef ATTR_NORMAL_MODE
constexpr StringRef ATTR_TILT
constexpr StringRef ATTR_HANDLE_POSITION_LEFT
void transform_custom_normal_attribute(const float4x4 &transform, MutableAttributeAccessor &attributes)
static Span< T > get_span_attribute(const CurvesGeometry &curves, const AttrDomain domain, const StringRef name)
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)
constexpr StringRef ATTR_HANDLE_TYPE_LEFT
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 void reverse_swap_curve_point_data(const CurvesGeometry &curves, const IndexMask &curve_selection, MutableSpan< T > data_a, MutableSpan< T > data_b)
constexpr StringRef ATTR_NURBS_KNOTS_MODE
constexpr StringRef ATTR_POSITION
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)
constexpr StringRef ATTR_NURBS_ORDER
constexpr StringRef ATTR_NURBS_WEIGHT
constexpr StringRef ATTR_HANDLE_TYPE_RIGHT
void curves_convert_storage_to_customdata(CurvesGeometry &curves)
static void rotate_directions_around_axes(MutableSpan< float3 > directions, const Span< float3 > axes, const Span< float > angles)
static void copy_curve_selection_custom_knots(const CurvesGeometry &curves, const IndexMask &curves_to_copy, CurvesGeometry &dst_curves)
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)
void attribute_storage_blend_write_prepare(AttributeStorage &data, const Map< AttrDomain, Vector< CustomDataLayer, 16 > * > &layers_to_write, AttributeStorage::BlendWriteData &write_data)
std::optional< Bounds< T > > min_max_with_radii(const Span< T > values, const Span< RadiusT > radii)
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
std::optional< T > max(const VArray< T > &values)
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)
bool is_zero(const T &a)
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:221
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:93
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:151
MatBase< float, 4, 4 > float4x4
VecBase< float, 3 > float3
static void init(bNodeTree *, bNode *node)
static Span< T > get_span_attribute(const PointCloud &pointcloud, const StringRef name)
static VArray< T > get_varray_attribute(const PointCloud &pointcloud, const StringRef name, const T default_value)
static MutableSpan< T > get_mutable_attribute(PointCloud &pointcloud, const StringRef name, const T default_value=T())
struct Attribute * dna_attributes
CustomData point_data
CurvesGeometryRuntimeHandle * runtime
ListBase vertex_group_names
CustomData curve_data
struct AttributeStorage attribute_storage
Definition DNA_ID.h:404
void pad(const PaddingT &padding)
Vector< CustomDataLayer, 16 > & curve_layers
Vector< CustomDataLayer, 16 > & point_layers
AttributeStorage::BlendWriteData attribute_data
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
i
Definition text_draw.cc:230