Blender V5.0
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 const int curve_index,
42 TransCustomData &custom_data)
43{
44 if (points_to_transform_per_attr.size() == 1) {
45 return;
46 }
47 const VArraySpan<int8_t> handle_types_left = curves.handle_types_left();
48 const VArraySpan<int8_t> handle_types_right = curves.handle_types_right();
49 CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(custom_data.data);
50
51 IndexMaskMemory memory;
52 /* When control point is selected both handles are threaded as selected and transformed together.
53 * So these will be excluded from alignment. */
54 const IndexMask &selected_points = points_to_transform_per_attr[0];
55 const IndexMask selected_left_handles = IndexMask::from_difference(
56 points_to_transform_per_attr[1], selected_points, memory);
58 /* Left are excluded here to align only one handle when both are selected. */
59 const IndexMask selected_right_handles = evaluate_expression(
60 builder.subtract({&points_to_transform_per_attr[2]},
61 {&selected_left_handles, &selected_points}),
62 memory);
63
64 const IndexMask &affected_handles = IndexMask::from_union(
65 selected_left_handles, selected_right_handles, memory);
66
67 auto aligned_handles_to_selection = [&](const VArraySpan<int8_t> &handle_types) {
69 affected_handles, GrainSize(4096), memory, [&](const int64_t i) {
70 return handle_types[i] == BEZIER_HANDLE_ALIGN;
71 });
72 };
73
74 const IndexMask both_aligned = IndexMask::from_intersection(
75 aligned_handles_to_selection(handle_types_left),
76 aligned_handles_to_selection(handle_types_right),
77 memory);
78
79 transform_data.aligned_with_left[curve_index] = IndexMask::from_intersection(
80 selected_left_handles, both_aligned, transform_data.memory);
81 transform_data.aligned_with_right[curve_index] = IndexMask::from_intersection(
82 selected_right_handles, both_aligned, transform_data.memory);
83}
84
86 MutableSpan<float> r_distances)
87{
88 BLI_assert(positions.size() == r_distances.size());
89 Array<bool, 32> visited(positions.size(), false);
90
92 while (!queue.is_empty()) {
93 int64_t index = queue.pop_index();
94 if (visited[index]) {
95 continue;
96 }
97 visited[index] = true;
98
99 const int left_i = index - 1;
100 if (left_i >= 0 && !visited[left_i]) {
101 const float left_dist = r_distances[index] +
102 math::distance(positions[index], positions[left_i]);
103 if (left_dist < r_distances[left_i]) {
104 r_distances[left_i] = left_dist;
105 queue.priority_increased(left_i);
106 }
107 }
108
109 const int right_i = index + 1;
110 if (right_i < positions.size() && !visited[right_i]) {
111 const float right_dist = r_distances[index] +
112 math::distance(positions[index], positions[right_i]);
113 if (right_dist < r_distances[right_i]) {
114 r_distances[right_i] = right_dist;
115 queue.priority_increased(right_i);
116 }
117 }
118 }
119}
120
122 MutableSpan<float> r_distances)
123{
124 BLI_assert(positions.size() == r_distances.size());
125 Array<bool, 32> visited(positions.size(), false);
126
127 InplacePriorityQueue<float, std::less<>> queue(r_distances);
128 while (!queue.is_empty()) {
129 int64_t index = queue.pop_index();
130 if (visited[index]) {
131 continue;
132 }
133 visited[index] = true;
134
135 const int left_i = math::mod_periodic<int>(index - 1, positions.size());
136 const float left_dist = r_distances[index] +
137 math::distance(positions[index], positions[left_i]);
138 if (left_dist < r_distances[left_i] && !visited[left_i]) {
139 r_distances[left_i] = left_dist;
140 queue.priority_increased(left_i);
141 }
142
143 const int right_i = math::mod_periodic<int>(index + 1, positions.size());
144 const float right_dist = r_distances[index] +
145 math::distance(positions[index], positions[right_i]);
146 if (right_dist < r_distances[right_i] && !visited[right_i]) {
147 r_distances[right_i] = right_dist;
148 queue.priority_increased(right_i);
149 }
150 }
151}
152
154 const VArray<int8_t> &types,
155 const HandleType type,
156 IndexMaskMemory &memory)
157{
158 if (const std::optional<int8_t> single_type = types.get_if_single()) {
159 return HandleType(*single_type) == type ? handles : IndexMask();
160 }
161 const VArraySpan types_span = types;
163 handles, GrainSize(4096), memory, [&](const int64_t i) { return types_span[i] == type; });
164}
165
167 const IndexMask &auto_handles,
168 const IndexMask &auto_handles_opposite,
169 const IndexMask &selected_handles,
170 const IndexMask &selected_handles_opposite,
171 const StringRef handle_type_name,
172 IndexMaskMemory &memory)
173{
175 const IndexMask convert_to_align = evaluate_expression(
176 builder.merge({
177 /* Selected BEZIER_HANDLE_AUTO handles from one side. */
178 &builder.intersect({&selected_handles, &auto_handles}),
179 /* Both sides are BEZIER_HANDLE_AUTO and opposite side is selected.
180 * It ensures to convert both handles, when only one is transformed. */
181 &builder.intersect({&selected_handles_opposite, &auto_handles_opposite, &auto_handles}),
182 }),
183 memory);
184 if (convert_to_align.is_empty()) {
185 return false;
186 }
187 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
188 bke::SpanAttributeWriter handle_types = attributes.lookup_or_add_for_write_span<int8_t>(
189 handle_type_name, bke::AttrDomain::Point);
190 index_mask::masked_fill(handle_types.span, int8_t(BEZIER_HANDLE_ALIGN), convert_to_align);
191 handle_types.finish();
192 return true;
193}
194
196 const IndexMask &selected_handles,
197 const StringRef handle_type_name)
198{
199 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
200
201 IndexMaskMemory memory;
202 const IndexMask selected_vector = handles_by_type(
203 selected_handles,
204 *attributes.lookup_or_default<int8_t>(handle_type_name, bke::AttrDomain::Point, 0),
206 memory);
207 if (selected_vector.is_empty()) {
208 return false;
209 }
210
211 bke::SpanAttributeWriter handle_types = attributes.lookup_or_add_for_write_span<int8_t>(
212 handle_type_name, bke::AttrDomain::Point);
213 index_mask::masked_fill(handle_types.span, int8_t(BEZIER_HANDLE_FREE), selected_vector);
214 handle_types.finish();
215 return true;
216}
217
219 const std::array<IndexMask, 3> &selection_per_attribute,
220 const IndexMask &bezier_points,
222{
223 IndexMaskMemory memory;
224
225 const IndexMask selected_left = IndexMask::from_difference(
226 selection_per_attribute[1], selection_per_attribute[0], memory);
227 const IndexMask selected_right = IndexMask::from_difference(
228 selection_per_attribute[2], selection_per_attribute[0], memory);
229
230 const IndexMask auto_left = handles_by_type(
231 bezier_points, curves.handle_types_left(), BEZIER_HANDLE_AUTO, memory);
232 const IndexMask auto_right = handles_by_type(
233 bezier_points, curves.handle_types_right(), BEZIER_HANDLE_AUTO, memory);
234
235 bool changed = false;
236
237 if (ELEM(mode, TFM_ROTATION, TFM_RESIZE) && selection_per_attribute[0].size() == 1 &&
238 selected_left.is_empty() && selected_right.is_empty())
239 {
240 const int64_t selected_point = selection_per_attribute[0].first();
241 if (auto_left.contains(selected_point)) {
242 curves.handle_types_left_for_write()[selected_point] = BEZIER_HANDLE_ALIGN;
243 changed = true;
244 }
245 if (auto_right.contains(selected_point)) {
246 curves.handle_types_right_for_write()[selected_point] = BEZIER_HANDLE_ALIGN;
247 changed = true;
248 }
249 }
250 else {
251 changed |= update_auto_handle_types(
252 curves, auto_left, auto_right, selected_left, selected_right, "handle_type_left", memory);
253 changed |= update_auto_handle_types(
254 curves, auto_right, auto_left, selected_right, selected_left, "handle_type_right", memory);
255
256 changed |= update_vector_handle_types(curves, selected_left, "handle_type_left");
257 changed |= update_vector_handle_types(curves, selected_right, "handle_type_right");
258 }
259
260 if (changed) {
261 curves.tag_topology_changed();
262 }
263
264 return changed;
265}
266
268 Span<float3> positions,
269 TransCustomData &custom_data)
270{
271 CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(custom_data.data);
272 transform_data.selection_by_layer.append(selection);
273 const int data_offset = transform_data.layer_offsets.last();
274 transform_data.layer_offsets.append(data_offset + selection.size());
276 positions,
277 selection,
278 transform_data.positions.as_mutable_span().slice(data_offset, selection.size()));
279 return transform_data.positions.as_mutable_span().slice(transform_data.layer_offsets.last(1),
280 selection.size());
281}
282
284{
286 Array<Vector<IndexMask>> points_to_transform_per_attribute(t->data_container_len);
287 Array<IndexMask> bezier_curves(t->data_container_len);
288 const bool use_proportional_edit = (t->flag & T_PROP_EDIT_ALL) != 0;
289 const bool use_connected_only = (t->flag & T_PROP_CONNECTED) != 0;
290
291 /* Evaluated depsgraph is necessary for taking into account deformation from modifiers. */
293
294 /* Count selected elements per object and create TransData structs. */
295 for (const int i : trans_data_contrainers.index_range()) {
296 TransDataContainer &tc = trans_data_contrainers[i];
297 Curves *curves_id = static_cast<Curves *>(tc.obedit->data);
298 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
300 tc.custom.type);
302 curves);
303 std::array<IndexMask, 3> selection_per_attribute;
304
305 bezier_curves[i] = bke::curves::indices_for_type(curves.curve_types(),
306 curves.curve_type_counts(),
308 curves.curves_range(),
309 curves_transform_data->memory);
310
312 curves.points_by_curve(), bezier_curves[i], curves_transform_data->memory);
313
314 for (const int attribute_i : selection_attribute_names.index_range()) {
315 const StringRef &selection_name = selection_attribute_names[attribute_i];
316 selection_per_attribute[attribute_i] = ed::curves::retrieve_selected_points(
317 curves, selection_name, bezier_points, curves_transform_data->memory);
318 }
319
320 /* Alter selection as in legacy curves bezt_select_to_transform_triple_flag(). */
321 if (!bezier_points.is_empty()) {
322 update_handle_types_for_transform(t->mode, selection_per_attribute, bezier_points, curves);
323
325 const index_mask::Expr &selected_bezier_points = builder.intersect(
326 {&bezier_points, selection_per_attribute.data()});
327
328 /* Select bezier handles that must be transformed because the control point is
329 * selected. */
330 selection_per_attribute[1] = evaluate_expression(
331 builder.merge({&selection_per_attribute[1], &selected_bezier_points}),
332 curves_transform_data->memory);
333 selection_per_attribute[2] = evaluate_expression(
334 builder.merge({&selection_per_attribute[2], &selected_bezier_points}),
335 curves_transform_data->memory);
336 }
337
338 if (use_proportional_edit) {
339 tc.data_len = curves.points_num() + 2 * bezier_points.size();
340 points_to_transform_per_attribute[i].append(curves.points_range());
341
342 if (selection_attribute_names.size() > 1) {
343 points_to_transform_per_attribute[i].append(bezier_points);
344 points_to_transform_per_attribute[i].append(bezier_points);
345 }
346 }
347 else {
348 tc.data_len = 0;
349 for (const int selection_i : selection_attribute_names.index_range()) {
350 points_to_transform_per_attribute[i].append(selection_per_attribute[selection_i]);
351 tc.data_len += points_to_transform_per_attribute[i][selection_i].size();
352 }
353 }
354
355 if (tc.data_len > 0) {
356 tc.data = MEM_calloc_arrayN<TransData>(tc.data_len, __func__);
357 curves_transform_data->positions.reinitialize(tc.data_len);
358 }
359 else {
360 tc.custom.type.free_cb(t, &tc, &tc.custom.type);
361 }
362 }
363
364 /* Populate TransData structs. */
365 for (const int i : trans_data_contrainers.index_range()) {
366 TransDataContainer &tc = trans_data_contrainers[i];
367 if (tc.data_len == 0) {
368 continue;
369 }
370 Object *object = tc.obedit;
371 Curves *curves_id = static_cast<Curves *>(object->data);
372 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
373 const bke::crazyspace::GeometryDeformation deformation =
375
376 std::optional<MutableSpan<float>> value_attribute;
377 bke::SpanAttributeWriter<float> attribute_writer;
378 if (t->mode == TFM_CURVE_SHRINKFATTEN) {
379 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
380 attribute_writer = attributes.lookup_or_add_for_write_span<float>(
381 "radius",
384 value_attribute = attribute_writer.span;
385 }
386 else if (t->mode == TFM_TILT) {
387 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
388 attribute_writer = attributes.lookup_or_add_for_write_span<float>("tilt",
390 value_attribute = attribute_writer.span;
391 }
392
393 CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(tc.custom.type.data);
394 transform_data.aligned_with_left.reinitialize(1);
395 transform_data.aligned_with_right.reinitialize(1);
396
398 tc,
399 curves,
400 object->object_to_world(),
401 deformation,
402 value_attribute,
403 points_to_transform_per_attribute[i],
404 curves.curves_range(),
405 use_connected_only,
406 bezier_curves[i]);
407 create_aligned_handles_masks(curves, points_to_transform_per_attribute[i], 0, tc.custom.type);
408
409 /* TODO: This is wrong. The attribute writer should live at least as long as the span. */
410 attribute_writer.finish();
411 }
412}
413
416 const int curve_index)
417{
419 return;
420 }
421 const CurvesTransformData &transform_data = *static_cast<const CurvesTransformData *>(
422 custom_data.data);
423
424 const Span<float3> positions = curves.positions();
425 MutableSpan<float3> handle_positions_left = curves.handle_positions_left_for_write();
426 MutableSpan<float3> handle_positions_right = curves.handle_positions_right_for_write();
427
429 positions,
430 handle_positions_left,
431 handle_positions_right);
433 positions,
434 handle_positions_right,
435 handle_positions_left);
436}
437
439{
440 if (t->state != TRANS_CANCEL) {
442 }
443
444 const Span<TransDataContainer> trans_data_contrainers(t->data_container, t->data_container_len);
445 for (const TransDataContainer &tc : trans_data_contrainers) {
446 Curves *curves_id = static_cast<Curves *>(tc.obedit->data);
447 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
448 if (t->mode == TFM_CURVE_SHRINKFATTEN) {
449 curves.tag_radii_changed();
450 }
451 else if (t->mode == TFM_TILT) {
452 curves.tag_normals_changed();
453 }
454 else {
455 const Vector<MutableSpan<float3>> positions_per_selection_attr =
457 for (const int i : positions_per_selection_attr.index_range()) {
459 tc.custom.type, i, positions_per_selection_attr[i]);
460 }
461 curves.tag_positions_changed();
462 curves.calculate_bezier_auto_handles();
463 calculate_aligned_handles(tc.custom.type, curves, 0);
464 }
466 }
467}
468
470{
471 const CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(
472 custom_data.data);
473 return OffsetIndices(transform_data.layer_offsets.as_span().slice(
474 transform_data.layer_offsets.size() - num - 1, num + 1));
475}
476
484static void fill_map(const CurveType curve_type,
485 const IndexRange curve_points,
486 const OffsetIndices<int> position_offsets_in_td,
487 const int handles_offset,
489{
490 const int position_index = curve_points.start() + position_offsets_in_td[0].start();
491 if (curve_type == CURVE_TYPE_BEZIER) {
492 const int left_handle_index = handles_offset + position_offsets_in_td[1].start();
493 const int right_handle_index = handles_offset + position_offsets_in_td[2].start();
494 std::array<int, 3> first_per_attr = {left_handle_index, position_index, right_handle_index};
495 threading::parallel_for(curve_points.index_range(), 4096, [&](const IndexRange range) {
496 for (const int i : range) {
497 for (const int attr : IndexRange(3)) {
498 map[i * 3 + attr] = first_per_attr[attr] + i;
499 }
500 }
501 });
502 }
503 else {
504 array_utils::fill_index_range(map, position_index);
505 }
506}
507
509{
510 CurvesTransformData *transform_data = MEM_new<CurvesTransformData>(__func__);
511 transform_data->layer_offsets.append(0);
512 custom_data.data = transform_data;
513 custom_data.free_cb = [](TransInfo *, TransDataContainer *, TransCustomData *custom_data) {
514 CurvesTransformData *data = static_cast<CurvesTransformData *>(custom_data->data);
515 MEM_delete(data);
516 custom_data->data = nullptr;
517 };
518 return transform_data;
519}
520
522 const int layer,
523 MutableSpan<float3> positions_dst)
524{
525 const CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(
526 custom_data.data);
527 const IndexMask &selection = transform_data.selection_by_layer[layer];
528 OffsetIndices<int> offsets{transform_data.layer_offsets};
529 Span<float3> positions = transform_data.positions.as_span().slice(offsets[layer]);
530
531 array_utils::scatter(positions, selection, positions_dst);
532}
533
537 const float4x4 &transform,
538 const bke::crazyspace::GeometryDeformation &deformation,
539 std::optional<MutableSpan<float>> value_attribute,
540 const Span<IndexMask> points_to_transform_per_attr,
541 const IndexMask &affected_curves,
542 bool use_connected_only,
543 const IndexMask &bezier_curves,
544 void *extra)
545{
546 const std::array<Span<float3>, 3> src_positions_per_selection_attr = {
547 curves.positions(),
548 curves.handle_positions_left().value_or(Span<float3>()),
549 curves.handle_positions_right().value_or(Span<float3>())};
550 const View3D *v3d = static_cast<const View3D *>(t.view);
551 const bool hide_handles = (v3d != nullptr) ? (v3d->overlay.handle_display == CURVE_HANDLE_NONE) :
552 false;
553 const bool use_individual_origin = (t.around == V3D_AROUND_LOCAL_ORIGINS);
554 const Span<float3> point_positions = curves.positions();
555 const VArray<bool> cyclic = curves.cyclic();
556 const VArray<bool> point_selection = *curves.attributes().lookup_or_default<bool>(
557 ".selection", bke::AttrDomain::Point, true);
558 const VArray<int8_t> curve_types = curves.curve_types();
559
560 std::array<MutableSpan<float3>, 3> positions_per_selection_attr;
561 for (const int selection_i : points_to_transform_per_attr.index_range()) {
562 positions_per_selection_attr[selection_i] = append_positions_to_custom_data(
563 points_to_transform_per_attr[selection_i],
564 src_positions_per_selection_attr[selection_i],
565 tc.custom.type);
566 }
567
568 MutableSpan<TransData> all_tc_data = MutableSpan(tc.data, tc.data_len);
569 OffsetIndices<int> position_offsets_in_td = recent_position_offsets(
570 tc.custom.type, points_to_transform_per_attr.size());
571
572 Vector<VArray<bool>> selection_attrs;
574 curves);
575 for (const StringRef selection_name : selection_attribute_names) {
576 const VArray<bool> selection_attr = *curves.attributes().lookup_or_default<bool>(
577 selection_name, bke::AttrDomain::Point, true);
578 selection_attrs.append(selection_attr);
579 }
580
581 const float3x3 mtx_base = transform.view<3, 3>();
582 const float3x3 smtx_base = math::pseudo_invert(mtx_base);
583
584 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
585 Array<float3> mean_center_point_per_curve(curves.curves_num(), float3(0));
586 if (use_individual_origin) {
587 affected_curves.foreach_index(GrainSize(512), [&](const int64_t curve_i) {
588 const IndexRange points = points_by_curve[curve_i];
589 IndexMaskMemory memory;
590 const IndexMask selection =
591 IndexMask::from_bools(point_selection, memory).slice_content(points);
592 if (selection.is_empty()) {
593 /* For proportional editing around individual origins, unselected points will not use the
594 * TransData center (instead the closest point found is used, see logic in #set_prop_dist /
595 * #prop_dist_loc_get). */
596 return;
597 }
598 float3 center(0.0f);
599 selection.foreach_index([&](const int64_t point_i) { center += point_positions[point_i]; });
600 center /= selection.size();
601 mean_center_point_per_curve[curve_i] = center;
602 });
603 }
604
605 const Array<int> point_to_curve_map = curves.point_to_curve_map();
606 for (const int selection_i : position_offsets_in_td.index_range()) {
607 if (position_offsets_in_td[selection_i].is_empty()) {
608 continue;
609 }
610 MutableSpan<TransData> tc_data = all_tc_data.slice(position_offsets_in_td[selection_i]);
611 MutableSpan<float3> positions = positions_per_selection_attr[selection_i];
612 const IndexMask points_to_transform = points_to_transform_per_attr[selection_i];
613 const VArray<bool> selection = selection_attrs[selection_i];
614
615 points_to_transform.foreach_index(
616 GrainSize(1024), [&](const int64_t domain_i, const int64_t transform_i) {
617 const int curve_i = point_to_curve_map[domain_i];
618
619 TransData &td = tc_data[transform_i];
620 float3 *elem = &positions[transform_i];
621
622 float3 center;
623 const bool use_local_center = hide_handles || use_individual_origin ||
624 point_selection[domain_i];
625 const bool use_mean_center = use_individual_origin &&
626 !(curve_types[curve_i] == CURVE_TYPE_BEZIER);
627 if (use_mean_center) {
628 center = mean_center_point_per_curve[curve_i];
629 }
630 else if (use_local_center) {
631 center = point_positions[domain_i];
632 }
633 else {
634 center = *elem;
635 }
636
637 copy_v3_v3(td.iloc, *elem);
638 copy_v3_v3(td.center, center);
639 td.loc = *elem;
640
641 td.flag = 0;
642 if (selection[domain_i]) {
643 td.flag = TD_SELECTED;
644 }
645
646 td.extra = extra;
647
648 if (value_attribute) {
649 float *value = &((*value_attribute)[domain_i]);
650 td.val = value;
651 td.ival = *value;
652 }
653 else {
654 td.val = nullptr;
655 }
656
657 if (deformation.deform_mats.is_empty()) {
658 copy_m3_m3(td.smtx, smtx_base.ptr());
659 copy_m3_m3(td.mtx, mtx_base.ptr());
660 }
661 else {
662 const float3x3 mtx = deformation.deform_mats[domain_i] * mtx_base;
663 const float3x3 smtx = math::pseudo_invert(mtx);
664 copy_m3_m3(td.smtx, smtx.ptr());
665 copy_m3_m3(td.mtx, mtx.ptr());
666 }
667 });
668 }
669 if (points_to_transform_per_attr.size() > 1 && points_to_transform_per_attr.first().is_empty()) {
670 auto update_handle_center = [&](const int handle_selection_attr,
671 const int opposite_handle_selection_attr) {
672 const IndexMask &handles_to_transform = points_to_transform_per_attr[handle_selection_attr];
673 const IndexMask &opposite_handles_to_transform =
674 points_to_transform_per_attr[opposite_handle_selection_attr];
675
676 if (handles_to_transform.size() == 1 && opposite_handles_to_transform.size() <= 1) {
677 MutableSpan<TransData> tc_data = all_tc_data.slice(
678 position_offsets_in_td[handle_selection_attr]);
679 copy_v3_v3(tc_data[0].center, point_positions[handles_to_transform.first()]);
680 }
681 };
682 update_handle_center(1, 2);
683 update_handle_center(2, 1);
684 }
685
686 if (use_connected_only) {
687 Array<int> curves_offsets_in_td_buffer(curves.curves_num() + 1, 0);
688 affected_curves.foreach_index(GrainSize(512), [&](const int64_t curve) {
689 curves_offsets_in_td_buffer[curve] =
690 points_to_transform_per_attr[0].slice_content(points_by_curve[curve]).size();
691 });
692 offset_indices::accumulate_counts_to_offsets(curves_offsets_in_td_buffer);
693 const OffsetIndices<int> curves_offsets_in_td(curves_offsets_in_td_buffer);
694
695 Array<int> bezier_offsets_in_td(curves.curves_num() + 1, 0);
696 offset_indices::copy_group_sizes(points_by_curve, bezier_curves, bezier_offsets_in_td);
698
699 affected_curves.foreach_segment(GrainSize(512), [&](const IndexMaskSegment segment) {
700 Array<int> map;
701 Array<float> closest_distances;
702 Array<float3> mapped_curve_positions;
703
704 for (const int curve_i : segment) {
705 const int selection_attrs_num = curve_types[curve_i] == CURVE_TYPE_BEZIER ? 3 : 1;
706 const IndexRange curve_points = points_by_curve[curve_i];
707 const IndexRange editable_curve_points = curves_offsets_in_td[curve_i];
708 const int total_curve_points = selection_attrs_num * editable_curve_points.size();
709 map.reinitialize(total_curve_points);
710 closest_distances.reinitialize(total_curve_points);
711 closest_distances.fill(std::numeric_limits<float>::max());
712 mapped_curve_positions.reinitialize(total_curve_points);
713
714 fill_map(CurveType(curve_types[curve_i]),
715 editable_curve_points,
716 position_offsets_in_td,
717 bezier_offsets_in_td[curve_i],
718 map);
719
720 bool has_any_selected = false;
721 for (const int selection_attr_i : IndexRange(selection_attrs_num)) {
722 has_any_selected = has_any_selected ||
723 ed::curves::has_anything_selected(selection_attrs[selection_attr_i],
724 curve_points);
725 }
726 if (!has_any_selected) {
727 for (const int i : map) {
728 TransData &td = all_tc_data[i];
729 td.flag |= TD_SKIP;
730 }
731 continue;
732 }
733
734 for (const int i : closest_distances.index_range()) {
735 TransData &td = all_tc_data[map[i]];
736 mapped_curve_positions[i] = td.loc;
737 if (td.flag & TD_SELECTED) {
738 closest_distances[i] = 0.0f;
739 }
740 }
741
742 if (cyclic[curve_i]) {
743 cyclic_curve_connected_point_distances(mapped_curve_positions.as_span(),
744 closest_distances.as_mutable_span());
745 }
746 else {
747 curve_connected_point_distances(mapped_curve_positions.as_span(),
748 closest_distances.as_mutable_span());
749 }
750
751 for (const int i : closest_distances.index_range()) {
752 TransData &td = all_tc_data[map[i]];
753 td.dist = closest_distances[i];
754 }
755 }
756 });
757 }
758}
759
761
763 /*flags*/ (T_EDIT | T_POINTS),
764 /*create_trans_data*/ createTransCurvesVerts,
765 /*recalc_data*/ recalcData_curves,
766 /*special_aftertrans_update*/ nullptr,
767};
768
769} // 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
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
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:256
Span< T > as_span() const
Definition BLI_array.hh:243
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
IndexRange index_range() const
Definition BLI_array.hh:360
void fill(const T &value) const
Definition BLI_array.hh:272
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
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 const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
static VArray from_single(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
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType 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
bool contains(int64_t query_index) 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)
static bool update_auto_handle_types(bke::CurvesGeometry &curves, const IndexMask &auto_handles, const IndexMask &auto_handles_opposite, const IndexMask &selected_handles, const IndexMask &selected_handles_opposite, const StringRef handle_type_name, IndexMaskMemory &memory)
void create_aligned_handles_masks(const bke::CurvesGeometry &curves, Span< IndexMask > points_to_transform_per_attr, int curve_index, TransCustomData &custom_data)
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)
CurvesTransformData * create_curves_transform_custom_data(TransCustomData &custom_data)
void calculate_aligned_handles(const TransCustomData &custom_data, bke::CurvesGeometry &curves, int curve_index)
bool update_handle_types_for_transform(eTfmMode mode, const std::array< IndexMask, 3 > &selection_per_attribute, const IndexMask &bezier_points, bke::CurvesGeometry &curves)
static bool update_vector_handle_types(bke::CurvesGeometry &curves, const IndexMask &selected_handles, const StringRef handle_type_name)
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)
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, Span< IndexMask > points_to_transform_per_attr, const IndexMask &affected_curves, bool use_connected_only, const IndexMask &bezier_curves, void *extra=nullptr)
static void curve_connected_point_distances(const Span< float3 > positions, MutableSpan< float > r_distances)
static IndexMask handles_by_type(const IndexMask &handles, const VArray< int8_t > &types, const HandleType type, IndexMaskMemory &memory)
void copy_positions_from_curves_transform_custom_data(const TransCustomData &custom_data, int layer, MutableSpan< float3 > positions_dst)
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:633
TransDataContainer * data_container
Definition transform.hh:802
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