Blender V5.0
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#include "DNA_object_types.h"
29
30#include "BKE_attribute.hh"
32#include "BKE_attribute_math.hh"
36#include "BKE_curves.hh"
37#include "BKE_curves_utils.hh"
38#include "BKE_customdata.hh"
39#include "BKE_deform.hh"
40
42
43namespace blender::bke {
44
45constexpr StringRef ATTR_POSITION = "position";
46constexpr StringRef ATTR_RADIUS = "radius";
47constexpr StringRef ATTR_TILT = "tilt";
48constexpr StringRef ATTR_CURVE_TYPE = "curve_type";
49constexpr StringRef ATTR_CYCLIC = "cyclic";
50constexpr StringRef ATTR_RESOLUTION = "resolution";
51constexpr StringRef ATTR_NORMAL_MODE = "normal_mode";
52constexpr StringRef ATTR_HANDLE_TYPE_LEFT = "handle_type_left";
53constexpr StringRef ATTR_HANDLE_TYPE_RIGHT = "handle_type_right";
54constexpr StringRef ATTR_HANDLE_POSITION_LEFT = "handle_left";
55constexpr StringRef ATTR_HANDLE_POSITION_RIGHT = "handle_right";
56constexpr StringRef ATTR_NURBS_ORDER = "nurbs_order";
57constexpr StringRef ATTR_NURBS_WEIGHT = "nurbs_weight";
58constexpr StringRef ATTR_NURBS_KNOTS_MODE = "knots_mode";
59constexpr StringRef ATTR_SURFACE_UV_COORDINATE = "surface_uv_coordinate";
60
61/* -------------------------------------------------------------------- */
64
65CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0) {}
66
67CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
68{
69 this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
70
71 this->point_num = point_num;
72 this->curve_num = curve_num;
77
80
81 this->custom_knots = nullptr;
82 this->custom_knot_num = 0;
83
84 if (curve_num > 0) {
85 this->curve_offsets = MEM_malloc_arrayN<int>(size_t(this->curve_num) + 1, __func__);
86 this->runtime->curve_offsets_sharing_info = implicit_sharing::info_for_mem_free(
87 this->curve_offsets);
88#ifndef NDEBUG
89 this->offsets_for_write().fill(-1);
90#endif
91 /* Set common values for convenience. */
92 this->curve_offsets[0] = 0;
93 this->curve_offsets[this->curve_num] = this->point_num;
94 }
95 else {
96 this->curve_offsets = nullptr;
97 }
98
99 /* Fill the type counts with the default so they're in a valid state. */
100 this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_num;
101}
102
103CurvesGeometry::CurvesGeometry(const CurvesGeometry &other)
104{
105 this->curve_offsets = other.curve_offsets;
106 if (other.runtime->curve_offsets_sharing_info) {
107 other.runtime->curve_offsets_sharing_info->add_user();
108 }
109
110 this->custom_knots = other.custom_knots;
111 this->custom_knot_num = other.custom_knot_num;
112 if (other.runtime->custom_knots_sharing_info) {
113 other.runtime->custom_knots_sharing_info->add_user();
114 }
115
116 CustomData_init_from(&other.point_data, &this->point_data, CD_MASK_MDEFORMVERT, other.point_num);
117
118 new (&this->attribute_storage.wrap()) AttributeStorage(other.attribute_storage.wrap());
119
120 this->point_num = other.point_num;
121 this->curve_num = other.curve_num;
122
125
127
128 this->runtime = MEM_new<CurvesGeometryRuntime>(
129 __func__,
130 CurvesGeometryRuntime{other.runtime->curve_offsets_sharing_info,
131 other.runtime->custom_knots_sharing_info,
132 other.runtime->type_counts,
133 other.runtime->evaluated_offsets_cache,
134 other.runtime->has_cyclic_curve_cache,
135 other.runtime->nurbs_basis_cache,
136 other.runtime->evaluated_position_cache,
137 other.runtime->bounds_cache,
138 other.runtime->bounds_with_radius_cache,
139 other.runtime->evaluated_length_cache,
140 other.runtime->evaluated_tangent_cache,
141 other.runtime->evaluated_normal_cache,
142 other.runtime->max_material_index_cache,
143 other.runtime->custom_knot_offsets_cache,
144 {},
145 true});
146
147 if (other.runtime->bake_materials) {
148 this->runtime->bake_materials = std::make_unique<bake::BakeMaterialsList>(
149 *other.runtime->bake_materials);
150 }
151}
152
153CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other)
154{
155 if (this == &other) {
156 return *this;
157 }
158 std::destroy_at(this);
159 new (this) CurvesGeometry(other);
160 return *this;
161}
162
163CurvesGeometry::CurvesGeometry(CurvesGeometry &&other)
164{
165 this->curve_offsets = other.curve_offsets;
166 other.curve_offsets = nullptr;
167
168 this->custom_knots = other.custom_knots;
169 other.custom_knots = nullptr;
170
171 this->custom_knot_num = other.custom_knot_num;
172 other.custom_knot_num = 0;
173
174 this->point_data = other.point_data;
175 CustomData_reset(&other.point_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{
212 this->attribute_storage.wrap().~AttributeStorage();
214 if (this->runtime) {
216 &this->runtime->curve_offsets_sharing_info);
218 &this->runtime->custom_knots_sharing_info);
219 MEM_delete(this->runtime);
220 }
221}
222
224
225/* -------------------------------------------------------------------- */
228
229VArray<int8_t> CurvesGeometry::curve_types() const
230{
234 this->curves_num(),
236}
237
238MutableSpan<int8_t> CurvesGeometry::curve_types_for_write()
239{
241 this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_CURVE_TYPE, this->curves_num());
242}
243
244void CurvesGeometry::fill_curve_types(const CurveType type)
245{
246 if (type == CURVE_TYPE_CATMULL_ROM) {
247 /* Avoid creating the attribute for Catmull Rom which is the default when the attribute doesn't
248 * exist anyway. */
249 this->attributes_for_write().remove("curve_type");
250 }
251 else {
252 this->curve_types_for_write().fill(type);
253 }
254 this->runtime->type_counts.fill(0);
255 this->runtime->type_counts[type] = this->curves_num();
256 this->tag_topology_changed();
257}
258
259void CurvesGeometry::fill_curve_types(const IndexMask &selection, const CurveType type)
260{
261 if (selection.size() == this->curves_num()) {
262 this->fill_curve_types(type);
263 return;
264 }
265 if (std::optional<int8_t> single_type = this->curve_types().get_if_single()) {
266 if (single_type == type) {
267 this->fill_curve_types(type);
268 return;
269 }
270 }
271 /* A potential performance optimization is only counting the changed indices. */
273 this->update_curve_types();
274 this->tag_topology_changed();
275}
276
277std::array<int, CURVE_TYPES_NUM> calculate_type_counts(const VArray<int8_t> &types)
278{
279 using CountsType = std::array<int, CURVE_TYPES_NUM>;
280 CountsType counts;
281 counts.fill(0);
282
283 if (types.is_single()) {
284 counts[types.get_internal_single()] = types.size();
285 return counts;
286 }
287
288 Span<int8_t> types_span = types.get_internal_span();
290 types.index_range(),
291 2048,
292 counts,
293 [&](const IndexRange curves_range, const CountsType &init) {
294 CountsType result = init;
295 for (const int curve_index : curves_range) {
296 result[types_span[curve_index]]++;
297 }
298 return result;
299 },
300 [](const CountsType &a, const CountsType &b) {
301 CountsType result = a;
302 for (const int i : IndexRange(CURVE_TYPES_NUM)) {
303 result[i] += b[i];
304 }
305 return result;
306 });
307}
308
309void CurvesGeometry::update_curve_types()
310{
311 this->runtime->type_counts = calculate_type_counts(this->curve_types());
312}
313
314Span<float3> CurvesGeometry::positions() const
315{
317 this->attribute_storage.wrap(), AttrDomain::Point, ATTR_POSITION, this->points_num());
318}
319MutableSpan<float3> CurvesGeometry::positions_for_write()
320{
322 this->attribute_storage.wrap(), AttrDomain::Point, ATTR_POSITION, this->points_num());
323}
324
325VArray<float> CurvesGeometry::radius() const
326{
328 this->attribute_storage.wrap(), AttrDomain::Point, ATTR_RADIUS, this->points_num(), 0.01f);
329}
330MutableSpan<float> CurvesGeometry::radius_for_write()
331{
333 this->attribute_storage.wrap(), AttrDomain::Point, ATTR_RADIUS, this->points_num(), 0.01f);
334}
335
336Span<int> CurvesGeometry::offsets() const
337{
338 if (this->curve_num == 0) {
339 return {};
340 }
341 return {this->curve_offsets, this->curve_num + 1};
342}
343MutableSpan<int> CurvesGeometry::offsets_for_write()
344{
345 if (this->curve_num == 0) {
346 return {};
347 }
349 &this->curve_offsets, &this->runtime->curve_offsets_sharing_info, this->curve_num + 1);
350 return {this->curve_offsets, this->curve_num + 1};
351}
352
353VArray<bool> CurvesGeometry::cyclic() const
354{
356 this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_CYCLIC, this->curves_num(), false);
357}
358MutableSpan<bool> CurvesGeometry::cyclic_for_write()
359{
361 this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_CYCLIC, this->curves_num(), false);
362}
363
364VArray<int> CurvesGeometry::resolution() const
365{
367 this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_RESOLUTION, this->curves_num(), 12);
368}
369MutableSpan<int> CurvesGeometry::resolution_for_write()
370{
372 this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_RESOLUTION, this->curves_num(), 12);
373}
374
375VArray<int8_t> CurvesGeometry::normal_mode() const
376{
378 this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_NORMAL_MODE, this->curves_num(), 0);
379}
380MutableSpan<int8_t> CurvesGeometry::normal_mode_for_write()
381{
383 this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_NORMAL_MODE, this->curves_num());
384}
385
386VArray<float> CurvesGeometry::tilt() const
387{
389 this->attribute_storage.wrap(), AttrDomain::Point, ATTR_TILT, this->points_num(), 0.0f);
390}
391MutableSpan<float> CurvesGeometry::tilt_for_write()
392{
394 this->attribute_storage.wrap(), AttrDomain::Point, ATTR_TILT, this->points_num());
395}
396
397VArray<int8_t> CurvesGeometry::handle_types_left() const
398{
402 this->points_num(),
403 0);
404}
405MutableSpan<int8_t> CurvesGeometry::handle_types_left_for_write()
406{
410 this->points_num(),
411 0);
412}
413
414VArray<int8_t> CurvesGeometry::handle_types_right() const
415{
419 this->points_num(),
420 0);
421}
422MutableSpan<int8_t> CurvesGeometry::handle_types_right_for_write()
423{
427 this->points_num(),
428 0);
429}
430
431std::optional<Span<float3>> CurvesGeometry::handle_positions_left() const
432{
436 this->points_num());
437}
438MutableSpan<float3> CurvesGeometry::handle_positions_left_for_write()
439{
443 this->points_num());
444}
445
446std::optional<Span<float3>> CurvesGeometry::handle_positions_right() const
447{
451 this->points_num());
452}
453MutableSpan<float3> CurvesGeometry::handle_positions_right_for_write()
454{
458 this->points_num());
459}
460
461VArray<int8_t> CurvesGeometry::nurbs_orders() const
462{
464 this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_NURBS_ORDER, this->curves_num(), 4);
465}
466MutableSpan<int8_t> CurvesGeometry::nurbs_orders_for_write()
467{
469 this->attribute_storage.wrap(), AttrDomain::Curve, ATTR_NURBS_ORDER, this->curves_num(), 4);
470}
471
472std::optional<Span<float>> CurvesGeometry::nurbs_weights() const
473{
475 this->attribute_storage.wrap(), AttrDomain::Point, ATTR_NURBS_WEIGHT, this->points_num());
476}
477MutableSpan<float> CurvesGeometry::nurbs_weights_for_write()
478{
482 this->points_num(),
483 1.0f);
484}
485
486VArray<int8_t> CurvesGeometry::nurbs_knots_modes() const
487{
491 this->curves_num(),
492 0);
493}
494MutableSpan<int8_t> CurvesGeometry::nurbs_knots_modes_for_write()
495{
499 this->curves_num(),
500 0);
501}
502
503std::optional<Span<float2>> CurvesGeometry::surface_uv_coords() const
504{
508 this->curves_num());
509}
510
511MutableSpan<float2> CurvesGeometry::surface_uv_coords_for_write()
512{
516 this->curves_num());
517}
518
519Span<float> CurvesGeometry::nurbs_custom_knots() const
520{
521 if (this->custom_knot_num == 0) {
522 return {};
523 }
524 return {this->custom_knots, this->custom_knot_num};
525}
526
527MutableSpan<float> CurvesGeometry::nurbs_custom_knots_for_write()
528{
529 if (this->custom_knot_num == 0) {
530 return {};
531 }
533 &this->custom_knots, &this->runtime->custom_knots_sharing_info, this->custom_knot_num);
534 return {this->custom_knots, this->custom_knot_num};
535}
536
537IndexMask CurvesGeometry::nurbs_custom_knot_curves(IndexMaskMemory &memory) const
538{
539 const VArray<int8_t> curve_types = this->curve_types();
540 const VArray<int8_t> knot_modes = this->nurbs_knots_modes();
542 this->curves_range(), GrainSize(4096), memory, [&](const int64_t curve) {
543 return curve_types[curve] == CURVE_TYPE_NURBS &&
544 knot_modes[curve] == NURBS_KNOT_MODE_CUSTOM;
545 });
546}
547
548OffsetIndices<int> CurvesGeometry::nurbs_custom_knots_by_curve() const
549{
550 const CurvesGeometryRuntime &runtime = *this->runtime;
552 return {};
553 }
554 runtime.custom_knot_offsets_cache.ensure([&](Vector<int> &r_data) {
555 r_data.resize(this->curve_num + 1);
556
557 const OffsetIndices<int> points_by_curve = this->points_by_curve();
558 const VArray<int8_t> curve_types = this->curve_types();
559 const VArray<int8_t> knot_modes = this->nurbs_knots_modes();
560 const VArray<int8_t> orders = this->nurbs_orders();
561
562 threading::parallel_for(this->curves_range(), 1024, [&](const IndexRange range) {
563 for (const int curve : range) {
564 if (curve_types[curve] != CURVE_TYPE_NURBS) {
565 r_data[curve] = 0;
566 continue;
567 }
568 if (knot_modes[curve] != NURBS_KNOT_MODE_CUSTOM) {
569 r_data[curve] = 0;
570 continue;
571 }
572 r_data[curve] = points_by_curve[curve].size() + orders[curve];
573 }
574 });
576 });
577 return OffsetIndices<int>(runtime.custom_knot_offsets_cache.data());
578}
579
580void CurvesGeometry::nurbs_custom_knots_update_size()
581{
582 this->runtime->custom_knot_offsets_cache.tag_dirty();
583 const OffsetIndices<int> knots_by_curve = this->nurbs_custom_knots_by_curve();
584 const int knots_num = knots_by_curve.total_size();
585 if (this->custom_knot_num != knots_num) {
587 &this->runtime->custom_knots_sharing_info,
588 this->custom_knot_num,
589 knots_num);
590 this->custom_knot_num = knots_num;
591 }
592}
593
594void CurvesGeometry::nurbs_custom_knots_resize(int knots_num)
595{
597 &this->runtime->custom_knots_sharing_info,
598 this->custom_knot_num,
599 knots_num);
600 this->custom_knot_num = knots_num;
601 this->runtime->custom_knot_offsets_cache.tag_dirty();
602}
603
604Span<MDeformVert> CurvesGeometry::deform_verts() const
605{
606 const MDeformVert *dverts = static_cast<const MDeformVert *>(
608 if (dverts == nullptr) {
609 return {};
610 }
611 return {dverts, this->point_num};
612}
613
614MutableSpan<MDeformVert> CurvesGeometry::deform_verts_for_write()
615{
616 MDeformVert *dvert = static_cast<MDeformVert *>(
618 if (dvert != nullptr) {
619 return {dvert, this->point_num};
620 }
621 return {static_cast<MDeformVert *>(CustomData_add_layer(
623 this->point_num};
624}
625
627
628/* -------------------------------------------------------------------- */
631
632template<typename CountFn> void build_offsets(MutableSpan<int> offsets, const CountFn &count_fn)
633{
634 int offset = 0;
635 for (const int i : offsets.drop_back(1).index_range()) {
636 offsets[i] = offset;
637 offset += count_fn(i);
638 }
639 offsets.last() = offset;
640}
641
643 MutableSpan<int> offsets,
644 MutableSpan<int> all_bezier_offsets)
645{
646 const OffsetIndices points_by_curve = curves.points_by_curve();
647 const VArray<int8_t> types = curves.curve_types();
648 const VArray<int> resolution = curves.resolution();
649 const VArray<bool> cyclic = curves.cyclic();
650
651 VArraySpan<int8_t> handle_types_left;
652 VArraySpan<int8_t> handle_types_right;
653 if (curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
654 handle_types_left = curves.handle_types_left();
655 handle_types_right = curves.handle_types_right();
656 }
657
658 const VArray<int8_t> nurbs_orders = curves.nurbs_orders();
659 const VArray<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes();
660 const OffsetIndices<int> custom_knots_by_curve = curves.nurbs_custom_knots_by_curve();
661 const Span<float> all_custom_knots = curves.nurbs_custom_knots();
662
663 build_offsets(offsets, [&](const int curve_index) -> int {
664 const IndexRange points = points_by_curve[curve_index];
665 switch (types[curve_index]) {
668 points.size(), cyclic[curve_index], resolution[curve_index]);
669 case CURVE_TYPE_POLY:
670 return points.size();
671 case CURVE_TYPE_BEZIER: {
672 const IndexRange offsets = curves::per_curve_point_offsets_range(points, curve_index);
673 curves::bezier::calculate_evaluated_offsets(handle_types_left.slice(points),
674 handle_types_right.slice(points),
675 cyclic[curve_index],
676 resolution[curve_index],
677 all_bezier_offsets.slice(offsets));
678 return all_bezier_offsets[offsets.last()];
679 }
680 case CURVE_TYPE_NURBS:
681 const bool is_cyclic = cyclic[curve_index];
682 const int8_t order = nurbs_orders[curve_index];
683 const KnotsMode knots_mode = KnotsMode(nurbs_knots_modes[curve_index]);
684 const IndexRange custom_knots_range = custom_knots_by_curve[curve_index];
685 const Span<float> custom_knots = knots_mode == NURBS_KNOT_MODE_CUSTOM &&
686 !all_custom_knots.is_empty() &&
687 !custom_knots_range.is_empty() ?
688 all_custom_knots.slice(custom_knots_range) :
689 Span<float>();
691 points.size(), order, is_cyclic, resolution[curve_index], knots_mode, custom_knots);
692 }
694 return 0;
695 });
696}
697
698OffsetIndices<int> CurvesGeometry::evaluated_points_by_curve() const
699{
700 const CurvesGeometryRuntime &runtime = *this->runtime;
701 if (this->is_single_type(CURVE_TYPE_POLY)) {
702 /* When all the curves are poly curves, the evaluated offsets are the same as the control
703 * point offsets, so it's possible to completely avoid building a new offsets array. */
704 runtime.evaluated_offsets_cache.ensure([&](CurvesGeometryRuntime::EvaluatedOffsets &r_data) {
707 });
708 return this->points_by_curve();
709 }
710
711 runtime.evaluated_offsets_cache.ensure([&](CurvesGeometryRuntime::EvaluatedOffsets &r_data) {
712 r_data.evaluated_offsets.resize(this->curves_num() + 1);
713
715 r_data.all_bezier_offsets.resize(this->points_num() + this->curves_num());
716 }
717 else {
719 }
720
722 });
723
724 return OffsetIndices<int>(runtime.evaluated_offsets_cache.data().evaluated_offsets);
725}
726
727bool CurvesGeometry::has_cyclic_curve() const
728{
729 this->runtime->has_cyclic_curve_cache.ensure([&](bool &r_data) {
730 r_data = array_utils::contains(this->cyclic(), this->curves_range(), true);
731 });
732 return this->runtime->has_cyclic_curve_cache.data();
733}
734
735IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
736 IndexMaskMemory &memory) const
737{
738 return this->indices_for_curve_type(type, this->curves_range(), memory);
739}
740
741IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
742 const IndexMask &selection,
743 IndexMaskMemory &memory) const
744{
746 this->curve_types(), this->curve_type_counts(), type, selection, memory);
747}
748
749Array<int> CurvesGeometry::point_to_curve_map() const
750{
751 Array<int> map(this->points_num());
753 return map;
754}
755
756void CurvesGeometry::ensure_nurbs_basis_cache() const
757{
758 const CurvesGeometryRuntime &runtime = *this->runtime;
759 runtime.nurbs_basis_cache.ensure([&](Vector<curves::nurbs::BasisCache> &r_data) {
760 IndexMaskMemory memory;
761 const IndexMask nurbs_mask = this->indices_for_curve_type(CURVE_TYPE_NURBS, memory);
762 if (nurbs_mask.is_empty()) {
763 r_data.clear_and_shrink();
764 return;
765 }
766
767 r_data.resize(this->curves_num());
768
769 const OffsetIndices<int> points_by_curve = this->points_by_curve();
770 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
771 const OffsetIndices<int> custom_knots_by_curve = this->nurbs_custom_knots_by_curve();
772 const VArray<bool> cyclic = this->cyclic();
773 const VArray<int8_t> orders = this->nurbs_orders();
774 const VArray<int> resolutions = this->resolution();
775 const VArray<int8_t> knots_modes = this->nurbs_knots_modes();
776 const Span<float> custom_knots = this->nurbs_custom_knots();
777
778 nurbs_mask.foreach_segment(GrainSize(64), [&](const IndexMaskSegment segment) {
779 Vector<float, 32> knots;
780 for (const int curve_index : segment) {
781 const IndexRange points = points_by_curve[curve_index];
782 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
783
784 const int8_t order = orders[curve_index];
785 const int resolution = resolutions[curve_index];
786 const bool is_cyclic = cyclic[curve_index];
787 const KnotsMode mode = KnotsMode(knots_modes[curve_index]);
788
789 if (!curves::nurbs::check_valid_eval_params(
790 points.size(), order, is_cyclic, mode, resolution))
791 {
792 r_data[curve_index].invalid = true;
793 continue;
794 }
795 const int knots_num = curves::nurbs::knots_num(points.size(), order, is_cyclic);
796 knots.reinitialize(knots_num);
797 curves::nurbs::load_curve_knots(mode,
798 points.size(),
799 order,
800 is_cyclic,
801 custom_knots_by_curve[curve_index],
802 custom_knots,
803 knots);
804
805 curves::nurbs::calculate_basis_cache(points.size(),
806 evaluated_points.size(),
807 order,
808 resolution,
809 is_cyclic,
810 mode,
811 knots,
812 r_data[curve_index]);
813 }
814 });
815 });
816}
817
818Span<float3> CurvesGeometry::evaluated_positions() const
819{
820 const CurvesGeometryRuntime &runtime = *this->runtime;
821 if (this->is_single_type(CURVE_TYPE_POLY)) {
822 runtime.evaluated_position_cache.ensure(
823 [&](Vector<float3> &r_data) { r_data.clear_and_shrink(); });
824 return this->positions();
825 }
826 this->ensure_nurbs_basis_cache();
827 runtime.evaluated_position_cache.ensure([&](Vector<float3> &r_data) {
828 r_data.resize(this->evaluated_points_num());
830
831 const OffsetIndices<int> points_by_curve = this->points_by_curve();
832 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
833 const Span<float3> positions = this->positions();
834
835 auto evaluate_catmull = [&](const IndexMask &selection) {
836 const VArray<bool> cyclic = this->cyclic();
837 const VArray<int> resolution = this->resolution();
838 selection.foreach_index(GrainSize(128), [&](const int curve_index) {
839 const IndexRange points = points_by_curve[curve_index];
840 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
842 cyclic[curve_index],
843 resolution[curve_index],
844 evaluated_positions.slice(evaluated_points));
845 });
846 };
847 auto evaluate_poly = [&](const IndexMask &selection) {
850 };
851 auto evaluate_bezier = [&](const IndexMask &selection) {
852 const std::optional<Span<float3>> handle_positions_left = this->handle_positions_left();
853 const std::optional<Span<float3>> handle_positions_right = this->handle_positions_right();
856 return;
857 }
858 const Span<int> all_bezier_offsets =
859 runtime.evaluated_offsets_cache.data().all_bezier_offsets;
860 selection.foreach_index(GrainSize(128), [&](const int curve_index) {
861 const IndexRange points = points_by_curve[curve_index];
862 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
863 const IndexRange offsets = curves::per_curve_point_offsets_range(points, curve_index);
865 handle_positions_left->slice(points),
866 handle_positions_right->slice(points),
867 all_bezier_offsets.slice(offsets),
868 evaluated_positions.slice(evaluated_points));
869 });
870 };
871 auto evaluate_nurbs = [&](const IndexMask &selection) {
872 this->ensure_nurbs_basis_cache();
873 const VArray<int8_t> nurbs_orders = this->nurbs_orders();
874 const std::optional<Span<float>> nurbs_weights = this->nurbs_weights();
875 const Span<curves::nurbs::BasisCache> nurbs_basis_cache = runtime.nurbs_basis_cache.data();
876 selection.foreach_index(GrainSize(128), [&](const int curve_index) {
877 const IndexRange points = points_by_curve[curve_index];
878 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
879 curves::nurbs::interpolate_to_evaluated(nurbs_basis_cache[curve_index],
880 nurbs_orders[curve_index],
881 nurbs_weights ? nurbs_weights->slice(points) :
882 Span<float>(),
883 positions.slice(points),
884 evaluated_positions.slice(evaluated_points));
885 });
886 };
888 this->curve_type_counts(),
889 this->curves_range(),
890 evaluate_catmull,
891 evaluate_poly,
892 evaluate_bezier,
893 evaluate_nurbs);
894 });
895 return runtime.evaluated_position_cache.data();
896}
897
898Span<float3> CurvesGeometry::evaluated_tangents() const
899{
900 const CurvesGeometryRuntime &runtime = *this->runtime;
901 runtime.evaluated_tangent_cache.ensure([&](Vector<float3> &r_data) {
902 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
903 const Span<float3> evaluated_positions = this->evaluated_positions();
904 const VArray<bool> cyclic = this->cyclic();
905
906 r_data.resize(this->evaluated_points_num());
907 MutableSpan<float3> tangents = r_data;
908
910 for (const int curve_index : curves_range) {
911 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
913 cyclic[curve_index],
914 tangents.slice(evaluated_points));
915 }
916 });
917
918 /* Correct the first and last tangents of non-cyclic Bezier curves so that they align with
919 * the inner handles. This is a separate loop to avoid the cost when Bezier type curves are
920 * not used. */
921 IndexMaskMemory memory;
922 const IndexMask bezier_mask = this->indices_for_curve_type(CURVE_TYPE_BEZIER, memory);
923 if (!bezier_mask.is_empty()) {
924 const OffsetIndices<int> points_by_curve = this->points_by_curve();
925 const Span<float3> positions = this->positions();
926 const Span<float3> handles_left = *this->handle_positions_left();
927 const Span<float3> handles_right = *this->handle_positions_right();
928
929 bezier_mask.foreach_index(GrainSize(1024), [&](const int curve_index) {
930 if (cyclic[curve_index]) {
931 return;
932 }
933 const IndexRange points = points_by_curve[curve_index];
934 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
935
936 const float epsilon = 1e-6f;
938 handles_right[points.first()], positions[points.first()], epsilon))
939 {
940 tangents[evaluated_points.first()] = math::normalize(handles_right[points.first()] -
941 positions[points.first()]);
942 }
944 handles_left[points.last()], positions[points.last()], epsilon))
945 {
946 tangents[evaluated_points.last()] = math::normalize(positions[points.last()] -
947 handles_left[points.last()]);
948 }
949 });
950 }
951 });
952 return runtime.evaluated_tangent_cache.data();
953}
954
956 const Span<float3> axes,
957 const Span<float> angles)
958{
959 for (const int i : directions.index_range()) {
960 const float3 axis = axes[i];
961 if (UNLIKELY(math::is_zero(axis))) {
962 continue;
963 }
964 directions[i] = math::rotate_direction_around_axis(directions[i], axis, angles[i]);
965 }
966}
967
969{
970 for (const int i : data.index_range()) {
972 }
973}
974
986
987static void evaluate_generic_data_for_curve(const EvalData &eval_data,
988 const int curve_index,
989 const GSpan src,
990 GMutableSpan dst)
991{
992 const IndexRange points = eval_data.points_by_curve[curve_index];
993 switch (eval_data.types[curve_index]) {
996 src, eval_data.cyclic[curve_index], eval_data.resolution[curve_index], dst);
997 break;
998 case CURVE_TYPE_POLY:
999 dst.copy_from(src);
1000 break;
1001 case CURVE_TYPE_BEZIER: {
1002 const IndexRange offsets = curves::per_curve_point_offsets_range(points, curve_index);
1004 src, eval_data.all_bezier_evaluated_offsets.slice(offsets), dst);
1005 break;
1006 }
1007 case CURVE_TYPE_NURBS:
1009 eval_data.nurbs_basis_cache[curve_index],
1010 eval_data.nurbs_orders[curve_index],
1011 eval_data.nurbs_weights ? eval_data.nurbs_weights->slice(points) : Span<float>(),
1012 src,
1013 dst);
1014 break;
1015 }
1016}
1017
1018Span<float3> CurvesGeometry::evaluated_normals() const
1019{
1020 const CurvesGeometryRuntime &runtime = *this->runtime;
1021 this->ensure_nurbs_basis_cache();
1022 runtime.evaluated_normal_cache.ensure([&](Vector<float3> &r_data) {
1023 const OffsetIndices<int> points_by_curve = this->points_by_curve();
1024 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
1025 const VArray<int8_t> types = this->curve_types();
1026 const VArray<bool> cyclic = this->cyclic();
1027 const VArray<int8_t> normal_mode = this->normal_mode();
1028 const Span<float3> evaluated_tangents = this->evaluated_tangents();
1029 const AttributeAccessor attributes = this->attributes();
1030 const EvalData eval_data{
1032 types,
1033 cyclic,
1034 this->resolution(),
1035 runtime.evaluated_offsets_cache.data().all_bezier_offsets,
1036 runtime.nurbs_basis_cache.data(),
1037 this->nurbs_orders(),
1038 this->nurbs_weights(),
1039 };
1040 const VArray<float> tilt = this->tilt();
1041 VArraySpan<float> tilt_span;
1042 const bool use_tilt = !(tilt.is_single() && tilt.get_internal_single() == 0.0f);
1043 if (use_tilt) {
1044 tilt_span = tilt;
1045 }
1046 VArraySpan<float3> custom_normal_span;
1047 if (const VArray<float3> custom_normal = *attributes.lookup<float3>("custom_normal",
1049 {
1050 custom_normal_span = custom_normal;
1051 }
1052
1053 r_data.resize(this->evaluated_points_num());
1055
1057 /* Reuse a buffer for the evaluated tilts. */
1058 Vector<float> evaluated_tilts;
1059
1060 for (const int curve_index : curves_range) {
1061 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
1062 switch (NormalMode(normal_mode[curve_index])) {
1063 case NORMAL_MODE_Z_UP:
1065 evaluated_normals.slice(evaluated_points));
1066 break;
1069 cyclic[curve_index],
1070 evaluated_normals.slice(evaluated_points));
1071 break;
1072 case NORMAL_MODE_FREE:
1073 if (custom_normal_span.is_empty()) {
1075 evaluated_normals.slice(evaluated_points));
1076 }
1077 else {
1078 const Span<float3> src = custom_normal_span.slice(points_by_curve[curve_index]);
1080 evaluated_points_by_curve[curve_index]);
1081 evaluate_generic_data_for_curve(eval_data, curve_index, src, dst);
1082 normalize_span(dst);
1083 }
1084 break;
1085 }
1086
1087 /* If the "tilt" attribute exists, rotate the normals around the tangents by the
1088 * evaluated angles. We can avoid copying the tilts to evaluate them for poly curves. */
1089 if (use_tilt) {
1090 const IndexRange points = points_by_curve[curve_index];
1091 if (types[curve_index] == CURVE_TYPE_POLY) {
1092 rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
1093 evaluated_tangents.slice(evaluated_points),
1094 tilt_span.slice(points));
1095 }
1096 else {
1097 evaluated_tilts.reinitialize(evaluated_points.size());
1099 curve_index,
1100 tilt_span.slice(points),
1101 evaluated_tilts.as_mutable_span());
1102 rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
1103 evaluated_tangents.slice(evaluated_points),
1104 evaluated_tilts.as_span());
1105 }
1106 }
1107 }
1108 });
1109 });
1110 return this->runtime->evaluated_normal_cache.data();
1111}
1112
1113void CurvesGeometry::interpolate_to_evaluated(const int curve_index,
1114 const GSpan src,
1115 GMutableSpan dst) const
1116{
1117 const CurvesGeometryRuntime &runtime = *this->runtime;
1118 const EvalData eval_data{
1119 this->points_by_curve(),
1120 this->curve_types(),
1121 this->cyclic(),
1122 this->resolution(),
1123 runtime.evaluated_offsets_cache.data().all_bezier_offsets,
1124 runtime.nurbs_basis_cache.data(),
1125 this->nurbs_orders(),
1126 this->nurbs_weights(),
1127 };
1128 BLI_assert(src.size() == this->points_by_curve()[curve_index].size());
1129 BLI_assert(dst.size() == this->evaluated_points_by_curve()[curve_index].size());
1130 evaluate_generic_data_for_curve(eval_data, curve_index, src, dst);
1131}
1132
1133void CurvesGeometry::interpolate_to_evaluated(const GSpan src, GMutableSpan dst) const
1134{
1135 const CurvesGeometryRuntime &runtime = *this->runtime;
1136 const OffsetIndices points_by_curve = this->points_by_curve();
1137 const EvalData eval_data{
1139 this->curve_types(),
1140 this->cyclic(),
1141 this->resolution(),
1142 runtime.evaluated_offsets_cache.data().all_bezier_offsets,
1143 runtime.nurbs_basis_cache.data(),
1144 this->nurbs_orders(),
1145 this->nurbs_weights(),
1146 };
1147 const OffsetIndices evaluated_points_by_curve = this->evaluated_points_by_curve();
1148
1150 for (const int curve_index : curves_range) {
1151 const IndexRange points = points_by_curve[curve_index];
1152 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
1154 eval_data, curve_index, src.slice(points), dst.slice(evaluated_points));
1155 }
1156 });
1157}
1158
1159void CurvesGeometry::ensure_evaluated_lengths() const
1160{
1161 const CurvesGeometryRuntime &runtime = *this->runtime;
1162 runtime.evaluated_length_cache.ensure([&](Vector<float> &r_data) {
1163 /* Use an extra length value for the final cyclic segment for a consistent size
1164 * (see comment on #evaluated_length_cache). */
1165 const int total_num = this->evaluated_points_num() + this->curves_num();
1166 r_data.resize(total_num);
1167 MutableSpan<float> evaluated_lengths = r_data;
1168
1169 const OffsetIndices<int> evaluated_points_by_curve = this->evaluated_points_by_curve();
1170 const Span<float3> evaluated_positions = this->evaluated_positions();
1171 const VArray<bool> curves_cyclic = this->cyclic();
1172
1174 for (const int curve_index : curves_range) {
1175 const bool cyclic = curves_cyclic[curve_index];
1176 const IndexRange evaluated_points = evaluated_points_by_curve[curve_index];
1177 const IndexRange lengths_range = this->lengths_range_for_curve(curve_index, cyclic);
1179 cyclic,
1180 evaluated_lengths.slice(lengths_range));
1181 }
1182 });
1183 });
1184}
1185
1186void CurvesGeometry::ensure_can_interpolate_to_evaluated() const
1187{
1188 this->evaluated_points_by_curve();
1189 this->ensure_nurbs_basis_cache();
1190}
1191
1193
1194/* -------------------------------------------------------------------- */
1197
1198void CurvesGeometry::resize(const int points_num, const int curves_num)
1199{
1200 BLI_assert(curves_num >= 0 && points_num >= 0);
1201 if (points_num != this->point_num) {
1202 this->attribute_storage.wrap().resize(AttrDomain::Point, points_num);
1203 CustomData_realloc(&this->point_data, this->points_num(), points_num);
1204 this->point_num = points_num;
1205 }
1206 if (curves_num != this->curve_num) {
1207 this->attribute_storage.wrap().resize(AttrDomain::Curve, curves_num);
1209 &this->runtime->curve_offsets_sharing_info,
1210 this->curve_num == 0 ? 0 : (this->curve_num + 1),
1211 curves_num == 0 ? 0 : (curves_num + 1));
1212 if (curves_num > 0) {
1213 /* Set common values for convenience. */
1214 this->curve_offsets[0] = 0;
1215 this->curve_offsets[curves_num] = this->point_num;
1216 }
1217 this->curve_num = curves_num;
1218 }
1219 this->tag_topology_changed();
1220}
1221
1222void CurvesGeometry::tag_positions_changed()
1223{
1224 this->runtime->evaluated_position_cache.tag_dirty();
1225 this->runtime->evaluated_tangent_cache.tag_dirty();
1226 this->runtime->evaluated_normal_cache.tag_dirty();
1227 this->runtime->evaluated_length_cache.tag_dirty();
1228 this->runtime->bounds_cache.tag_dirty();
1229 this->runtime->bounds_with_radius_cache.tag_dirty();
1230}
1231void CurvesGeometry::tag_topology_changed()
1232{
1233 this->runtime->custom_knot_offsets_cache.tag_dirty();
1234 this->tag_positions_changed();
1235 this->runtime->evaluated_offsets_cache.tag_dirty();
1236 this->runtime->has_cyclic_curve_cache.tag_dirty();
1237 this->runtime->nurbs_basis_cache.tag_dirty();
1238 this->runtime->max_material_index_cache.tag_dirty();
1239 this->runtime->check_type_counts = true;
1240}
1241void CurvesGeometry::tag_normals_changed()
1242{
1243 this->runtime->evaluated_normal_cache.tag_dirty();
1244}
1245void CurvesGeometry::tag_radii_changed()
1246{
1247 this->runtime->bounds_with_radius_cache.tag_dirty();
1248}
1249void CurvesGeometry::tag_material_index_changed()
1250{
1251 this->runtime->max_material_index_cache.tag_dirty();
1252}
1253
1254static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
1255{
1256 threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
1257 for (float3 &position : positions.slice(range)) {
1258 position += translation;
1259 }
1260 });
1261}
1262
1263void CurvesGeometry::calculate_bezier_auto_handles()
1264{
1266 return;
1267 }
1268 if (!this->handle_positions_left() || !this->handle_positions_right()) {
1269 return;
1270 }
1271 const OffsetIndices points_by_curve = this->points_by_curve();
1272 const VArray<int8_t> types = this->curve_types();
1273 const VArray<bool> cyclic = this->cyclic();
1274 const VArraySpan<int8_t> types_left{this->handle_types_left()};
1275 const VArraySpan<int8_t> types_right{this->handle_types_right()};
1276 const Span<float3> positions = this->positions();
1278 MutableSpan<float3> positions_right = this->handle_positions_right_for_write();
1279
1280 threading::parallel_for(this->curves_range(), 128, [&](IndexRange range) {
1281 for (const int i_curve : range) {
1282 if (types[i_curve] == CURVE_TYPE_BEZIER) {
1283 const IndexRange points = points_by_curve[i_curve];
1285 types_left.slice(points),
1286 types_right.slice(points),
1287 positions.slice(points),
1288 positions_left.slice(points),
1289 positions_right.slice(points));
1290 }
1291 }
1292 });
1293}
1294
1295void CurvesGeometry::translate(const float3 &translation)
1296{
1297 if (math::is_zero(translation)) {
1298 return;
1299 }
1300
1301 std::optional<Bounds<float3>> bounds;
1302 if (this->runtime->bounds_cache.is_cached()) {
1303 bounds = this->runtime->bounds_cache.data();
1304 }
1305
1306 translate_positions(this->positions_for_write(), translation);
1307 if (this->handle_positions_left()) {
1309 }
1310 if (this->handle_positions_right()) {
1312 }
1313 this->tag_positions_changed();
1314
1315 if (bounds) {
1316 bounds->min += translation;
1317 bounds->max += translation;
1318 this->runtime->bounds_cache.ensure([&](blender::Bounds<float3> &r_data) { r_data = *bounds; });
1319 }
1320}
1321
1322void CurvesGeometry::transform(const float4x4 &matrix)
1323{
1325 if (this->handle_positions_left()) {
1327 }
1328 if (this->handle_positions_right()) {
1330 }
1333 this->tag_positions_changed();
1334}
1335
1336std::optional<Bounds<float3>> CurvesGeometry::bounds_min_max(const bool use_radius) const
1337{
1338 if (this->is_empty()) {
1339 return std::nullopt;
1340 }
1341 if (use_radius) {
1342 this->runtime->bounds_with_radius_cache.ensure([&](Bounds<float3> &r_bounds) {
1343 const VArray<float> radius = this->radius();
1344 if (const std::optional radius_single = radius.get_if_single()) {
1345 r_bounds = *this->bounds_min_max(false);
1346 r_bounds.pad(*radius_single);
1347 return;
1348 }
1349 const Span radius_span = radius.get_internal_span();
1350 if (this->is_single_type(CURVE_TYPE_POLY)) {
1351 r_bounds = *bounds::min_max_with_radii(this->positions(), radius_span);
1352 return;
1353 }
1354 Array<float> radii_eval(this->evaluated_points_num());
1356 this->interpolate_to_evaluated(radius_span, radii_eval.as_mutable_span());
1357 r_bounds = *bounds::min_max_with_radii(this->evaluated_positions(), radii_eval.as_span());
1358 });
1359 }
1360 else {
1361 this->runtime->bounds_cache.ensure([&](Bounds<float3> &r_bounds) {
1362 r_bounds = *bounds::min_max(this->evaluated_positions());
1363 });
1364 }
1365 return use_radius ? this->runtime->bounds_with_radius_cache.data() :
1366 this->runtime->bounds_cache.data();
1367}
1368
1369std::optional<int> CurvesGeometry::material_index_max() const
1370{
1371 this->runtime->max_material_index_cache.ensure([&](std::optional<int> &r_max_material_index) {
1372 r_max_material_index = blender::bounds::max<int>(
1373 this->attributes()
1374 .lookup_or_default<int>("material_index", blender::bke::AttrDomain::Curve, 0)
1375 .varray);
1376 if (r_max_material_index.has_value()) {
1377 r_max_material_index = std::clamp(*r_max_material_index, 0, MAXMAT);
1378 }
1379 });
1380 return this->runtime->max_material_index_cache.data();
1381}
1382
1383void CurvesGeometry::count_memory(MemoryCounter &memory) const
1384{
1385 memory.add_shared(this->runtime->curve_offsets_sharing_info, this->offsets().size_in_bytes());
1386 memory.add_shared(this->runtime->custom_knots_sharing_info,
1387 this->nurbs_custom_knots().size_in_bytes());
1388 this->attribute_storage.wrap().count_memory(memory);
1389 CustomData_count_memory(this->point_data, this->point_num, memory);
1390}
1391
1393 const IndexMask &points_to_copy,
1394 const Span<int> curve_point_counts,
1395 CurvesGeometry &dst_curves)
1396{
1397 const OffsetIndices points_by_curve = curves.points_by_curve();
1398 const VArray<int8_t> orders = curves.nurbs_orders();
1399 const VArray<bool> cyclic = curves.cyclic();
1400
1401 IndexMaskMemory memory;
1402 const IndexMask custom_knot_curves = curves.nurbs_custom_knot_curves(memory);
1403 const IndexMask custom_knot_points = bke::curves::curve_to_point_selection(
1404 points_by_curve, custom_knot_curves, memory);
1405 const IndexMask custom_knot_points_to_copy = IndexMask::from_intersection(
1406 points_to_copy, custom_knot_points, memory);
1407
1408 int dst_knot_count = 0;
1409 custom_knot_curves.foreach_index([&](const int64_t curve) {
1410 dst_knot_count += curves::nurbs::knots_num(
1411 curve_point_counts[curve], orders[curve], cyclic[curve]);
1412 });
1413 const OffsetIndices<int> src_knots_by_curve = curves.nurbs_custom_knots_by_curve();
1414 const Span<float> src_knots = curves.nurbs_custom_knots();
1415
1416 Vector<float> new_knots;
1417 new_knots.reserve(dst_knot_count);
1418
1420 custom_knot_points_to_copy,
1421 points_by_curve,
1422 [&](int curve, IndexRange points, Span<IndexRange> ranges_to_copy) {
1423 const IndexRange src_range = src_knots_by_curve[curve];
1424 const int order = orders[curve];
1425 const int leading_spans = order / 2;
1426 const int point_to_knot = -points.start() + src_range.start();
1427 const int point_to_span = point_to_knot + leading_spans;
1428
1429 const int first_knot = ranges_to_copy.first().start() + point_to_knot;
1430 new_knots.extend(
1431 src_knots.slice(IndexRange::from_begin_size(first_knot, leading_spans + 1)));
1432 float last_knot = new_knots.last();
1433 for (IndexRange range : ranges_to_copy) {
1434 for (const int spans_left_knot : range.shift(point_to_span)) {
1435 last_knot += src_knots[spans_left_knot + 1] - src_knots[spans_left_knot];
1436 new_knots.append(last_knot);
1437 }
1438 }
1439 const int last_spans_left_knot = ranges_to_copy.last().last() + point_to_span + 1;
1440 last_knot += src_knots[last_spans_left_knot + 1] - src_knots[last_spans_left_knot];
1441 new_knots.append(last_knot);
1442 });
1443 dst_curves.nurbs_custom_knots_update_size();
1444 dst_curves.nurbs_custom_knots_for_write().copy_from(new_knots);
1445}
1446
1448 const IndexMask &points_to_copy,
1449 const AttributeFilter &attribute_filter)
1450{
1451 const Array<int> point_to_curve_map = curves.point_to_curve_map();
1452 Array<int> curve_point_counts(curves.curves_num(), 0);
1453 points_to_copy.foreach_index(
1454 [&](const int64_t point_i) { curve_point_counts[point_to_curve_map[point_i]]++; });
1455
1456 IndexMaskMemory memory;
1457 const IndexMask curves_to_copy = IndexMask::from_predicate(
1458 curves.curves_range(), GrainSize(4096), memory, [&](const int64_t i) {
1459 return curve_point_counts[i] > 0;
1460 });
1461
1462 CurvesGeometry dst_curves(points_to_copy.size(), curves_to_copy.size());
1463
1464 BKE_defgroup_copy_list(&dst_curves.vertex_group_names, &curves.vertex_group_names);
1465
1467 dst_curves.curves_num() > 1024,
1468 [&]() {
1469 if (curves_to_copy.is_empty()) {
1470 return;
1471 }
1472 MutableSpan<int> new_curve_offsets = dst_curves.offsets_for_write();
1474 curve_point_counts.as_span(), curves_to_copy, new_curve_offsets.drop_back(1));
1476 },
1477 [&]() {
1478 gather_attributes(curves.attributes(),
1479 AttrDomain::Point,
1480 AttrDomain::Point,
1481 attribute_filter,
1482 points_to_copy,
1483 dst_curves.attributes_for_write());
1484 gather_attributes(curves.attributes(),
1485 AttrDomain::Curve,
1486 AttrDomain::Curve,
1487 attribute_filter,
1488 curves_to_copy,
1489 dst_curves.attributes_for_write());
1490 });
1491
1492 if (curves.nurbs_has_custom_knots()) {
1493 copy_point_selection_custom_knots(curves, points_to_copy, curve_point_counts, dst_curves);
1494 }
1495
1496 if (dst_curves.curves_num() == curves.curves_num()) {
1497 dst_curves.runtime->type_counts = curves.runtime->type_counts;
1498 }
1499 else {
1500 dst_curves.remove_attributes_based_on_types();
1501 }
1502
1503 return dst_curves;
1504}
1505
1506void CurvesGeometry::remove_points(const IndexMask &points_to_delete,
1507 const AttributeFilter &attribute_filter)
1508{
1509 if (points_to_delete.is_empty()) {
1510 return;
1511 }
1512 if (points_to_delete.size() == this->points_num()) {
1513 this->resize(0, 0);
1514 this->update_curve_types();
1515 return;
1516 }
1517 IndexMaskMemory memory;
1518 const IndexMask points_to_copy = points_to_delete.complement(this->points_range(), memory);
1519 *this = curves_copy_point_selection(*this, points_to_copy, attribute_filter);
1520}
1521
1523 const IndexMask &curves_to_copy,
1524 CurvesGeometry &dst_curves)
1525{
1526 IndexMaskMemory memory;
1527 const IndexMask custom_knot_curves = curves.nurbs_custom_knot_curves(memory);
1528 const IndexMask custom_knot_curves_to_copy = IndexMask::from_intersection(
1529 curves_to_copy, custom_knot_curves, memory);
1530
1531 Array<int> dst_knot_offsets_data(custom_knot_curves_to_copy.size() + 1, 0);
1532
1533 const OffsetIndices<int> src_knots_by_curve = curves.nurbs_custom_knots_by_curve();
1535 src_knots_by_curve, custom_knot_curves_to_copy, dst_knot_offsets_data);
1536
1537 dst_curves.nurbs_custom_knots_update_size();
1538 array_utils::gather_group_to_group(src_knots_by_curve,
1539 dst_knots_by_curve,
1540 custom_knot_curves_to_copy,
1541 curves.nurbs_custom_knots(),
1542 dst_curves.nurbs_custom_knots_for_write());
1543}
1544
1546 const IndexMask &curves_to_copy,
1547 const AttributeFilter &attribute_filter)
1548{
1549 const OffsetIndices points_by_curve = curves.points_by_curve();
1550 CurvesGeometry dst_curves(0, curves_to_copy.size());
1551 const OffsetIndices dst_points_by_curve = offset_indices::gather_selected_offsets(
1552 points_by_curve, curves_to_copy, dst_curves.offsets_for_write());
1553 dst_curves.resize(dst_points_by_curve.total_size(), dst_curves.curves_num());
1554
1555 BKE_defgroup_copy_list(&dst_curves.vertex_group_names, &curves.vertex_group_names);
1556
1557 const AttributeAccessor src_attributes = curves.attributes();
1558 MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
1559
1560 gather_attributes_group_to_group(src_attributes,
1563 attribute_filter,
1564 points_by_curve,
1565 dst_points_by_curve,
1566 curves_to_copy,
1567 dst_attributes);
1568
1569 gather_attributes(src_attributes,
1572 attribute_filter,
1573 curves_to_copy,
1574 dst_attributes);
1575
1576 if (curves.nurbs_has_custom_knots()) {
1577 copy_curve_selection_custom_knots(curves, curves_to_copy, dst_curves);
1578 }
1579
1580 dst_curves.update_curve_types();
1582
1583 return dst_curves;
1584}
1585
1586void CurvesGeometry::remove_curves(const IndexMask &curves_to_delete,
1587 const AttributeFilter &attribute_filter)
1588{
1589 if (curves_to_delete.is_empty()) {
1590 return;
1591 }
1592 if (curves_to_delete.size() == this->curves_num()) {
1593 this->resize(0, 0);
1594 this->update_curve_types();
1595 return;
1596 }
1597 IndexMaskMemory memory;
1598 const IndexMask curves_to_copy = curves_to_delete.complement(this->curves_range(), memory);
1599 *this = curves_copy_curve_selection(*this, curves_to_copy, attribute_filter);
1600}
1601
1603{
1604 const float last = custom_knots.last();
1605 custom_knots.reverse();
1606 for (float &knot_value : custom_knots) {
1607 knot_value = last - knot_value;
1608 }
1609}
1610
1611template<typename T>
1613 const IndexMask &curve_selection,
1615{
1616 const OffsetIndices points_by_curve = curves.points_by_curve();
1617 curve_selection.foreach_index(
1618 GrainSize(256), [&](const int curve_i) { data.slice(points_by_curve[curve_i]).reverse(); });
1619}
1620
1621template<typename T>
1623 const IndexMask &curve_selection,
1624 MutableSpan<T> data_a,
1625 MutableSpan<T> data_b)
1626{
1627 const OffsetIndices points_by_curve = curves.points_by_curve();
1628 curve_selection.foreach_index(GrainSize(256), [&](const int curve_i) {
1629 const IndexRange points = points_by_curve[curve_i];
1630 MutableSpan<T> a = data_a.slice(points);
1631 MutableSpan<T> b = data_b.slice(points);
1632 for (const int i : IndexRange(points.size() / 2)) {
1633 const int end_index = points.size() - 1 - i;
1634 std::swap(a[end_index], b[i]);
1635 std::swap(b[end_index], a[i]);
1636 }
1637 if (points.size() % 2) {
1638 const int64_t middle_index = points.size() / 2;
1639 std::swap(a[middle_index], b[middle_index]);
1640 }
1641 });
1642}
1643
1644void CurvesGeometry::reverse_curves(const IndexMask &curves_to_reverse)
1645{
1646 Set<StringRef> bezier_handle_names{{ATTR_HANDLE_POSITION_LEFT,
1650
1652
1653 attributes.foreach_attribute([&](const AttributeIter &iter) {
1654 if (iter.domain != AttrDomain::Point) {
1655 return;
1656 }
1657 if (iter.data_type == bke::AttrType::String) {
1658 return;
1659 }
1660 if (bezier_handle_names.contains(iter.name)) {
1661 return;
1662 }
1663
1664 GSpanAttributeWriter attribute = attributes.lookup_for_write_span(iter.name);
1665 attribute_math::convert_to_static_type(attribute.span.type(), [&](auto dummy) {
1666 using T = decltype(dummy);
1667 reverse_curve_point_data<T>(*this, curves_to_reverse, attribute.span.typed<T>());
1668 });
1669 attribute.finish();
1670 return;
1671 });
1672
1673 if (this->nurbs_has_custom_knots()) {
1674 const OffsetIndices custom_knots_by_curve = this->nurbs_custom_knots_by_curve();
1676 curves_to_reverse.foreach_index(GrainSize(256), [&](const int64_t curve) {
1677 const IndexRange curve_knots = custom_knots_by_curve[curve];
1678 if (!custom_knots.is_empty()) {
1679 reverse_custom_knots(custom_knots.slice(curve_knots));
1680 }
1681 });
1682 }
1683
1684 /* In order to maintain the shape of Bezier curves, handle attributes must reverse, but also the
1685 * values for the left and right must swap. Use a utility to swap and reverse at the same time,
1686 * to avoid loading the attribute twice. Generally we can expect the right layer to exist when
1687 * the left does, but there's no need to count on it, so check for both attributes. */
1688
1689 if (attributes.contains(ATTR_HANDLE_POSITION_LEFT) &&
1691 {
1693 curves_to_reverse,
1696 }
1699 curves_to_reverse,
1702 }
1703
1704 this->tag_topology_changed();
1705}
1706
1707void CurvesGeometry::remove_attributes_based_on_types()
1708{
1715 }
1720 }
1723 }
1724}
1725
1726CurvesGeometry curves_new_no_attributes(int point_num, int curve_num)
1727{
1728 CurvesGeometry curves(0, curve_num);
1729 curves.point_num = point_num;
1730 curves.attribute_storage.wrap().remove("position");
1731 return curves;
1732}
1733
1735
1736/* -------------------------------------------------------------------- */
1739
1747template<typename T>
1749 const VArray<T> &old_values,
1750 MutableSpan<T> r_values)
1751{
1752 attribute_math::DefaultMixer<T> mixer(r_values);
1753
1754 const OffsetIndices points_by_curve = curves.points_by_curve();
1755 threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) {
1756 for (const int i_curve : range) {
1757 for (const int i_point : points_by_curve[i_curve]) {
1758 mixer.mix_in(i_curve, old_values[i_point]);
1759 }
1760 }
1761 mixer.finalize(range);
1762 });
1763}
1764
1772template<>
1774 const VArray<bool> &old_values,
1775 MutableSpan<bool> r_values)
1776{
1777 const OffsetIndices points_by_curve = curves.points_by_curve();
1778 r_values.fill(true);
1779 for (const int i_curve : IndexRange(curves.curves_num())) {
1780 for (const int i_point : points_by_curve[i_curve]) {
1781 if (!old_values[i_point]) {
1782 r_values[i_curve] = false;
1783 break;
1784 }
1785 }
1786 }
1787}
1788
1790 const GVArray &varray)
1791{
1792 GVArray new_varray;
1793 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
1794 using T = decltype(dummy);
1795 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
1796 Array<T> values(curves.curves_num());
1797 adapt_curve_domain_point_to_curve_impl<T>(curves, varray.typed<T>(), values);
1798 new_varray = VArray<T>::from_container(std::move(values));
1799 }
1800 });
1801 return new_varray;
1802}
1803
1811template<typename T>
1813 const VArray<T> &old_values,
1814 MutableSpan<T> r_values)
1815{
1816 const OffsetIndices points_by_curve = curves.points_by_curve();
1817 for (const int i_curve : IndexRange(curves.curves_num())) {
1818 r_values.slice(points_by_curve[i_curve]).fill(old_values[i_curve]);
1819 }
1820}
1821
1823 const GVArray &varray)
1824{
1825 GVArray new_varray;
1826 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
1827 using T = decltype(dummy);
1828 Array<T> values(curves.points_num());
1829 adapt_curve_domain_curve_to_point_impl<T>(curves, varray.typed<T>(), values);
1830 new_varray = VArray<T>::from_container(std::move(values));
1831 });
1832 return new_varray;
1833}
1834
1835GVArray CurvesGeometry::adapt_domain(const GVArray &varray,
1836 const AttrDomain from,
1837 const AttrDomain to) const
1838{
1839 if (!varray) {
1840 return {};
1841 }
1842 if (varray.is_empty()) {
1843 return {};
1844 }
1845 if (from == to) {
1846 return varray;
1847 }
1848 if (varray.is_single()) {
1849 BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), value);
1850 varray.get_internal_single(value);
1851 return GVArray::from_single(varray.type(), this->attributes().domain_size(to), value);
1852 }
1853
1854 if (from == AttrDomain::Point && to == AttrDomain::Curve) {
1855 return adapt_curve_domain_point_to_curve(*this, varray);
1856 }
1857 if (from == AttrDomain::Curve && to == AttrDomain::Point) {
1858 return adapt_curve_domain_curve_to_point(*this, varray);
1859 }
1860
1862 return {};
1863}
1864
1865AttributeAccessor CurvesGeometry::attributes() const
1866{
1868}
1869
1870MutableAttributeAccessor CurvesGeometry::attributes_for_write()
1871{
1873}
1874
1876
1877/* -------------------------------------------------------------------- */
1880
1881void CurvesGeometry::blend_read(BlendDataReader &reader)
1882{
1883 this->runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__);
1884
1885 CustomData_blend_read(&reader, &this->point_data, this->point_num);
1886 CustomData_blend_read(&reader, &this->curve_data_legacy, this->curve_num);
1887 this->attribute_storage.wrap().blend_read(reader);
1888
1889 if (this->curve_offsets) {
1891 &reader, &this->curve_offsets, [&]() {
1892 BLO_read_int32_array(&reader, this->curve_num + 1, &this->curve_offsets);
1894 });
1895 }
1896
1898
1899 if (this->custom_knot_num) {
1901 &reader, &this->custom_knots, [&]() {
1902 BLO_read_float_array(&reader, this->custom_knot_num, &this->custom_knots);
1903 return implicit_sharing::info_for_mem_free(this->custom_knots);
1904 });
1905 }
1906
1907 /* Recalculate curve type count cache that isn't saved in files. */
1908 this->update_curve_types();
1909}
1910
1911CurvesGeometry::BlendWriteData::BlendWriteData(ResourceScope &scope)
1912 : scope(scope),
1913 point_layers(scope.construct<Vector<CustomDataLayer, 16>>()),
1914 curve_layers(scope.construct<Vector<CustomDataLayer, 16>>()),
1916{
1917}
1918
1919void CurvesGeometry::blend_write_prepare(CurvesGeometry::BlendWriteData &write_data)
1920{
1922 attribute_storage_blend_write_prepare(this->attribute_storage.wrap(), write_data.attribute_data);
1925 this->points_num(),
1926 write_data.point_layers,
1927 write_data.attribute_data);
1928 if (write_data.attribute_data.attributes.is_empty()) {
1929 this->attribute_storage.dna_attributes = nullptr;
1930 this->attribute_storage.dna_attributes_num = 0;
1931 }
1932 else {
1933 this->attribute_storage.dna_attributes = write_data.attribute_data.attributes.data();
1934 this->attribute_storage.dna_attributes_num = write_data.attribute_data.attributes.size();
1935 }
1936}
1937
1938void CurvesGeometry::blend_write(BlendWriter &writer,
1939 ID &id,
1940 const CurvesGeometry::BlendWriteData &write_data)
1941{
1943 &writer, &this->point_data, write_data.point_layers, this->point_num, CD_MASK_ALL, &id);
1944 this->attribute_storage.wrap().blend_write(writer, write_data.attribute_data);
1945
1946 if (this->curve_offsets) {
1948 &writer,
1949 this->curve_offsets,
1950 sizeof(int) * (this->curve_num + 1),
1951 this->runtime->curve_offsets_sharing_info,
1952 [&]() { BLO_write_int32_array(&writer, this->curve_num + 1, this->curve_offsets); });
1953 }
1954
1956
1957 if (this->custom_knot_num) {
1959 &writer,
1960 this->custom_knots,
1961 sizeof(float) * this->custom_knot_num,
1962 this->runtime->custom_knots_sharing_info,
1963 [&]() { BLO_write_float_array(&writer, this->custom_knot_num, this->custom_knots); });
1964 }
1965}
1966
1968
1969} // 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)
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
@ CD_SET_DEFAULT
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)
support for deformation groups and hooks.
void BKE_defbase_blend_write(BlendWriter *writer, const ListBase *defbase)
Definition deform.cc:1595
void BKE_defgroup_copy_list(ListBase *outbase, const ListBase *inbase)
Definition deform.cc:73
#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
#define UNLIKELY(x)
void BLO_read_float_array(BlendDataReader *reader, int64_t array_size, float **ptr_p)
Definition readfile.cc:5809
void BLO_read_int32_array(BlendDataReader *reader, int64_t array_size, int32_t **ptr_p)
Definition readfile.cc:5795
#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)
NormalMode
@ NORMAL_MODE_MINIMUM_TWIST
@ NORMAL_MODE_FREE
@ NORMAL_MODE_Z_UP
#define CURVE_TYPES_NUM
KnotsMode
@ NURBS_KNOT_MODE_CUSTOM
#define CD_MASK_MDEFORMVERT
#define CD_MASK_ALL
@ CD_MDEFORMVERT
#define MAXMAT
Object is a sort of wrapper for general info.
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(int64_t start, int64_t size) const
Definition BLI_span.hh:137
Span< T > as_span() const
Definition BLI_array.hh:243
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
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 from_single(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
constexpr bool is_empty() 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 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
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
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:96
SharedCache< Vector< float3 > > evaluated_normal_cache
const ImplicitSharingInfo * custom_knots_sharing_info
Definition BKE_curves.hh:80
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:77
SharedCache< bool > has_cyclic_curve_cache
Definition BKE_curves.hh:98
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
std::optional< Span< float > > nurbs_weights() 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
std::optional< Span< float3 > > handle_positions_left() const
Span< float3 > positions() const
OffsetIndices< int > evaluated_points_by_curve() const
bool has_curve_with_type(CurveType type) const
std::optional< Span< float3 > > handle_positions_right() const
void resize(int points_num, int curves_num)
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 add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
bool remove(const StringRef attribute_id)
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)
static char ** types
Definition makesdna.cc:71
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
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)
bool contains(const VArray< bool > &varray, const IndexMask &indices_to_check, bool value)
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, Span< float > knots)
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:74
void foreach_selected_point_ranges_per_curve(const IndexMask &mask, OffsetIndices< int > points_by_curve, SelectedCallback selected_fn)
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)
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 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
GMutableSpan get_mutable_attribute(AttributeStorage &storage, const AttrDomain domain, const CPPType &cpp_type, const StringRef name, const int64_t domain_size, const void *custom_default_value)
static void copy_point_selection_custom_knots(const CurvesGeometry &curves, const IndexMask &points_to_copy, const Span< int > curve_point_counts, CurvesGeometry &dst_curves)
void attribute_storage_blend_write_prepare(AttributeStorage &data, AttributeStorage::BlendWriteData &write_data)
std::optional< GSpan > get_span_attribute(const AttributeStorage &storage, const AttrDomain domain, const CPPType &cpp_type, const StringRef name, const int64_t domain_size)
constexpr StringRef ATTR_CURVE_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)
GVArray get_varray_attribute(const AttributeStorage &storage, AttrDomain domain, const CPPType &cpp_type, StringRef name, int64_t domain_size, const void *default_value)
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)
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 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
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)
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 transform_points(const float4x4 &transform, MutableSpan< float3 > points, bool use_threading=true)
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)
CustomData curve_data_legacy
CustomData point_data
CurvesGeometryRuntimeHandle * runtime
ListBase vertex_group_names
struct AttributeStorage attribute_storage
Definition DNA_ID.h:414
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< 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
const std::optional< Span< float > > nurbs_weights
i
Definition text_draw.cc:230