diff --git a/source/blender/editors/transform/transform_convert.hh b/source/blender/editors/transform/transform_convert.hh index d1346f7124e..285a1a3550f 100644 --- a/source/blender/editors/transform/transform_convert.hh +++ b/source/blender/editors/transform/transform_convert.hh @@ -90,7 +90,8 @@ struct CurvesTransformData { /** * The offsets of every grease pencil layer into `positions` array. - * For curves only one layer is used. + * For curves layers are used to store: positions, handle_positions_left and + * handle_positions_right. */ blender::Vector layer_offsets; @@ -171,15 +172,15 @@ void animrecord_check_state(TransInfo *t, ID *id); /** * Used for both curves and grease pencil objects. */ -void curve_populate_trans_data_structs(TransDataContainer &tc, - blender::bke::CurvesGeometry &curves, - const blender::float4x4 &matrix, - std::optional> value_attribute, - const blender::IndexMask &selected_indices, - bool use_proportional_edit, - const blender::IndexMask &affected_curves, - bool use_connected_only, - int trans_data_offset); +void curve_populate_trans_data_structs( + TransDataContainer &tc, + blender::bke::CurvesGeometry &curves, + const blender::float4x4 &transform, + std::optional> value_attribute, + const blender::Span 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); diff --git a/source/blender/editors/transform/transform_convert_curves.cc b/source/blender/editors/transform/transform_convert_curves.cc index d8b7237fdfe..b13a0c03dd6 100644 --- a/source/blender/editors/transform/transform_convert_curves.cc +++ b/source/blender/editors/transform/transform_convert_curves.cc @@ -16,6 +16,7 @@ #include "BKE_attribute.hh" #include "BKE_curves.hh" +#include "BKE_curves_utils.hh" #include "ED_curves.hh" @@ -63,9 +64,9 @@ static void calculate_curve_point_distances_for_proportional_editing( } } -static void append_positions_to_custom_data(const IndexMask selection, - Span positions, - TransCustomData &custom_data) +static MutableSpan append_positions_to_custom_data(const IndexMask selection, + Span positions, + TransCustomData &custom_data) { CurvesTransformData &transform_data = *static_cast(custom_data.data); transform_data.selection_by_layer.append(selection); @@ -75,32 +76,101 @@ static void append_positions_to_custom_data(const IndexMask selection, positions, selection, transform_data.positions.as_mutable_span().slice(data_offset, selection.size())); + return transform_data.positions.as_mutable_span().slice(transform_data.layer_offsets.last(1), + selection.size()); } static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t) { MutableSpan trans_data_contrainers(t->data_container, t->data_container_len); - Array selection_per_object(t->data_container_len); + Array> points_to_transform_per_attribute(t->data_container_len); + Array bezier_curves(t->data_container_len); const bool use_proportional_edit = (t->flag & T_PROP_EDIT_ALL) != 0; const bool use_connected_only = (t->flag & T_PROP_CONNECTED) != 0; + Vector must_be_selected; + /* Count selected elements per object and create TransData structs. */ for (const int i : trans_data_contrainers.index_range()) { TransDataContainer &tc = trans_data_contrainers[i]; Curves *curves_id = static_cast(tc.obedit->data); bke::CurvesGeometry &curves = curves_id->geometry.wrap(); - CurvesTransformData *curves_transform_data = create_curves_transform_custom_data( tc.custom.type); + Span selection_attribute_names = ed::curves::get_curves_selection_attribute_names( + curves); + std::array selection_per_attribute; + + for (const int attribute_i : selection_attribute_names.index_range()) { + const StringRef &selection_name = selection_attribute_names[attribute_i]; + selection_per_attribute[attribute_i] = ed::curves::retrieve_selected_points( + curves, selection_name, curves_transform_data->memory); + } + + bezier_curves[i] = bke::curves::indices_for_type(curves.curve_types(), + curves.curve_type_counts(), + CURVE_TYPE_BEZIER, + curves.curves_range(), + curves_transform_data->memory); + /* Alter selection as in legacy curves bezt_select_to_transform_triple_flag(). */ + if (!bezier_curves[i].is_empty()) { + const OffsetIndices points_by_curve = curves.points_by_curve(); + const VArray handle_types_left = curves.handle_types_left(); + const VArray handle_types_right = curves.handle_types_right(); + + must_be_selected.clear(); + bezier_curves[i].foreach_index([&](const int bezier_index) { + for (const int point_i : points_by_curve[bezier_index]) { + if (selection_per_attribute[0].contains(point_i)) { + const HandleType type_left = HandleType(handle_types_left[point_i]); + const HandleType type_right = HandleType(handle_types_right[point_i]); + if (ELEM(type_left, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_ALIGN) && + ELEM(type_right, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_ALIGN)) + { + must_be_selected.append(point_i); + } + } + } + }); + + /* Select bezier handles that must be transformed if the main control point is selected. */ + IndexMask must_be_selected_mask = IndexMask::from_indices(must_be_selected.as_span(), + curves_transform_data->memory); + if (must_be_selected.size()) { + selection_per_attribute[1] = IndexMask::from_union( + selection_per_attribute[1], must_be_selected_mask, curves_transform_data->memory); + selection_per_attribute[2] = IndexMask::from_union( + selection_per_attribute[2], must_be_selected_mask, curves_transform_data->memory); + } + } if (use_proportional_edit) { - selection_per_object[i] = curves.points_range(); - tc.data_len = curves.point_num; + Array bezier_point_offset_data(bezier_curves[i].size() + 1); + OffsetIndices bezier_offsets = offset_indices::gather_selected_offsets( + curves.points_by_curve(), bezier_curves[i], bezier_point_offset_data); + + const int bezier_point_count = bezier_offsets.total_size(); + tc.data_len = curves.points_num() + 2 * bezier_point_count; + points_to_transform_per_attribute[i].append(curves.points_range()); + + if (bezier_point_count > 0) { + Vector bezier_point_ranges; + OffsetIndices points_by_curve = curves.points_by_curve(); + bezier_curves[i].foreach_index(GrainSize(512), [&](const int bezier_curve_i) { + bezier_point_ranges.append(points_by_curve[bezier_curve_i]); + }); + IndexMask bezier_points = IndexMask::from_initializers(bezier_point_ranges, + curves_transform_data->memory); + points_to_transform_per_attribute[i].append(bezier_points); + points_to_transform_per_attribute[i].append(bezier_points); + } } else { - selection_per_object[i] = ed::curves::retrieve_selected_points( - curves, curves_transform_data->memory); - tc.data_len = selection_per_object[i].size(); + tc.data_len = 0; + for (const int selection_i : selection_attribute_names.index_range()) { + points_to_transform_per_attribute[i].append(selection_per_attribute[selection_i]); + tc.data_len += points_to_transform_per_attribute[i][selection_i].size(); + } } if (tc.data_len > 0) { @@ -140,11 +210,10 @@ static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t) curves, object->object_to_world(), value_attribute, - selection_per_object[i], - use_proportional_edit, + points_to_transform_per_attribute[i], curves.curves_range(), use_connected_only, - 0 /* No data offset for curves. */); + bezier_curves[i]); /* TODO: This is wrong. The attribute writer should live at least as long as the span. */ attribute_writer.finish(); @@ -164,8 +233,16 @@ static void recalcData_curves(TransInfo *t) curves.tag_normals_changed(); } else { - copy_positions_from_curves_transform_custom_data( - tc.custom.type, 0, curves.positions_for_write()); + const std::array, 3> positions_per_selection_attr = { + curves.positions_for_write(), + curves.handle_positions_left_for_write(), + curves.handle_positions_right_for_write()}; + for (const int selection_i : + ed::curves::get_curves_selection_attribute_names(curves).index_range()) + { + copy_positions_from_curves_transform_custom_data( + tc.custom.type, selection_i, positions_per_selection_attr[selection_i]); + } curves.tag_positions_changed(); curves.calculate_bezier_auto_handles(); } @@ -173,6 +250,45 @@ static void recalcData_curves(TransInfo *t) } } +static OffsetIndices recent_position_offsets(TransCustomData &custom_data, int num) +{ + const CurvesTransformData &transform_data = *static_cast( + custom_data.data); + return OffsetIndices(transform_data.layer_offsets.as_span().slice( + transform_data.layer_offsets.size() - num - 1, num + 1)); +} + +/** + * Creates map of indices to `tc.data` representing curve in layout + * [L0, P0, R0, L1, P1, R1, L2,P2, R2], where [P0, P1, P2], [L0, L1, L2] and [R0, R1, R2] are + * positions, left handles and right handles respectively. + */ +static void fill_map(const CurveType curve_type, + const IndexRange curve_points, + const OffsetIndices position_offsets_in_td, + const int handles_offset, + MutableSpan map) +{ + const int attr_num = (curve_type == CURVE_TYPE_BEZIER) ? 3 : 1; + const int left_handle_index = handles_offset + position_offsets_in_td[1].start(); + const int position_index = curve_points.start() + position_offsets_in_td[0].start(); + const int right_handle_index = handles_offset + position_offsets_in_td[2].start(); + + std::array first_per_attr = {curve_type == CURVE_TYPE_BEZIER ? left_handle_index : + position_index, + /* Next two unused for non Bezier curves. */ + position_index, + right_handle_index}; + + threading::parallel_for(curve_points.index_range(), 4096, [&](const IndexRange range) { + for (const int i : range) { + for (const int attr : IndexRange(attr_num)) { + map[i * attr_num + attr] = first_per_attr[attr] + i; + } + } + }); +} + } // namespace blender::ed::transform::curves CurvesTransformData *create_curves_transform_custom_data(TransCustomData &custom_data) @@ -203,108 +319,136 @@ void copy_positions_from_curves_transform_custom_data( array_utils::scatter(positions, selection, positions_dst); } -void curve_populate_trans_data_structs(TransDataContainer &tc, - blender::bke::CurvesGeometry &curves, - const blender::float4x4 &transform, - std::optional> value_attribute, - const blender::IndexMask &selected_indices, - const bool use_proportional_edit, - const blender::IndexMask &affected_curves, - bool use_connected_only, - int trans_data_offset) +void curve_populate_trans_data_structs( + TransDataContainer &tc, + blender::bke::CurvesGeometry &curves, + const blender::float4x4 &transform, + std::optional> value_attribute, + const blender::Span points_to_transform_per_attr, + const blender::IndexMask &affected_curves, + bool use_connected_only, + const blender::IndexMask &bezier_curves) { using namespace blender; + const std::array, 3> src_positions_per_selection_attr = { + curves.positions(), curves.handle_positions_left(), curves.handle_positions_right()}; + std::array, 3> positions_per_selection_attr; + + for (const int selection_i : points_to_transform_per_attr.index_range()) { + positions_per_selection_attr[selection_i] = + ed::transform::curves::append_positions_to_custom_data( + points_to_transform_per_attr[selection_i], + src_positions_per_selection_attr[selection_i], + tc.custom.type); + } float mtx[3][3], smtx[3][3]; copy_m3_m4(mtx, transform.ptr()); pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); - ed::transform::curves::append_positions_to_custom_data( - selected_indices, curves.positions(), tc.custom.type); - MutableSpan positions = static_cast(tc.custom.type.data) - ->positions.as_mutable_span() - .slice(trans_data_offset, selected_indices.size()); + MutableSpan all_tc_data = MutableSpan(tc.data, tc.data_len); + OffsetIndices position_offsets_in_td = ed::transform::curves::recent_position_offsets( + tc.custom.type, points_to_transform_per_attr.size()); - if (use_proportional_edit) { + Vector> selection_attrs; + Span selection_attribute_names = ed::curves::get_curves_selection_attribute_names( + curves); + for (const StringRef selection_name : selection_attribute_names) { + const VArray selection_attr = *curves.attributes().lookup_or_default( + selection_name, bke::AttrDomain::Point, true); + selection_attrs.append(selection_attr); + } + + for (const int selection_i : position_offsets_in_td.index_range()) { + if (position_offsets_in_td[selection_i].is_empty()) { + continue; + } + MutableSpan tc_data = all_tc_data.slice(position_offsets_in_td[selection_i]); + MutableSpan positions = positions_per_selection_attr[selection_i]; + IndexMask points_to_transform = points_to_transform_per_attr[selection_i]; + VArray selection = selection_attrs[selection_i]; + + threading::parallel_for(points_to_transform.index_range(), 1024, [&](const IndexRange range) { + for (const int tranform_point_i : range) { + const int point_in_domain_i = points_to_transform[tranform_point_i]; + TransData &td = tc_data[tranform_point_i]; + float3 *elem = &positions[tranform_point_i]; + + copy_v3_v3(td.iloc, *elem); + copy_v3_v3(td.center, td.iloc); + td.loc = *elem; + + td.flag = 0; + if (selection[point_in_domain_i]) { + td.flag = TD_SELECTED; + } + + if (value_attribute) { + float *value = &((*value_attribute)[point_in_domain_i]); + td.val = value; + td.ival = *value; + } + td.ext = nullptr; + + copy_m3_m3(td.smtx, smtx); + copy_m3_m3(td.mtx, mtx); + } + }); + } + if (use_connected_only) { + const VArray curve_types = curves.curve_types(); const OffsetIndices points_by_curve = curves.points_by_curve(); - const VArray selection = *curves.attributes().lookup_or_default( - ".selection", bke::AttrDomain::Point, true); + Array bezier_offsets_in_td(curves.curves_num() + 1, 0); + offset_indices::copy_group_sizes(points_by_curve, bezier_curves, bezier_offsets_in_td); + offset_indices::accumulate_counts_to_offsets(bezier_offsets_in_td); + affected_curves.foreach_segment(GrainSize(512), [&](const IndexMaskSegment segment) { - Vector closest_distances; + Array map; + Array closest_distances; + Array mapped_curve_positions; + for (const int curve_i : segment) { - const IndexRange points = points_by_curve[curve_i]; - const bool has_any_selected = ed::curves::has_anything_selected(selection, points); - if (!has_any_selected && use_connected_only) { - for (const int point_i : points) { - TransData &td = tc.data[point_i + trans_data_offset]; + const int selection_attrs_num = curve_types[curve_i] == CURVE_TYPE_BEZIER ? 3 : 1; + const IndexRange curve_points = points_by_curve[curve_i]; + const int total_curve_points = selection_attrs_num * curve_points.size(); + map.reinitialize(total_curve_points); + closest_distances.reinitialize(total_curve_points); + closest_distances.fill(std::numeric_limits::max()); + mapped_curve_positions.reinitialize(total_curve_points); + + ed::transform::curves::fill_map(CurveType(curve_types[curve_i]), + curve_points, + position_offsets_in_td, + bezier_offsets_in_td[curve_i], + map); + + bool has_any_selected = false; + for (const int selection_attr_i : IndexRange(selection_attrs_num)) { + has_any_selected = has_any_selected || + ed::curves::has_anything_selected(selection_attrs[selection_attr_i], + curve_points); + } + if (!has_any_selected) { + for (const int i : map) { + TransData &td = all_tc_data[i]; td.flag |= TD_SKIP; } continue; } - closest_distances.reinitialize(points.size()); - closest_distances.fill(std::numeric_limits::max()); - - for (const int i : IndexRange(points.size())) { - const int point_i = points[i]; - TransData &td = tc.data[point_i + trans_data_offset]; - float3 *elem = &positions[point_i]; - - copy_v3_v3(td.iloc, *elem); - copy_v3_v3(td.center, td.iloc); - td.loc = *elem; - - td.flag = 0; - if (selection[point_i]) { + for (const int i : closest_distances.index_range()) { + TransData &td = all_tc_data[map[i]]; + mapped_curve_positions[i] = td.loc; + if (td.flag & TD_SELECTED) { closest_distances[i] = 0.0f; - td.flag = TD_SELECTED; - } - - if (value_attribute) { - float *value = &((*value_attribute)[point_i]); - td.val = value; - td.ival = *value; - } - - td.ext = nullptr; - - copy_m3_m3(td.smtx, smtx); - copy_m3_m3(td.mtx, mtx); - } - - if (use_connected_only) { - blender::ed::transform::curves::calculate_curve_point_distances_for_proportional_editing( - positions.slice(points), closest_distances.as_mutable_span()); - for (const int i : IndexRange(points.size())) { - TransData &td = tc.data[points[i] + trans_data_offset]; - td.dist = closest_distances[i]; } } - } - }); - } - else { - threading::parallel_for(selected_indices.index_range(), 1024, [&](const IndexRange range) { - for (const int selection_i : range) { - TransData *td = &tc.data[selection_i + trans_data_offset]; - const int point_i = selected_indices[selection_i]; - float3 *elem = &positions[selection_i]; - - copy_v3_v3(td->iloc, *elem); - copy_v3_v3(td->center, td->iloc); - td->loc = *elem; - - if (value_attribute) { - float *value = &((*value_attribute)[point_i]); - td->val = value; - td->ival = *value; + blender::ed::transform::curves::calculate_curve_point_distances_for_proportional_editing( + mapped_curve_positions.as_span(), closest_distances.as_mutable_span()); + for (const int i : closest_distances.index_range()) { + TransData &td = all_tc_data[map[i]]; + td.dist = closest_distances[i]; } - - td->flag = TD_SELECTED; - td->ext = nullptr; - - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); } }); } diff --git a/source/blender/editors/transform/transform_convert_grease_pencil.cc b/source/blender/editors/transform/transform_convert_grease_pencil.cc index 551a35ff1f9..3a959491da5 100644 --- a/source/blender/editors/transform/transform_convert_grease_pencil.cc +++ b/source/blender/editors/transform/transform_convert_grease_pencil.cc @@ -89,7 +89,6 @@ static void createTransGreasePencilVerts(bContext *C, TransInfo *t) GreasePencil &grease_pencil = *static_cast(tc.obedit->data); Span layers = grease_pencil.layers(); - int layer_points_offset = 0; const Vector drawings = all_drawings[i]; for (ed::greasepencil::MutableDrawingInfo info : drawings) { const bke::greasepencil::Layer &layer = *layers[info.layer_index]; @@ -115,13 +114,10 @@ static void createTransGreasePencilVerts(bContext *C, TransInfo *t) curves, layer_space_to_world_space, value_attribute, - points, - use_proportional_edit, + {points}, affected_strokes, use_connected_only, - layer_points_offset); - - layer_points_offset += points.size(); + IndexMask()); layer_offset++; } }