Blender V5.0
geometry_component_curves.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_task.hh"
6
7#include "DNA_ID_enums.h"
8#include "DNA_curve_types.h"
9
10#include "BKE_curves.hh"
12#include "BKE_geometry_set.hh"
13#include "BKE_lib_id.hh"
14
16
17namespace blender::bke {
18
19/* -------------------------------------------------------------------- */
22
24
26 : GeometryComponent(Type::Curve), curves_(curve), ownership_(ownership)
27{
28}
29
34
36{
37 CurveComponent *new_component = new CurveComponent();
38 if (curves_ != nullptr) {
39 new_component->curves_ = BKE_curves_copy_for_eval(curves_);
40 new_component->ownership_ = GeometryOwnershipType::Owned;
41 }
42 return GeometryComponentPtr(new_component);
43}
44
46{
47 BLI_assert(this->is_mutable() || this->is_expired());
48 if (curves_ != nullptr) {
49 if (ownership_ == GeometryOwnershipType::Owned) {
50 BKE_id_free(nullptr, curves_);
51 }
52 if (curve_for_render_ != nullptr) {
53 /* The curve created by this component should not have any edit mode data. */
54 BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr);
55 BKE_id_free(nullptr, curve_for_render_);
56 curve_for_render_ = nullptr;
57 }
58
59 curves_ = nullptr;
60 }
61}
62
64{
65 return curves_ != nullptr;
66}
67
69{
70 BLI_assert(this->is_mutable());
71 this->clear();
72 curves_ = curves;
73 ownership_ = ownership;
74}
75
77{
78 BLI_assert(this->is_mutable());
79 Curves *curves = curves_;
80 curves_ = nullptr;
81 return curves;
82}
83
85{
86 return curves_;
87}
88
90{
91 BLI_assert(this->is_mutable());
92 if (ownership_ == GeometryOwnershipType::ReadOnly) {
93 curves_ = BKE_curves_copy_for_eval(curves_);
95 }
96 return curves_;
97}
98
100{
101 return curves_ == nullptr;
102}
103
105{
106 return ownership_ == GeometryOwnershipType::Owned;
107}
108
110{
111 BLI_assert(this->is_mutable());
112 if (ownership_ != GeometryOwnershipType::Owned) {
113 if (curves_) {
114 curves_ = BKE_curves_copy_for_eval(curves_);
115 }
116 ownership_ = GeometryOwnershipType::Owned;
117 }
118}
119
121{
122 if (curves_) {
123 curves_->geometry.wrap().count_memory(memory);
124 }
125}
126
128{
129 if (curves_ == nullptr) {
130 return nullptr;
131 }
132 if (curve_for_render_ != nullptr) {
133 return curve_for_render_;
134 }
135 std::lock_guard lock{curve_for_render_mutex_};
136 if (curve_for_render_ != nullptr) {
137 return curve_for_render_;
138 }
139
140 curve_for_render_ = BKE_id_new_nomain<Curve>(nullptr);
141 curve_for_render_->curve_eval = curves_;
142
143 return curve_for_render_;
144}
145
147
148/* -------------------------------------------------------------------- */
151
153{
154 const OffsetIndices points_by_curve = curves.points_by_curve();
155 const OffsetIndices evaluated_points_by_curve = curves.evaluated_points_by_curve();
156 const VArray<int8_t> types = curves.curve_types();
157 const VArray<int> resolutions = curves.resolution();
158 const VArray<bool> curves_cyclic = curves.cyclic();
159 const AttributeAccessor attributes = curves.attributes();
160 const VArray<float3> custom_normals = *attributes.lookup_or_default<float3>(
161 "custom_normal", AttrDomain::Point, float3(0, 0, 1));
162
163 const Span<float3> positions = curves.positions();
164 const VArray<int8_t> normal_modes = curves.normal_mode();
165
166 const Span<float3> evaluated_normals = curves.evaluated_normals();
167
168 Array<float3> results(curves.points_num());
169
170 threading::parallel_for(curves.curves_range(), 128, [&](IndexRange range) {
171 Vector<float3> nurbs_tangents;
172
173 for (const int i_curve : range) {
174 const IndexRange points = points_by_curve[i_curve];
175 const IndexRange evaluated_points = evaluated_points_by_curve[i_curve];
176
177 MutableSpan<float3> curve_normals = results.as_mutable_span().slice(points);
178
179 switch (types[i_curve]) {
180 case CURVE_TYPE_CATMULL_ROM: {
181 const Span<float3> normals = evaluated_normals.slice(evaluated_points);
182 const int resolution = resolutions[i_curve];
183 for (const int i : IndexRange(points.size())) {
184 curve_normals[i] = normals[resolution * i];
185 }
186 break;
187 }
188 case CURVE_TYPE_POLY:
189 curve_normals.copy_from(evaluated_normals.slice(evaluated_points));
190 break;
191 case CURVE_TYPE_BEZIER: {
192 const Span<float3> normals = evaluated_normals.slice(evaluated_points);
193 curve_normals.first() = normals.first();
194 const Span<int> offsets = curves.bezier_evaluated_offsets_for_curve(i_curve);
195 for (const int i : IndexRange(points.size()).drop_front(1)) {
196 curve_normals[i] = normals[offsets[i]];
197 }
198 break;
199 }
200 case CURVE_TYPE_NURBS: {
201 /* For NURBS curves there is no obvious correspondence between specific evaluated points
202 * and control points, so normals are determined by treating them as poly curves. */
203 nurbs_tangents.clear();
204 nurbs_tangents.resize(points.size());
205 const bool cyclic = curves_cyclic[i_curve];
206 const Span<float3> curve_positions = positions.slice(points);
207 curves::poly::calculate_tangents(curve_positions, cyclic, nurbs_tangents);
208 switch (NormalMode(normal_modes[i_curve])) {
209 case NORMAL_MODE_Z_UP:
210 curves::poly::calculate_normals_z_up(nurbs_tangents, curve_normals);
211 break;
212 case NORMAL_MODE_MINIMUM_TWIST:
213 curves::poly::calculate_normals_minimum(nurbs_tangents, cyclic, curve_normals);
214 break;
215 case NORMAL_MODE_FREE:
216 custom_normals.materialize(points, results);
217 break;
218 }
219 break;
220 }
221 }
222 }
223 });
224 return results;
225}
226
228{
229 const VArray<int8_t> types = curves.curve_types();
230 if (curves.is_single_type(CURVE_TYPE_POLY)) {
231 return curves.adapt_domain<float3>(
232 VArray<float3>::from_span(curves.evaluated_normals()), AttrDomain::Point, domain);
233 }
234
236
237 if (domain == AttrDomain::Point) {
238 return VArray<float3>::from_container(std::move(normals));
239 }
240
241 if (domain == AttrDomain::Curve) {
242 return curves.adapt_domain<float3>(
244 }
245
246 return nullptr;
247}
248
250
251/* -------------------------------------------------------------------- */
254
256 const AttrDomain domain)
257{
258 curves.ensure_evaluated_lengths();
259
260 VArray<bool> cyclic = curves.cyclic();
262 curves.curves_num(), [&curves, cyclic = std::move(cyclic)](int64_t index) {
263 return curves.evaluated_length_total_for_curve(index, cyclic[index]);
264 });
265
266 if (domain == AttrDomain::Curve) {
267 return lengths;
268 }
269
270 if (domain == AttrDomain::Point) {
271 return curves.adapt_domain<float>(std::move(lengths), AttrDomain::Curve, AttrDomain::Point);
272 }
273
274 return {};
275}
276
282
284 const AttrDomain domain,
285 const IndexMask & /*mask*/) const
286{
288}
289
291{
292 /* Some random constant hash. */
293 return 3549623580;
294}
295
297{
298 return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr;
299}
300
302 const CurvesGeometry & /*curves*/) const
303{
304 return AttrDomain::Curve;
305}
306
308
309/* -------------------------------------------------------------------- */
312
313std::optional<AttributeAccessor> CurveComponent::attributes() const
314{
315 return AttributeAccessor(curves_ ? &curves_->geometry : nullptr,
317}
318
319std::optional<MutableAttributeAccessor> CurveComponent::attributes_for_write()
320{
321 Curves *curves = this->get_for_write();
322 return MutableAttributeAccessor(curves ? &curves->geometry : nullptr,
324}
325
326} // namespace blender::bke
struct Curves * BKE_curves_copy_for_eval(const struct Curves *curves_src)
Low-level operations for curves.
void BKE_id_free(Main *bmain, void *idv)
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1519
#define BLI_assert(a)
Definition BLI_assert.h:46
Enumerations for DNA_ID.h.
volatile int lock
long long int int64_t
unsigned long long int uint64_t
static VArray from_func(const int64_t size, GetFunc get_func)
static VArray from_span(Span< T > values)
static VArray from_container(ContainerT container)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
void count_memory(MemoryCounter &memory) const override
void replace(Curves *curve, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
GeometryComponentPtr copy() const override
std::optional< AttributeAccessor > attributes() const final
std::optional< MutableAttributeAccessor > attributes_for_write() final
GVArray get_varray_for_context(const CurvesGeometry &curves, AttrDomain domain, const IndexMask &mask) const final
bool is_equal_to(const fn::FieldNode &other) const override
std::optional< AttrDomain > preferred_domain(const bke::CurvesGeometry &curves) const final
nullptr float
static float normals[][3]
static char ** types
Definition makesdna.cc:71
const AttributeAccessorFunctions & get_attribute_accessor_functions()
ImplicitSharingPtr< GeometryComponent > GeometryComponentPtr
static Array< float3 > curve_normal_point_domain(const CurvesGeometry &curves)
VArray< float3 > curve_normals_varray(const CurvesGeometry &curves, AttrDomain domain)
static VArray< float > construct_curve_length_gvarray(const CurvesGeometry &curves, const AttrDomain domain)
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
VecBase< float, 3 > float3