Blender V4.5
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
8
9#include <optional>
10
11#include "BLI_array.hh"
12#include "BLI_array_utils.hh"
15#include "BLI_math_matrix.h"
16#include "BLI_span.hh"
17
18#include "BKE_attribute.hh"
19#include "BKE_context.hh"
20#include "BKE_crazyspace.hh"
21#include "BKE_curves.hh"
22#include "BKE_curves_utils.hh"
23#include "BKE_geometry_set.hh"
24
25#include "ED_curves.hh"
26
27#include "MEM_guardedalloc.h"
28
29#include "transform.hh"
30#include "transform_convert.hh"
31#include "transform_snap.hh"
32
33/* -------------------------------------------------------------------- */
36
38
40 const Span<IndexMask> points_to_transform_per_attr,
41 TransCustomData &custom_data)
42{
43 if (points_to_transform_per_attr.size() == 1) {
44 return;
45 }
46 const VArraySpan<int8_t> handle_types_left = curves.handle_types_left();
47 const VArraySpan<int8_t> handle_types_right = curves.handle_types_right();
48 CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(custom_data.data);
49
50 IndexMaskMemory memory;
51 /* When control point is selected both handles are threaded as selected and transformed together.
52 * So these will be excluded from alignment. */
53 const IndexMask &selected_points = points_to_transform_per_attr[0];
54 const IndexMask selected_left_handles = IndexMask::from_difference(
55 points_to_transform_per_attr[1], selected_points, memory);
57 /* Left are excluded here to align only one handle when both are selected. */
58 const IndexMask selected_right_handles = evaluate_expression(
59 builder.subtract({&points_to_transform_per_attr[2]},
60 {&selected_left_handles, &selected_points}),
61 memory);
62
63 const IndexMask &affected_handles = IndexMask::from_union(
64 selected_left_handles, selected_right_handles, memory);
65
66 auto aligned_handles_to_selection = [&](const VArraySpan<int8_t> &handle_types) {
68 affected_handles, GrainSize(4096), memory, [&](const int64_t i) {
69 return handle_types[i] == BEZIER_HANDLE_ALIGN;
70 });
71 };
72
73 const IndexMask both_aligned = IndexMask::from_intersection(
74 aligned_handles_to_selection(handle_types_left),
75 aligned_handles_to_selection(handle_types_right),
76 memory);
77
79 selected_left_handles, both_aligned, transform_data.memory);
81 selected_right_handles, both_aligned, transform_data.memory);
82}
83
85 MutableSpan<float> r_distances)
86{
87 BLI_assert(positions.size() == r_distances.size());
88 Array<bool, 32> visited(positions.size(), false);
89
91 while (!queue.is_empty()) {
92 int64_t index = queue.pop_index();
93 if (visited[index]) {
94 continue;
95 }
96 visited[index] = true;
97
98 const int left_i = index - 1;
99 if (left_i >= 0 && !visited[left_i]) {
100 const float left_dist = r_distances[index] +
101 math::distance(positions[index], positions[left_i]);
102 if (left_dist < r_distances[left_i]) {
103 r_distances[left_i] = left_dist;
104 queue.priority_increased(left_i);
105 }
106 }
107
108 const int right_i = index + 1;
109 if (right_i < positions.size() && !visited[right_i]) {
110 const float right_dist = r_distances[index] +
111 math::distance(positions[index], positions[right_i]);
112 if (right_dist < r_distances[right_i]) {
113 r_distances[right_i] = right_dist;
114 queue.priority_increased(right_i);
115 }
116 }
117 }
118}
119
121 MutableSpan<float> r_distances)
122{
123 BLI_assert(positions.size() == r_distances.size());
124 Array<bool, 32> visited(positions.size(), false);
125
126 InplacePriorityQueue<float, std::less<>> queue(r_distances);
127 while (!queue.is_empty()) {
128 int64_t index = queue.pop_index();
129 if (visited[index]) {
130 continue;
131 }
132 visited[index] = true;
133
134 const int left_i = math::mod_periodic<int>(index - 1, positions.size());
135 const float left_dist = r_distances[index] +
136 math::distance(positions[index], positions[left_i]);
137 if (left_dist < r_distances[left_i] && !visited[left_i]) {
138 r_distances[left_i] = left_dist;
139 queue.priority_increased(left_i);
140 }
141
142 const int right_i = math::mod_periodic<int>(index + 1, positions.size());
143 const float right_dist = r_distances[index] +
144 math::distance(positions[index], positions[right_i]);
145 if (right_dist < r_distances[right_i] && !visited[right_i]) {
146 r_distances[right_i] = right_dist;
147 queue.priority_increased(right_i);
148 }
149 }
150}
151
153 const HandleType type,
155 IndexMaskMemory &memory)
156{
158 handles, GrainSize(4096), memory, [&](const int64_t i) { return types[i] == type; });
159}
160
161void update_vector_handle_types(const IndexMask &selected_handles,
162 MutableSpan<int8_t> handle_types)
163{
164 IndexMaskMemory memory;
165 /* Selected BEZIER_HANDLE_VECTOR handles. */
166 const IndexMask convert_to_free = handles_by_type(
167 selected_handles, BEZIER_HANDLE_VECTOR, handle_types, memory);
168 index_mask::masked_fill(handle_types, int8_t(BEZIER_HANDLE_FREE), convert_to_free);
169}
170
171static void update_auto_handle_types(const IndexMask &auto_handles,
172 const IndexMask &auto_handles_opposite,
173 const IndexMask &selected_handles,
174 const IndexMask &selected_handles_opposite,
175 MutableSpan<int8_t> handle_types,
176 IndexMaskMemory &memory)
177{
179 const IndexMask &convert_to_align = evaluate_expression(
180 builder.merge({
181 /* Selected BEZIER_HANDLE_AUTO handles from one side. */
182 &builder.intersect({&selected_handles, &auto_handles}),
183 /* Both sides are BEZIER_HANDLE_AUTO and opposite side is selected.
184 * It ensures to convert both handles, when only one is transformed. */
185 &builder.intersect({&selected_handles_opposite, &auto_handles_opposite, &auto_handles}),
186 }),
187 memory);
188 index_mask::masked_fill(handle_types, int8_t(BEZIER_HANDLE_ALIGN), convert_to_align);
189}
190
191void update_auto_handle_types(const IndexMask &selected_handles_left,
192 const IndexMask &selected_handles_right,
193 const IndexMask &bezier_points,
194 MutableSpan<int8_t> handle_types_left,
195 MutableSpan<int8_t> handle_types_right)
196{
197 IndexMaskMemory memory;
198 const IndexMask auto_left = handles_by_type(
199 bezier_points, BEZIER_HANDLE_AUTO, handle_types_left, memory);
200 const IndexMask auto_right = handles_by_type(
201 bezier_points, BEZIER_HANDLE_AUTO, handle_types_right, memory);
202
203 update_auto_handle_types(auto_left,
204 auto_right,
205 selected_handles_left,
206 selected_handles_right,
207 handle_types_left,
208 memory);
209 update_auto_handle_types(auto_right,
210 auto_left,
211 selected_handles_right,
212 selected_handles_left,
213 handle_types_right,
214 memory);
215}
216
218 Span<float3> positions,
219 TransCustomData &custom_data)
220{
221 CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(custom_data.data);
222 transform_data.selection_by_layer.append(selection);
223 const int data_offset = transform_data.layer_offsets.last();
224 transform_data.layer_offsets.append(data_offset + selection.size());
226 positions,
227 selection,
228 transform_data.positions.as_mutable_span().slice(data_offset, selection.size()));
229 return transform_data.positions.as_mutable_span().slice(transform_data.layer_offsets.last(1),
230 selection.size());
231}
232
234{
236 Array<Vector<IndexMask>> points_to_transform_per_attribute(t->data_container_len);
237 Array<IndexMask> bezier_curves(t->data_container_len);
238 const bool use_proportional_edit = (t->flag & T_PROP_EDIT_ALL) != 0;
239 const bool use_connected_only = (t->flag & T_PROP_CONNECTED) != 0;
240
241 /* Evaluated depsgraph is necessary for taking into account deformation from modifiers. */
243
244 /* Count selected elements per object and create TransData structs. */
245 for (const int i : trans_data_contrainers.index_range()) {
246 TransDataContainer &tc = trans_data_contrainers[i];
247 Curves *curves_id = static_cast<Curves *>(tc.obedit->data);
248 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
250 tc.custom.type);
252 curves);
253 std::array<IndexMask, 3> selection_per_attribute;
254
255 for (const int attribute_i : selection_attribute_names.index_range()) {
256 const StringRef &selection_name = selection_attribute_names[attribute_i];
257 selection_per_attribute[attribute_i] = ed::curves::retrieve_selected_points(
258 curves, selection_name, curves_transform_data->memory);
259 }
260
261 bezier_curves[i] = bke::curves::indices_for_type(curves.curve_types(),
262 curves.curve_type_counts(),
264 curves.curves_range(),
265 curves_transform_data->memory);
266
268 curves.points_by_curve(), bezier_curves[i], curves_transform_data->memory);
269
270 /* Alter selection as in legacy curves bezt_select_to_transform_triple_flag(). */
271 if (!bezier_points.is_empty()) {
272 IndexMaskMemory memory;
273 /* Selected handles, but not the control point. */
274 const IndexMask selected_left = IndexMask::from_difference(
275 selection_per_attribute[1], selection_per_attribute[0], memory);
276 const IndexMask selected_right = IndexMask::from_difference(
277 selection_per_attribute[2], selection_per_attribute[0], memory);
278 MutableSpan<int8_t> handle_types_left = curves.handle_types_left_for_write();
279 MutableSpan<int8_t> handle_types_right = curves.handle_types_right_for_write();
280
281 update_vector_handle_types(selected_left, handle_types_left);
282 update_vector_handle_types(selected_right, handle_types_right);
284 selected_left, selected_right, bezier_points, handle_types_left, handle_types_right);
285 curves.tag_topology_changed();
286
288 const index_mask::Expr &selected_bezier_points = builder.intersect(
289 {&bezier_points, selection_per_attribute.data()});
290
291 /* Select bezier handles that must be transformed because the control point is
292 * selected. */
293 selection_per_attribute[1] = evaluate_expression(
294 builder.merge({&selection_per_attribute[1], &selected_bezier_points}),
295 curves_transform_data->memory);
296 selection_per_attribute[2] = evaluate_expression(
297 builder.merge({&selection_per_attribute[2], &selected_bezier_points}),
298 curves_transform_data->memory);
299 }
300
301 if (use_proportional_edit) {
302 tc.data_len = curves.points_num() + 2 * bezier_points.size();
303 points_to_transform_per_attribute[i].append(curves.points_range());
304
305 if (selection_attribute_names.size() > 1) {
306 points_to_transform_per_attribute[i].append(bezier_points);
307 points_to_transform_per_attribute[i].append(bezier_points);
308 }
309 }
310 else {
311 tc.data_len = 0;
312 for (const int selection_i : selection_attribute_names.index_range()) {
313 points_to_transform_per_attribute[i].append(selection_per_attribute[selection_i]);
314 tc.data_len += points_to_transform_per_attribute[i][selection_i].size();
315 }
316 }
317
318 if (tc.data_len > 0) {
319 tc.data = MEM_calloc_arrayN<TransData>(tc.data_len, __func__);
320 curves_transform_data->positions.reinitialize(tc.data_len);
321 }
322 else {
323 tc.custom.type.free_cb(t, &tc, &tc.custom.type);
324 }
325 }
326
327 /* Populate TransData structs. */
328 for (const int i : trans_data_contrainers.index_range()) {
329 TransDataContainer &tc = trans_data_contrainers[i];
330 if (tc.data_len == 0) {
331 continue;
332 }
333 Object *object = tc.obedit;
334 Curves *curves_id = static_cast<Curves *>(object->data);
335 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
336 const bke::crazyspace::GeometryDeformation deformation =
338
339 std::optional<MutableSpan<float>> value_attribute;
340 bke::SpanAttributeWriter<float> attribute_writer;
341 if (t->mode == TFM_CURVE_SHRINKFATTEN) {
342 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
343 attribute_writer = attributes.lookup_or_add_for_write_span<float>(
344 "radius",
347 value_attribute = attribute_writer.span;
348 }
349 else if (t->mode == TFM_TILT) {
350 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
351 attribute_writer = attributes.lookup_or_add_for_write_span<float>("tilt",
353 value_attribute = attribute_writer.span;
354 }
355
357 tc,
358 curves,
359 object->object_to_world(),
360 deformation,
361 value_attribute,
362 points_to_transform_per_attribute[i],
363 curves.curves_range(),
364 use_connected_only,
365 bezier_curves[i]);
366 create_aligned_handles_masks(curves, points_to_transform_per_attribute[i], tc.custom.type);
367
368 /* TODO: This is wrong. The attribute writer should live at least as long as the span. */
369 attribute_writer.finish();
370 }
371}
372
373static void calculate_aligned_handles(const TransCustomData &custom_data,
375{
377 return;
378 }
379 const CurvesTransformData &transform_data = *static_cast<const CurvesTransformData *>(
380 custom_data.data);
381
382 const Span<float3> positions = curves.positions();
383 MutableSpan<float3> handle_positions_left = curves.handle_positions_left_for_write();
384 MutableSpan<float3> handle_positions_right = curves.handle_positions_right_for_write();
385
387 transform_data.aligned_with_left, positions, handle_positions_left, handle_positions_right);
389 transform_data.aligned_with_right, positions, handle_positions_right, handle_positions_left);
390}
391
393{
394 if (t->state != TRANS_CANCEL) {
396 }
397
398 const Span<TransDataContainer> trans_data_contrainers(t->data_container, t->data_container_len);
399 for (const TransDataContainer &tc : trans_data_contrainers) {
400 Curves *curves_id = static_cast<Curves *>(tc.obedit->data);
401 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
402 if (t->mode == TFM_CURVE_SHRINKFATTEN) {
403 curves.tag_radii_changed();
404 }
405 else if (t->mode == TFM_TILT) {
406 curves.tag_normals_changed();
407 }
408 else {
409 const Vector<MutableSpan<float3>> positions_per_selection_attr =
411 for (const int i : positions_per_selection_attr.index_range()) {
413 tc.custom.type, i, positions_per_selection_attr[i]);
414 }
415 curves.tag_positions_changed();
416 curves.calculate_bezier_auto_handles();
417 calculate_aligned_handles(tc.custom.type, curves);
418 }
420 }
421}
422
424{
425 const CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(
426 custom_data.data);
427 return OffsetIndices(transform_data.layer_offsets.as_span().slice(
428 transform_data.layer_offsets.size() - num - 1, num + 1));
429}
430
438static void fill_map(const CurveType curve_type,
439 const IndexRange curve_points,
440 const OffsetIndices<int> position_offsets_in_td,
441 const int handles_offset,
443{
444 const int position_index = curve_points.start() + position_offsets_in_td[0].start();
445 if (curve_type == CURVE_TYPE_BEZIER) {
446 const int left_handle_index = handles_offset + position_offsets_in_td[1].start();
447 const int right_handle_index = handles_offset + position_offsets_in_td[2].start();
448 std::array<int, 3> first_per_attr = {left_handle_index, position_index, right_handle_index};
449 threading::parallel_for(curve_points.index_range(), 4096, [&](const IndexRange range) {
450 for (const int i : range) {
451 for (const int attr : IndexRange(3)) {
452 map[i * 3 + attr] = first_per_attr[attr] + i;
453 }
454 }
455 });
456 }
457 else {
458 array_utils::fill_index_range(map, position_index);
459 }
460}
461
463{
464 CurvesTransformData *transform_data = MEM_new<CurvesTransformData>(__func__);
465 transform_data->layer_offsets.append(0);
466 custom_data.data = transform_data;
467 custom_data.free_cb = [](TransInfo *, TransDataContainer *, TransCustomData *custom_data) {
468 CurvesTransformData *data = static_cast<CurvesTransformData *>(custom_data->data);
469 MEM_delete(data);
470 custom_data->data = nullptr;
471 };
472 return transform_data;
473}
474
476 const int layer,
477 MutableSpan<float3> positions_dst)
478{
479 const CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(
480 custom_data.data);
481 const IndexMask &selection = transform_data.selection_by_layer[layer];
482 OffsetIndices<int> offsets{transform_data.layer_offsets};
483 Span<float3> positions = transform_data.positions.as_span().slice(offsets[layer]);
484
485 array_utils::scatter(positions, selection, positions_dst);
486}
487
491 const float4x4 &transform,
492 const bke::crazyspace::GeometryDeformation &deformation,
493 std::optional<MutableSpan<float>> value_attribute,
494 const Span<IndexMask> points_to_transform_per_attr,
495 const IndexMask &affected_curves,
496 bool use_connected_only,
497 const IndexMask &bezier_curves,
498 void *extra)
499{
500 const std::array<Span<float3>, 3> src_positions_per_selection_attr = {
501 curves.positions(), curves.handle_positions_left(), curves.handle_positions_right()};
502 const View3D *v3d = static_cast<const View3D *>(t.view);
503 const bool hide_handles = (v3d != nullptr) ? (v3d->overlay.handle_display == CURVE_HANDLE_NONE) :
504 false;
505 const bool use_individual_origin = (t.around == V3D_AROUND_LOCAL_ORIGINS);
506 const Span<float3> point_positions = curves.positions();
507 const VArray<bool> cyclic = curves.cyclic();
508 const VArray<bool> point_selection = *curves.attributes().lookup_or_default<bool>(
509 ".selection", bke::AttrDomain::Point, true);
510 const VArray<int8_t> curve_types = curves.curve_types();
511
512 std::array<MutableSpan<float3>, 3> positions_per_selection_attr;
513 for (const int selection_i : points_to_transform_per_attr.index_range()) {
514 positions_per_selection_attr[selection_i] = append_positions_to_custom_data(
515 points_to_transform_per_attr[selection_i],
516 src_positions_per_selection_attr[selection_i],
517 tc.custom.type);
518 }
519
520 MutableSpan<TransData> all_tc_data = MutableSpan(tc.data, tc.data_len);
521 OffsetIndices<int> position_offsets_in_td = recent_position_offsets(
522 tc.custom.type, points_to_transform_per_attr.size());
523
524 Vector<VArray<bool>> selection_attrs;
526 curves);
527 for (const StringRef selection_name : selection_attribute_names) {
528 const VArray<bool> selection_attr = *curves.attributes().lookup_or_default<bool>(
529 selection_name, bke::AttrDomain::Point, true);
530 selection_attrs.append(selection_attr);
531 }
532
533 const float3x3 mtx_base = transform.view<3, 3>();
534 const float3x3 smtx_base = math::pseudo_invert(mtx_base);
535
536 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
537 Array<float3> mean_center_point_per_curve(curves.curves_num(), float3(0));
538 if (use_individual_origin) {
539 affected_curves.foreach_index(GrainSize(512), [&](const int64_t curve_i) {
540 const IndexRange points = points_by_curve[curve_i];
541 IndexMaskMemory memory;
542 const IndexMask selection =
543 IndexMask::from_bools(point_selection, memory).slice_content(points);
544 if (selection.is_empty()) {
545 /* For proportional editing around individual origins, unselected points will not use the
546 * TransData center (instead the closest point found is used, see logic in #set_prop_dist /
547 * #prop_dist_loc_get). */
548 return;
549 }
550 float3 center(0.0f);
551 selection.foreach_index([&](const int64_t point_i) { center += point_positions[point_i]; });
552 center /= selection.size();
553 mean_center_point_per_curve[curve_i] = center;
554 });
555 }
556
557 const Array<int> point_to_curve_map = curves.point_to_curve_map();
558 for (const int selection_i : position_offsets_in_td.index_range()) {
559 if (position_offsets_in_td[selection_i].is_empty()) {
560 continue;
561 }
562 MutableSpan<TransData> tc_data = all_tc_data.slice(position_offsets_in_td[selection_i]);
563 MutableSpan<float3> positions = positions_per_selection_attr[selection_i];
564 const IndexMask points_to_transform = points_to_transform_per_attr[selection_i];
565 const VArray<bool> selection = selection_attrs[selection_i];
566
567 points_to_transform.foreach_index(
568 GrainSize(1024), [&](const int64_t domain_i, const int64_t transform_i) {
569 const int curve_i = point_to_curve_map[domain_i];
570
571 TransData &td = tc_data[transform_i];
572 float3 *elem = &positions[transform_i];
573
574 float3 center;
575 const bool use_local_center = hide_handles || use_individual_origin ||
576 point_selection[domain_i];
577 const bool use_mean_center = use_individual_origin &&
578 !(curve_types[curve_i] == CURVE_TYPE_BEZIER);
579 if (use_mean_center) {
580 center = mean_center_point_per_curve[curve_i];
581 }
582 else if (use_local_center) {
583 center = point_positions[domain_i];
584 }
585 else {
586 center = *elem;
587 }
588
589 copy_v3_v3(td.iloc, *elem);
590 copy_v3_v3(td.center, center);
591 td.loc = *elem;
592
593 td.flag = 0;
594 if (selection[domain_i]) {
595 td.flag = TD_SELECTED;
596 }
597
598 td.extra = extra;
599
600 if (value_attribute) {
601 float *value = &((*value_attribute)[domain_i]);
602 td.val = value;
603 td.ival = *value;
604 }
605 td.ext = nullptr;
606
607 if (deformation.deform_mats.is_empty()) {
608 copy_m3_m3(td.smtx, smtx_base.ptr());
609 copy_m3_m3(td.mtx, mtx_base.ptr());
610 }
611 else {
612 const float3x3 mtx = deformation.deform_mats[domain_i] * mtx_base;
613 const float3x3 smtx = math::pseudo_invert(mtx);
614 copy_m3_m3(td.smtx, smtx.ptr());
615 copy_m3_m3(td.mtx, mtx.ptr());
616 }
617 });
618 }
619 if (use_connected_only) {
620 Array<int> curves_offsets_in_td_buffer(curves.curves_num() + 1, 0);
621 affected_curves.foreach_index(GrainSize(512), [&](const int64_t curve) {
622 curves_offsets_in_td_buffer[curve] =
623 points_to_transform_per_attr[0].slice_content(points_by_curve[curve]).size();
624 });
625 offset_indices::accumulate_counts_to_offsets(curves_offsets_in_td_buffer);
626 const OffsetIndices<int> curves_offsets_in_td(curves_offsets_in_td_buffer);
627
628 Array<int> bezier_offsets_in_td(curves.curves_num() + 1, 0);
629 offset_indices::copy_group_sizes(points_by_curve, bezier_curves, bezier_offsets_in_td);
631
632 affected_curves.foreach_segment(GrainSize(512), [&](const IndexMaskSegment segment) {
633 Array<int> map;
634 Array<float> closest_distances;
635 Array<float3> mapped_curve_positions;
636
637 for (const int curve_i : segment) {
638 const int selection_attrs_num = curve_types[curve_i] == CURVE_TYPE_BEZIER ? 3 : 1;
639 const IndexRange curve_points = points_by_curve[curve_i];
640 const IndexRange editable_curve_points = curves_offsets_in_td[curve_i];
641 const int total_curve_points = selection_attrs_num * editable_curve_points.size();
642 map.reinitialize(total_curve_points);
643 closest_distances.reinitialize(total_curve_points);
644 closest_distances.fill(std::numeric_limits<float>::max());
645 mapped_curve_positions.reinitialize(total_curve_points);
646
647 fill_map(CurveType(curve_types[curve_i]),
648 editable_curve_points,
649 position_offsets_in_td,
650 bezier_offsets_in_td[curve_i],
651 map);
652
653 bool has_any_selected = false;
654 for (const int selection_attr_i : IndexRange(selection_attrs_num)) {
655 has_any_selected = has_any_selected ||
656 ed::curves::has_anything_selected(selection_attrs[selection_attr_i],
657 curve_points);
658 }
659 if (!has_any_selected) {
660 for (const int i : map) {
661 TransData &td = all_tc_data[i];
662 td.flag |= TD_SKIP;
663 }
664 continue;
665 }
666
667 for (const int i : closest_distances.index_range()) {
668 TransData &td = all_tc_data[map[i]];
669 mapped_curve_positions[i] = td.loc;
670 if (td.flag & TD_SELECTED) {
671 closest_distances[i] = 0.0f;
672 }
673 }
674
675 if (cyclic[curve_i]) {
676 cyclic_curve_connected_point_distances(mapped_curve_positions.as_span(),
677 closest_distances.as_mutable_span());
678 }
679 else {
680 curve_connected_point_distances(mapped_curve_positions.as_span(),
681 closest_distances.as_mutable_span());
682 }
683
684 for (const int i : closest_distances.index_range()) {
685 TransData &td = all_tc_data[map[i]];
686 td.dist = closest_distances[i];
687 }
688 }
689 });
690 }
691}
692
694
696 /*flags*/ (T_EDIT | T_POINTS),
697 /*create_trans_data*/ createTransCurvesVerts,
698 /*recalc_data*/ recalcData_curves,
699 /*special_aftertrans_update*/ nullptr,
700};
701
702} // namespace blender::ed::transform::curves
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Low-level operations for curves.
Low-level operations for curves.
#define BLI_assert(a)
Definition BLI_assert.h:46
void copy_m3_m3(float m1[3][3], const float m2[3][3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
ATTR_WARN_UNUSED_RESULT const size_t num
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
CurveType
@ CURVE_TYPE_BEZIER
HandleType
@ BEZIER_HANDLE_FREE
@ BEZIER_HANDLE_ALIGN
@ BEZIER_HANDLE_VECTOR
@ BEZIER_HANDLE_AUTO
@ V3D_AROUND_LOCAL_ORIGINS
@ CURVE_HANDLE_NONE
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
BMesh const char void * data
BPy_StructRNA * depsgraph
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
static IndexMask from_difference(const IndexMask &mask_a, const IndexMask &mask_b, IndexMaskMemory &memory)
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
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
IndexRange index_range() const
Definition BLI_array.hh:349
void fill(const T &value) const
Definition BLI_array.hh:261
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:398
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_union(const IndexMask &mask_a, const IndexMask &mask_b, IndexMaskMemory &memory)
static IndexMask from_intersection(const IndexMask &mask_a, const IndexMask &mask_b, IndexMaskMemory &memory)
static IndexMask from_difference(const IndexMask &mask_a, const IndexMask &mask_b, IndexMaskMemory &memory)
constexpr int64_t size() const
constexpr int64_t start() const
constexpr IndexRange index_range() const
void priority_increased(const int64_t index)
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
static VArray ForSingle(T value, const int64_t size)
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
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
const IntersectionExpr & intersect(const Span< Term > terms)
const UnionExpr & merge(const Span< Term > terms)
const DifferenceExpr & subtract(const Term &main_term, const Span< Term > subtract_terms)
IndexMask slice_content(IndexRange range) const
void foreach_index(Fn &&fn) const
void foreach_segment(Fn &&fn) const
static char ** types
Definition makesdna.cc:71
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void scatter(const Span< T > src, const Span< IndexT > indices, MutableSpan< T > dst, const int64_t grain_size=4096)
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)
GeometryDeformation get_evaluated_curves_deformation(const Object *ob_eval, const Object &ob_orig)
void calculate_aligned_handles(const IndexMask &selection, Span< float3 > positions, Span< float3 > align_by, MutableSpan< float3 > align)
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)
IndexMask curve_to_point_selection(OffsetIndices< int > points_by_curve, const IndexMask &curve_selection, IndexMaskMemory &memory)
static bool has_anything_selected(const Span< Curves * > curves_ids)
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)
void update_vector_handle_types(const IndexMask &selected_handles, MutableSpan< int8_t > handle_types)
static void recalcData_curves(TransInfo *t)
void curve_populate_trans_data_structs(const TransInfo &t, TransDataContainer &tc, bke::CurvesGeometry &curves, const float4x4 &transform, const bke::crazyspace::GeometryDeformation &deformation, std::optional< MutableSpan< float > > value_attribute, const Span< IndexMask > points_to_transform_per_attr, const IndexMask &affected_curves, bool use_connected_only, const IndexMask &bezier_curves, void *extra=nullptr)
void copy_positions_from_curves_transform_custom_data(const TransCustomData &custom_data, const int layer, MutableSpan< float3 > positions_dst)
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)
CurvesTransformData * create_curves_transform_custom_data(TransCustomData &custom_data)
void update_auto_handle_types(const IndexMask &selected_handles_left, const IndexMask &selected_handles_right, const IndexMask &bezier_points, MutableSpan< int8_t > handle_types_left, MutableSpan< int8_t > handle_types_right)
static void cyclic_curve_connected_point_distances(const Span< float3 > positions, MutableSpan< float > r_distances)
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 IndexMask handles_by_type(const IndexMask handles, const HandleType type, Span< int8_t > types, IndexMaskMemory &memory)
static void create_aligned_handles_masks(const bke::CurvesGeometry &curves, const Span< IndexMask > points_to_transform_per_attr, TransCustomData &custom_data)
static void curve_connected_point_distances(const Span< float3 > positions, MutableSpan< float > r_distances)
static void calculate_aligned_handles(const TransCustomData &custom_data, bke::CurvesGeometry &curves)
static void createTransCurvesVerts(bContext *C, TransInfo *t)
void transform_snap_project_individual_apply(TransInfo *t)
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
T distance(const T &a, const T &b)
MatBase< T, Size, Size > pseudo_invert(const MatBase< T, Size, Size > &mat, T epsilon=1e-8)
T mod_periodic(const T &a, const T &b)
void copy_group_sizes(OffsetIndices< int > offsets, const IndexMask &mask, MutableSpan< int > sizes)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
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
MatBase< float, 4, 4 > float4x4
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
CurvesGeometry geometry
View3DOverlay overlay
const c_style_mat & ptr() const
void(* free_cb)(TransInfo *, TransDataContainer *tc, TransCustomData *custom_data)
Definition transform.hh:628
TransDataContainer * data_container
Definition transform.hh:797
i
Definition text_draw.cc:230
#define T_PROP_EDIT_ALL
Definition transform.hh:28
conversion and adaptation of different datablocks to a common struct.
ParamHandle ** handles