Blender V4.3
transform_convert_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
9#include <optional>
10
11#include "BLI_array.hh"
12#include "BLI_array_utils.hh"
14#include "BLI_math_matrix.h"
15#include "BLI_span.hh"
16
17#include "BKE_attribute.hh"
18#include "BKE_curves.hh"
19#include "BKE_curves_utils.hh"
20
21#include "ED_curves.hh"
22
23#include "MEM_guardedalloc.h"
24
25#include "transform.hh"
26#include "transform_convert.hh"
27
28/* -------------------------------------------------------------------- */
33
35 const Span<float3> positions, MutableSpan<float> r_distances)
36{
37 Array<bool, 32> visited(positions.size(), false);
38
40 while (!queue.is_empty()) {
41 int64_t index = queue.pop_index();
42 if (visited[index]) {
43 continue;
44 }
45 visited[index] = true;
46
47 /* TODO(Falk): Handle cyclic curves here. */
48 if (index > 0 && !visited[index - 1]) {
49 int adjacent = index - 1;
50 float dist = r_distances[index] + math::distance(positions[index], positions[adjacent]);
51 if (dist < r_distances[adjacent]) {
52 r_distances[adjacent] = dist;
53 queue.priority_changed(adjacent);
54 }
55 }
56 if (index < positions.size() - 1 && !visited[index + 1]) {
57 int adjacent = index + 1;
58 float dist = r_distances[index] + math::distance(positions[index], positions[adjacent]);
59 if (dist < r_distances[adjacent]) {
60 r_distances[adjacent] = dist;
61 queue.priority_changed(adjacent);
62 }
63 }
64 }
65}
66
68 Span<float3> positions,
69 TransCustomData &custom_data)
70{
71 CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(custom_data.data);
72 transform_data.selection_by_layer.append(selection);
73 const int data_offset = transform_data.layer_offsets.last();
74 transform_data.layer_offsets.append(data_offset + selection.size());
76 positions,
77 selection,
78 transform_data.positions.as_mutable_span().slice(data_offset, selection.size()));
79 return transform_data.positions.as_mutable_span().slice(transform_data.layer_offsets.last(1),
80 selection.size());
81}
82
84{
86 Array<Vector<IndexMask>> points_to_transform_per_attribute(t->data_container_len);
87 Array<IndexMask> bezier_curves(t->data_container_len);
88 const bool use_proportional_edit = (t->flag & T_PROP_EDIT_ALL) != 0;
89 const bool use_connected_only = (t->flag & T_PROP_CONNECTED) != 0;
90
91 Vector<int> must_be_selected;
92
93 /* Count selected elements per object and create TransData structs. */
94 for (const int i : trans_data_contrainers.index_range()) {
95 TransDataContainer &tc = trans_data_contrainers[i];
96 Curves *curves_id = static_cast<Curves *>(tc.obedit->data);
97 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
99 tc.custom.type);
101 curves);
102 std::array<IndexMask, 3> selection_per_attribute;
103
104 for (const int attribute_i : selection_attribute_names.index_range()) {
105 const StringRef &selection_name = selection_attribute_names[attribute_i];
106 selection_per_attribute[attribute_i] = ed::curves::retrieve_selected_points(
107 curves, selection_name, curves_transform_data->memory);
108 }
109
110 bezier_curves[i] = bke::curves::indices_for_type(curves.curve_types(),
111 curves.curve_type_counts(),
113 curves.curves_range(),
114 curves_transform_data->memory);
115 /* Alter selection as in legacy curves bezt_select_to_transform_triple_flag(). */
116 if (!bezier_curves[i].is_empty()) {
117 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
118 const VArray<int8_t> handle_types_left = curves.handle_types_left();
119 const VArray<int8_t> handle_types_right = curves.handle_types_right();
120
121 must_be_selected.clear();
122 bezier_curves[i].foreach_index([&](const int bezier_index) {
123 for (const int point_i : points_by_curve[bezier_index]) {
124 if (selection_per_attribute[0].contains(point_i)) {
125 const HandleType type_left = HandleType(handle_types_left[point_i]);
126 const HandleType type_right = HandleType(handle_types_right[point_i]);
129 {
130 must_be_selected.append(point_i);
131 }
132 }
133 }
134 });
135
136 /* Select bezier handles that must be transformed if the main control point is selected. */
137 IndexMask must_be_selected_mask = IndexMask::from_indices(must_be_selected.as_span(),
138 curves_transform_data->memory);
139 if (must_be_selected.size()) {
140 selection_per_attribute[1] = IndexMask::from_union(
141 selection_per_attribute[1], must_be_selected_mask, curves_transform_data->memory);
142 selection_per_attribute[2] = IndexMask::from_union(
143 selection_per_attribute[2], must_be_selected_mask, curves_transform_data->memory);
144 }
145 }
146
147 if (use_proportional_edit) {
148 Array<int> bezier_point_offset_data(bezier_curves[i].size() + 1);
150 curves.points_by_curve(), bezier_curves[i], bezier_point_offset_data);
151
152 const int bezier_point_count = bezier_offsets.total_size();
153 tc.data_len = curves.points_num() + 2 * bezier_point_count;
154 points_to_transform_per_attribute[i].append(curves.points_range());
155
156 if (bezier_point_count > 0) {
158 OffsetIndices<int> points_by_curve = curves.points_by_curve();
159 bezier_curves[i].foreach_index(GrainSize(512), [&](const int bezier_curve_i) {
160 bezier_point_ranges.append(points_by_curve[bezier_curve_i]);
161 });
162 IndexMask bezier_points = IndexMask::from_initializers(bezier_point_ranges,
163 curves_transform_data->memory);
164 points_to_transform_per_attribute[i].append(bezier_points);
165 points_to_transform_per_attribute[i].append(bezier_points);
166 }
167 }
168 else {
169 tc.data_len = 0;
170 for (const int selection_i : selection_attribute_names.index_range()) {
171 points_to_transform_per_attribute[i].append(selection_per_attribute[selection_i]);
172 tc.data_len += points_to_transform_per_attribute[i][selection_i].size();
173 }
174 }
175
176 if (tc.data_len > 0) {
177 tc.data = MEM_cnew_array<TransData>(tc.data_len, __func__);
178 curves_transform_data->positions.reinitialize(tc.data_len);
179 }
180 else {
181 tc.custom.type.free_cb(t, &tc, &tc.custom.type);
182 }
183 }
184
185 /* Populate TransData structs. */
186 for (const int i : trans_data_contrainers.index_range()) {
187 TransDataContainer &tc = trans_data_contrainers[i];
188 if (tc.data_len == 0) {
189 continue;
190 }
191 Object *object = tc.obedit;
192 Curves *curves_id = static_cast<Curves *>(object->data);
193 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
194
195 std::optional<MutableSpan<float>> value_attribute;
196 bke::SpanAttributeWriter<float> attribute_writer;
197 if (t->mode == TFM_CURVE_SHRINKFATTEN) {
198 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
199 attribute_writer = attributes.lookup_or_add_for_write_span<float>(
200 "radius",
202 bke::AttributeInitVArray(VArray<float>::ForSingle(0.01f, curves.points_num())));
203 value_attribute = attribute_writer.span;
204 }
205 else if (t->mode == TFM_TILT) {
206 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
207 attribute_writer = attributes.lookup_or_add_for_write_span<float>("tilt",
209 value_attribute = attribute_writer.span;
210 }
211
213 curves,
214 object->object_to_world(),
215 value_attribute,
216 points_to_transform_per_attribute[i],
217 curves.curves_range(),
218 use_connected_only,
219 bezier_curves[i]);
220
221 /* TODO: This is wrong. The attribute writer should live at least as long as the span. */
222 attribute_writer.finish();
223 }
224}
225
227{
228 const Span<TransDataContainer> trans_data_contrainers(t->data_container, t->data_container_len);
229 for (const TransDataContainer &tc : trans_data_contrainers) {
230 Curves *curves_id = static_cast<Curves *>(tc.obedit->data);
231 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
232 if (t->mode == TFM_CURVE_SHRINKFATTEN) {
233 /* No cache to update currently. */
234 }
235 else if (t->mode == TFM_TILT) {
236 curves.tag_normals_changed();
237 }
238 else {
239 const Vector<MutableSpan<float3>> positions_per_selection_attr =
241 for (const int i : positions_per_selection_attr.index_range()) {
243 tc.custom.type, i, positions_per_selection_attr[i]);
244 }
245 curves.tag_positions_changed();
246 curves.calculate_bezier_auto_handles();
247 }
249 }
250}
251
253{
254 const CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(
255 custom_data.data);
256 return OffsetIndices(transform_data.layer_offsets.as_span().slice(
257 transform_data.layer_offsets.size() - num - 1, num + 1));
258}
259
267static void fill_map(const CurveType curve_type,
268 const IndexRange curve_points,
269 const OffsetIndices<int> position_offsets_in_td,
270 const int handles_offset,
272{
273 const int position_index = curve_points.start() + position_offsets_in_td[0].start();
274 if (curve_type == CURVE_TYPE_BEZIER) {
275 const int left_handle_index = handles_offset + position_offsets_in_td[1].start();
276 const int right_handle_index = handles_offset + position_offsets_in_td[2].start();
277 std::array<int, 3> first_per_attr = {left_handle_index, position_index, right_handle_index};
278 threading::parallel_for(curve_points.index_range(), 4096, [&](const IndexRange range) {
279 for (const int i : range) {
280 for (const int attr : IndexRange(3)) {
281 map[i * 3 + attr] = first_per_attr[attr] + i;
282 }
283 }
284 });
285 }
286 else {
287 array_utils::fill_index_range(map, position_index);
288 }
289}
290
291} // namespace blender::ed::transform::curves
292
294{
295 CurvesTransformData *transform_data = MEM_new<CurvesTransformData>(__func__);
296 transform_data->layer_offsets.append(0);
297 custom_data.data = transform_data;
298 custom_data.free_cb = [](TransInfo *, TransDataContainer *, TransCustomData *custom_data) {
299 CurvesTransformData *data = static_cast<CurvesTransformData *>(custom_data->data);
300 MEM_delete(data);
301 custom_data->data = nullptr;
302 };
303 return transform_data;
304}
305
307 const TransCustomData &custom_data,
308 const int layer,
310{
311 using namespace blender;
312 const CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(
313 custom_data.data);
314 const IndexMask &selection = transform_data.selection_by_layer[layer];
315 OffsetIndices<int> offsets{transform_data.layer_offsets};
316 Span<float3> positions = transform_data.positions.as_span().slice(offsets[layer]);
317
318 array_utils::scatter(positions, selection, positions_dst);
319}
320
324 const blender::float4x4 &transform,
325 std::optional<blender::MutableSpan<float>> value_attribute,
326 const blender::Span<blender::IndexMask> points_to_transform_per_attr,
327 const blender::IndexMask &affected_curves,
328 bool use_connected_only,
329 const blender::IndexMask &bezier_curves)
330{
331 using namespace blender;
332 const std::array<Span<float3>, 3> src_positions_per_selection_attr = {
333 curves.positions(), curves.handle_positions_left(), curves.handle_positions_right()};
334 std::array<MutableSpan<float3>, 3> positions_per_selection_attr;
335
336 for (const int selection_i : points_to_transform_per_attr.index_range()) {
337 positions_per_selection_attr[selection_i] =
338 ed::transform::curves::append_positions_to_custom_data(
339 points_to_transform_per_attr[selection_i],
340 src_positions_per_selection_attr[selection_i],
341 tc.custom.type);
342 }
343
344 float mtx[3][3], smtx[3][3];
345 copy_m3_m4(mtx, transform.ptr());
347
348 MutableSpan<TransData> all_tc_data = MutableSpan(tc.data, tc.data_len);
349 OffsetIndices<int> position_offsets_in_td = ed::transform::curves::recent_position_offsets(
350 tc.custom.type, points_to_transform_per_attr.size());
351
352 Vector<VArray<bool>> selection_attrs;
353 Span<StringRef> selection_attribute_names = ed::curves::get_curves_selection_attribute_names(
354 curves);
355 for (const StringRef selection_name : selection_attribute_names) {
356 const VArray<bool> selection_attr = *curves.attributes().lookup_or_default<bool>(
357 selection_name, bke::AttrDomain::Point, true);
358 selection_attrs.append(selection_attr);
359 }
360
361 for (const int selection_i : position_offsets_in_td.index_range()) {
362 if (position_offsets_in_td[selection_i].is_empty()) {
363 continue;
364 }
365 MutableSpan<TransData> tc_data = all_tc_data.slice(position_offsets_in_td[selection_i]);
366 MutableSpan<float3> positions = positions_per_selection_attr[selection_i];
367 IndexMask points_to_transform = points_to_transform_per_attr[selection_i];
368 VArray<bool> selection = selection_attrs[selection_i];
369
370 threading::parallel_for(points_to_transform.index_range(), 1024, [&](const IndexRange range) {
371 for (const int tranform_point_i : range) {
372 const int point_in_domain_i = points_to_transform[tranform_point_i];
373 TransData &td = tc_data[tranform_point_i];
374 float3 *elem = &positions[tranform_point_i];
375
376 copy_v3_v3(td.iloc, *elem);
377 copy_v3_v3(td.center, td.iloc);
378 td.loc = *elem;
379
380 td.flag = 0;
381 if (selection[point_in_domain_i]) {
382 td.flag = TD_SELECTED;
383 }
384
385 if (value_attribute) {
386 float *value = &((*value_attribute)[point_in_domain_i]);
387 td.val = value;
388 td.ival = *value;
389 }
390 td.ext = nullptr;
391
392 copy_m3_m3(td.smtx, smtx);
393 copy_m3_m3(td.mtx, mtx);
394 }
395 });
396 }
397 if (use_connected_only) {
398 const VArray<int8_t> curve_types = curves.curve_types();
399 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
400 Array<int> bezier_offsets_in_td(curves.curves_num() + 1, 0);
401 offset_indices::copy_group_sizes(points_by_curve, bezier_curves, bezier_offsets_in_td);
402 offset_indices::accumulate_counts_to_offsets(bezier_offsets_in_td);
403
404 affected_curves.foreach_segment(GrainSize(512), [&](const IndexMaskSegment segment) {
405 Array<int> map;
406 Array<float> closest_distances;
407 Array<float3> mapped_curve_positions;
408
409 for (const int curve_i : segment) {
410 const int selection_attrs_num = curve_types[curve_i] == CURVE_TYPE_BEZIER ? 3 : 1;
411 const IndexRange curve_points = points_by_curve[curve_i];
412 const int total_curve_points = selection_attrs_num * curve_points.size();
413 map.reinitialize(total_curve_points);
414 closest_distances.reinitialize(total_curve_points);
415 closest_distances.fill(std::numeric_limits<float>::max());
416 mapped_curve_positions.reinitialize(total_curve_points);
417
418 ed::transform::curves::fill_map(CurveType(curve_types[curve_i]),
419 curve_points,
420 position_offsets_in_td,
421 bezier_offsets_in_td[curve_i],
422 map);
423
424 bool has_any_selected = false;
425 for (const int selection_attr_i : IndexRange(selection_attrs_num)) {
426 has_any_selected = has_any_selected ||
427 ed::curves::has_anything_selected(selection_attrs[selection_attr_i],
428 curve_points);
429 }
430 if (!has_any_selected) {
431 for (const int i : map) {
432 TransData &td = all_tc_data[i];
433 td.flag |= TD_SKIP;
434 }
435 continue;
436 }
437
438 for (const int i : closest_distances.index_range()) {
439 TransData &td = all_tc_data[map[i]];
440 mapped_curve_positions[i] = td.loc;
441 if (td.flag & TD_SELECTED) {
442 closest_distances[i] = 0.0f;
443 }
444 }
446 mapped_curve_positions.as_span(), closest_distances.as_mutable_span());
447 for (const int i : closest_distances.index_range()) {
448 TransData &td = all_tc_data[map[i]];
449 td.dist = closest_distances[i];
450 }
451 }
452 });
453 }
454}
455
459 /*flags*/ (T_EDIT | T_POINTS),
462 /*special_aftertrans_update*/ nullptr,
463};
Low-level operations for curves.
Low-level operations for curves.
void pseudoinverse_m3_m3(float inverse[3][3], const float mat[3][3], float epsilon)
void copy_m3_m4(float m1[3][3], const float m2[4][4])
#define PSEUDOINVERSE_EPSILON
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
CurveType
@ CURVE_TYPE_BEZIER
HandleType
@ BEZIER_HANDLE_ALIGN
@ BEZIER_HANDLE_AUTO
@ TFM_CURVE_SHRINKFATTEN
@ TFM_TILT
Read Guarded memory(de)allocation.
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int64_t size() const
Definition BLI_array.hh:245
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
void fill(const T &value) const
Definition BLI_array.hh:261
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
constexpr int64_t size() const
constexpr int64_t start() const
constexpr IndexRange index_range() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
IndexRange index_range() const
Span< T > as_span() const
static IndexMask from_union(const IndexMask &mask_a, const IndexMask &mask_b, IndexMaskMemory &memory)
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
static IndexMask from_initializers(const Span< Initializer > initializers, IndexMaskMemory &memory)
Set< ComponentNode * > visited
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void fill_index_range(MutableSpan< T > span, const T start=0)
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)
Vector< MutableSpan< float3 > > get_curves_positions_for_write(bke::CurvesGeometry &curves)
Span< StringRef > get_curves_selection_attribute_names(const bke::CurvesGeometry &curves)
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
static void recalcData_curves(TransInfo *t)
static MutableSpan< float3 > append_positions_to_custom_data(const IndexMask selection, Span< float3 > positions, TransCustomData &custom_data)
static OffsetIndices< int > recent_position_offsets(TransCustomData &custom_data, int num)
static void fill_map(const CurveType curve_type, const IndexRange curve_points, const OffsetIndices< int > position_offsets_in_td, const int handles_offset, MutableSpan< int > map)
static void createTransCurvesVerts(bContext *, TransInfo *t)
static void calculate_curve_point_distances_for_proportional_editing(const Span< float3 > positions, MutableSpan< float > r_distances)
T distance(const T &a, const T &b)
OffsetIndices< int > gather_selected_offsets(OffsetIndices< int > src_offsets, const IndexMask &selection, int start_offset, MutableSpan< int > dst_offsets)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
__int64 int64_t
Definition stdint.h:89
blender::IndexMaskMemory memory
blender::Vector< int > layer_offsets
blender::Array< blender::float3 > positions
blender::Vector< blender::IndexMask > selection_by_layer
CurvesGeometry geometry
TransCustomData type
Definition transform.hh:425
void(* free_cb)(TransInfo *, TransDataContainer *tc, TransCustomData *custom_data)
Definition transform.hh:409
TransCustomDataContainer custom
Definition transform.hh:501
TransData * data
Definition transform.hh:445
eTfmMode mode
Definition transform.hh:517
int data_container_len
Definition transform.hh:506
eTFlag flag
Definition transform.hh:523
TransDataContainer * data_container
Definition transform.hh:505
@ T_PROP_CONNECTED
Definition transform.hh:99
@ T_POINTS
Definition transform.hh:93
@ T_EDIT
Definition transform.hh:91
#define T_PROP_EDIT_ALL
Definition transform.hh:157
conversion and adaptation of different datablocks to a common struct.
void curve_populate_trans_data_structs(TransDataContainer &tc, blender::bke::CurvesGeometry &curves, const blender::float4x4 &transform, std::optional< blender::MutableSpan< float > > value_attribute, const blender::Span< blender::IndexMask > points_to_transform_indices, const blender::IndexMask &affected_curves, bool use_connected_only, const blender::IndexMask &bezier_curves)
CurvesTransformData * create_curves_transform_custom_data(TransCustomData &custom_data)
void copy_positions_from_curves_transform_custom_data(const TransCustomData &custom_data, const int layer, blender::MutableSpan< blender::float3 > positions_dst)
CurvesTransformData * create_curves_transform_custom_data(TransCustomData &custom_data)
void curve_populate_trans_data_structs(TransDataContainer &tc, blender::bke::CurvesGeometry &curves, const blender::float4x4 &transform, std::optional< blender::MutableSpan< float > > value_attribute, const blender::Span< blender::IndexMask > points_to_transform_per_attr, const blender::IndexMask &affected_curves, bool use_connected_only, const blender::IndexMask &bezier_curves)
void copy_positions_from_curves_transform_custom_data(const TransCustomData &custom_data, const int layer, blender::MutableSpan< blender::float3 > positions_dst)
TransConvertTypeInfo TransConvertType_Curves
@ TD_SELECTED
@ TD_SKIP