Blender V5.0
curves_attributes.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_listbase.h"
6
7#include "DNA_object_types.h"
8
10#include "BKE_curves.hh"
11#include "BKE_deform.hh"
12
14
17
18namespace blender::bke::curves {
19
20static void tag_topology_changed(void *owner)
21{
22 CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
23 curves.tag_topology_changed();
24}
25
26static void tag_curve_types_changed(void *owner)
27{
28 CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
29 curves.update_curve_types();
30 curves.tag_topology_changed();
31}
32
33static void tag_positions_changed(void *owner)
34{
35 CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
36 curves.tag_positions_changed();
37}
38
39static void tag_radii_changed(void *owner)
40{
41 CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
42 curves.tag_radii_changed();
43}
44
45static void tag_normals_changed(void *owner)
46{
47 CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
48 curves.tag_normals_changed();
49}
50
51static void tag_material_index_changed(void *owner)
52{
53 CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
54 curves.tag_material_index_changed();
55}
56
57static const auto &changed_tags()
58{
59 static Map<StringRef, AttrUpdateOnChange> attributes{
60 {"position", tag_positions_changed},
61 {"radius", tag_radii_changed},
62 {"tilt", tag_normals_changed},
63 {"handle_left", tag_positions_changed},
64 {"handle_right", tag_positions_changed},
65 {"handle_type_left", tag_topology_changed},
66 {"handle_type_right", tag_topology_changed},
67 {"nurbs_weight", tag_positions_changed},
68 {"nurbs_order", tag_topology_changed},
69 {"normal_mode", tag_normals_changed},
70 {"custom_normal", tag_normals_changed},
71 {"curve_type", tag_curve_types_changed},
72 {"resolution", tag_topology_changed},
73 {"cyclic", tag_topology_changed},
74 {"material_index", tag_material_index_changed},
75 };
76 return attributes;
77}
78
79static int get_domain_size(const void *owner, const AttrDomain domain)
80{
81 const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
82 switch (domain) {
84 return curves.points_num();
86 return curves.curves_num();
87 default:
88 return 0;
89 }
90}
91
93 const Span<MDeformVert> dverts,
94 const int vertex_group_index)
95{
96 BLI_assert(vertex_group_index >= 0);
97 if (dverts.is_empty()) {
98 return {VArray<float>::from_single(0.0f, curves.points_num()), AttrDomain::Point};
99 }
100 return {varray_for_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
101}
102
103static GAttributeReader try_get_vertex_group(const void *owner, const StringRef attribute_id)
104{
105 const CurvesGeometry *curves = static_cast<const CurvesGeometry *>(owner);
106 if (curves == nullptr) {
107 return {};
108 }
109 const int vertex_group_index = BKE_defgroup_name_index(&curves->vertex_group_names,
110 attribute_id);
111 if (vertex_group_index < 0) {
112 return {};
113 }
114 const Span<MDeformVert> dverts = curves->deform_verts();
115 return reader_for_vertex_group_index(*curves, dverts, vertex_group_index);
116}
117
118static GAttributeWriter try_get_vertex_group_for_write(void *owner, const StringRef attribute_id)
119{
120 CurvesGeometry *curves = static_cast<CurvesGeometry *>(owner);
121 if (curves == nullptr) {
122 return {};
123 }
124 const int vertex_group_index = BKE_defgroup_name_index(&curves->vertex_group_names,
125 attribute_id);
126 if (vertex_group_index < 0) {
127 return {};
128 }
129 MutableSpan<MDeformVert> dverts = curves->deform_verts_for_write();
130 return {varray_for_mutable_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
131}
132
133static bool try_delete_vertex_group(void *owner, const StringRef name)
134{
135 CurvesGeometry *curves = static_cast<CurvesGeometry *>(owner);
136 if (curves == nullptr) {
137 return true;
138 }
139
140 int index;
141 bDeformGroup *group;
142 if (!BKE_defgroup_listbase_name_find(&curves->vertex_group_names, name, &index, &group)) {
143 return false;
144 }
145 BLI_remlink(&curves->vertex_group_names, group);
146 MEM_freeN(group);
147 if (curves->deform_verts().is_empty()) {
148 return true;
149 }
150
151 MutableSpan<MDeformVert> dverts = curves->deform_verts_for_write();
152 remove_defgroup_index(dverts, index);
153 return true;
154}
155
156static bool foreach_vertex_group(const void *owner, FunctionRef<void(const AttributeIter &)> fn)
157{
158 const CurvesGeometry *curves = static_cast<const CurvesGeometry *>(owner);
159 if (curves == nullptr) {
160 return true;
161 }
162 const AttributeAccessor accessor = curves->attributes();
163 const Span<MDeformVert> dverts = curves->deform_verts();
164 int group_index = 0;
165 LISTBASE_FOREACH_INDEX (const bDeformGroup *, group, &curves->vertex_group_names, group_index) {
166 const auto get_fn = [&]() {
167 return reader_for_vertex_group_index(*curves, dverts, group_index);
168 };
169 AttributeIter iter{group->name, AttrDomain::Point, bke::AttrType::Float, get_fn};
170 iter.is_builtin = false;
171 iter.accessor = &accessor;
172 fn(iter);
173 if (iter.is_stopped()) {
174 return false;
175 }
176 }
177 return true;
178}
179
180static const auto &builtin_attributes()
181{
182 static auto attributes = []() {
184
186 position.deletable = false;
187 map.add_new("position", std::move(position));
188
190 map.add_new("radius", std::move(radius));
191
193 map.add_new("tilt", std::move(tilt));
194
196 map.add_new("handle_left", std::move(handle_left));
197
199 map.add_new("handle_right", std::move(handle_right));
200
201 static auto handle_type_clamp = mf::build::SI1_SO<int8_t, int8_t>(
202 "Handle Type Validate",
203 [](int8_t value) {
204 return std::clamp<int8_t>(value, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN);
205 },
206 mf::build::exec_presets::AllSpanOrSingle());
207
209 handle_type_left.validator = AttributeValidator{&handle_type_clamp};
210 map.add_new("handle_type_left", std::move(handle_type_left));
211
213 handle_type_right.validator = AttributeValidator{&handle_type_clamp};
214 map.add_new("handle_type_right", std::move(handle_type_right));
215
216 static float default_nurbs_weight = 1.0f;
218 nurbs_weight.default_value = &default_nurbs_weight;
219 map.add_new("nurbs_weight", std::move(nurbs_weight));
220
221 static const auto nurbs_order_clamp = mf::build::SI1_SO<int8_t, int8_t>(
222 "NURBS Order Validate",
223 [](int8_t value) { return std::max<int8_t>(value, 1); },
224 mf::build::exec_presets::AllSpanOrSingle());
225 static int nurbs_order_default = 4;
227 nurbs_order.default_value = &nurbs_order_default;
228 nurbs_order.validator = AttributeValidator{&nurbs_order_clamp};
229 map.add_new("nurbs_order", std::move(nurbs_order));
230
231 static const auto normal_mode_clamp = mf::build::SI1_SO<int8_t, int8_t>(
232 "Normal Mode Validate",
233 [](int8_t value) {
234 return std::clamp<int8_t>(value, NORMAL_MODE_MINIMUM_TWIST, NORMAL_MODE_FREE);
235 },
236 mf::build::exec_presets::AllSpanOrSingle());
238 normal_mode.validator = AttributeValidator{&normal_mode_clamp};
239 map.add_new("normal_mode", std::move(normal_mode));
240
242 map.add_new("custom_normal", std::move(custom_normal));
243
244 static const auto knots_mode_clamp = mf::build::SI1_SO<int8_t, int8_t>(
245 "Knots Mode Validate",
246 [](int8_t value) {
247 return std::clamp<int8_t>(
249 },
250 mf::build::exec_presets::AllSpanOrSingle());
252 knots_mode.validator = AttributeValidator{&knots_mode_clamp};
253 map.add_new("knots_mode", std::move(knots_mode));
254
255 static const auto curve_type_clamp = mf::build::SI1_SO<int8_t, int8_t>(
256 "Curve Type Validate",
257 [](int8_t value) {
258 return std::clamp<int8_t>(value, CURVE_TYPE_CATMULL_ROM, CURVE_TYPES_NUM);
259 },
260 mf::build::exec_presets::AllSpanOrSingle());
262 curve_type.validator = AttributeValidator{&curve_type_clamp};
263 map.add_new("curve_type", std::move(curve_type));
264
265 static const auto resolution_clamp = mf::build::SI1_SO<int, int>(
266 "Resolution Validate",
267 [](int value) { return std::max<int>(value, 1); },
268 mf::build::exec_presets::AllSpanOrSingle());
269 static int resolution_default = 12;
271 resolution.default_value = &resolution_default;
272 resolution.validator = AttributeValidator{&resolution_clamp};
273 map.add_new("resolution", std::move(resolution));
274
276 map.add_new("cyclic", std::move(cyclic));
277
278 static const auto material_index_clamp = mf::build::SI1_SO<int, int>(
279 "Material Index Validate",
280 [](int value) {
281 /* Use #short for the maximum since many areas still use that type for indices. */
282 return std::clamp<int>(value, 0, std::numeric_limits<short>::max());
283 },
284 mf::build::exec_presets::AllSpanOrSingle());
286 material_index.validator = AttributeValidator{&material_index_clamp};
287 map.add_new("material_index", std::move(material_index));
288
289 return map;
290 }();
291 return attributes;
292}
293
295
297{
299 fn.domain_supported = [](const void * /*owner*/, const AttrDomain domain) {
301 };
302 fn.domain_size = get_domain_size;
303 fn.builtin_domain_and_type = [](const void * /*owner*/,
304 const StringRef name) -> std::optional<AttributeDomainAndType> {
305 const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name);
306 if (!info) {
307 return std::nullopt;
308 }
309 return AttributeDomainAndType{info->domain, info->type};
310 };
311 fn.get_builtin_default = [](const void * /*owner*/, StringRef name) -> GPointer {
312 const AttrBuiltinInfo &info = builtin_attributes().lookup(name);
313 return info.default_value;
314 };
315 fn.lookup = [](const void *owner, const StringRef name) -> GAttributeReader {
316 const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
317
318 if (GAttributeReader vertex_group = try_get_vertex_group(owner, name)) {
319 return vertex_group;
320 }
321
322 const AttributeStorage &storage = curves.attribute_storage.wrap();
323 const Attribute *attr = storage.lookup(name);
324 if (!attr) {
325 return {};
326 }
327 const int domain_size = get_domain_size(owner, attr->domain());
328 return attribute_to_reader(*attr, attr->domain(), domain_size);
329 };
330 fn.adapt_domain = [](const void *owner,
331 const GVArray &varray,
332 const AttrDomain from_domain,
333 const AttrDomain to_domain) -> GVArray {
334 const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
335 return curves.adapt_domain(varray, from_domain, to_domain);
336 };
337 fn.foreach_attribute = [](const void *owner,
338 const FunctionRef<void(const AttributeIter &)> fn,
339 const AttributeAccessor &accessor) {
340 const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
341
342 const bool should_continue = foreach_vertex_group(
343 owner, [&](const AttributeIter &iter) { fn(iter); });
344 if (!should_continue) {
345 return;
346 }
347
348 const AttributeStorage &storage = curves.attribute_storage.wrap();
349 storage.foreach_with_stop([&](const Attribute &attr) {
350 const auto get_fn = [&]() {
351 const int domain_size = get_domain_size(owner, attr.domain());
352 return attribute_to_reader(attr, attr.domain(), domain_size);
353 };
354 AttributeIter iter(attr.name(), attr.domain(), attr.data_type(), get_fn);
355 iter.is_builtin = builtin_attributes().contains(attr.name());
356 iter.accessor = &accessor;
357 fn(iter);
358 return !iter.is_stopped();
359 });
360 };
361 fn.lookup_validator = [](const void * /*owner*/, const StringRef name) -> AttributeValidator {
362 const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name);
363 if (!info) {
364 return {};
365 }
366 return info->validator;
367 };
368 fn.lookup_for_write = [](void *owner, const StringRef name) -> GAttributeWriter {
369 CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
370
371 if (GAttributeWriter vertex_group = try_get_vertex_group_for_write(owner, name)) {
372 return vertex_group;
373 }
374
375 AttributeStorage &storage = curves.attribute_storage.wrap();
376 Attribute *attr = storage.lookup(name);
377 if (!attr) {
378 return {};
379 }
380 const int domain_size = get_domain_size(owner, attr->domain());
381 return attribute_to_writer(&curves, changed_tags(), domain_size, *attr);
382 };
383 fn.remove = [](void *owner, const StringRef name) -> bool {
384 CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
385
386 if (try_delete_vertex_group(owner, name)) {
387 return true;
388 }
389
390 AttributeStorage &storage = curves.attribute_storage.wrap();
391 if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) {
392 if (!info->deletable) {
393 return false;
394 }
395 }
396 const std::optional<AttrUpdateOnChange> fn = changed_tags().lookup_try(name);
397 const bool removed = storage.remove(name);
398 if (!removed) {
399 return false;
400 }
401 if (fn) {
402 (*fn)(owner);
403 }
404 return true;
405 };
406 fn.add = [](void *owner,
407 const StringRef name,
408 const AttrDomain domain,
409 const AttrType type,
410 const AttributeInit &initializer) {
411 CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
412 const int domain_size = get_domain_size(owner, domain);
413 AttributeStorage &storage = curves.attribute_storage.wrap();
414 if (const AttrBuiltinInfo *info = builtin_attributes().lookup_ptr(name)) {
415 if (info->domain != domain || info->type != type) {
416 return false;
417 }
418 }
419 if (storage.lookup(name)) {
420 return false;
421 }
422 storage.add(name, domain, type, attribute_init_to_data(type, domain_size, initializer));
423 if (initializer.type != AttributeInit::Type::Construct) {
424 if (const std::optional<AttrUpdateOnChange> fn = changed_tags().lookup_try(name)) {
425 (*fn)(owner);
426 }
427 }
428 return true;
429 };
430
431 return fn;
432}
433
439
440} // namespace blender::bke::curves
Low-level operations for curves.
support for deformation groups and hooks.
bool BKE_defgroup_listbase_name_find(const ListBase *defbase, blender::StringRef name, int *r_index, bDeformGroup **r_group)
Definition deform.cc:554
int BKE_defgroup_name_index(const ListBase *defbase, blender::StringRef name)
Definition deform.cc:540
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
#define ELEM(...)
@ NORMAL_MODE_MINIMUM_TWIST
@ NORMAL_MODE_FREE
#define CURVE_TYPES_NUM
@ BEZIER_HANDLE_FREE
@ BEZIER_HANDLE_ALIGN
@ NURBS_KNOT_MODE_NORMAL
@ NURBS_KNOT_MODE_ENDPOINT_BEZIER
Object is a sort of wrapper for general info.
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
constexpr bool is_empty() const
Definition BLI_span.hh:260
static VArray from_single(T value, const int64_t size)
const AttributeAccessor * accessor
Attribute & add(std::string name, bke::AttrDomain domain, bke::AttrType data_type, Attribute::DataVariant data)
Attribute * lookup(StringRef name)
void foreach_with_stop(FunctionRef< bool(Attribute &)> fn)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static void tag_curve_types_changed(void *owner)
static void tag_topology_changed(void *owner)
static bool foreach_vertex_group(const void *owner, FunctionRef< void(const AttributeIter &)> fn)
static void tag_material_index_changed(void *owner)
static GAttributeWriter try_get_vertex_group_for_write(void *owner, const StringRef attribute_id)
static void tag_normals_changed(void *owner)
static const auto & changed_tags()
static void tag_radii_changed(void *owner)
static void tag_positions_changed(void *owner)
static int get_domain_size(const void *owner, const AttrDomain domain)
static const auto & builtin_attributes()
const AttributeAccessorFunctions & get_attribute_accessor_functions()
static bool try_delete_vertex_group(void *owner, const StringRef name)
static GAttributeReader reader_for_vertex_group_index(const CurvesGeometry &curves, const Span< MDeformVert > dverts, const int vertex_group_index)
static GAttributeReader try_get_vertex_group(const void *owner, const StringRef attribute_id)
static AttributeAccessorFunctions get_curves_accessor_functions()
GAttributeReader attribute_to_reader(const Attribute &attribute, const AttrDomain domain, const int64_t domain_size)
void remove_defgroup_index(MutableSpan< MDeformVert > dverts, int defgroup_index)
Definition deform.cc:1753
Attribute::DataVariant attribute_init_to_data(const bke::AttrType data_type, const int64_t domain_size, const AttributeInit &initializer)
GAttributeWriter attribute_to_writer(void *owner, const Map< StringRef, AttrUpdateOnChange > &changed_tags, const int64_t domain_size, Attribute &attribute)
VMutableArray< float > varray_for_mutable_deform_verts(MutableSpan< MDeformVert > dverts, int defgroup_index)
Definition deform.cc:1747
VArray< float > varray_for_deform_verts(Span< MDeformVert > dverts, int defgroup_index)
Definition deform.cc:1743
const char * name