From 82e557eb218fa3767c7982f43fd51bc8810ded39 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Sat, 17 May 2025 10:38:01 +0200 Subject: [PATCH] Fix #138636: "Affect only origins" fails for bezier handles ... for Grease Pencil & Curves Was only moving the points relative to the origin (so only they stayed in place, handles were still transformed along with the origin). In order to fix this this, we now take into account curves handles in the whole `XFormObjectData` related code (for both Grease Pencil & Curves). There was a handly existing pair of functions [`curves::bezier::retrieve_all_positions` & `curves::bezier::write_all_positions`] which we can use for Curves, for Grease Pencil this uses code from those functions (but not the functions directly -- indices would fail there because Grease Pencil would call this from multiple layers). This also introduces `BKE_grease_pencil_has_curve_with_type` so we can know in advance how many elements we need for the `XFormObjectData_GreasePencil`. Also corrects a typo in c1c67c918ea7 (swapping `XFormObjectData_Curves` with `XFormObjectData_GreasePencil`) Pull Request: https://projects.blender.org/blender/blender/pulls/138665 --- .../blender/blenkernel/BKE_grease_pencil.hh | 4 + .../blenkernel/intern/grease_pencil.cc | 92 ++++++++++++++++--- .../editors/object/object_data_transform.cc | 37 +++++++- 3 files changed, 116 insertions(+), 17 deletions(-) diff --git a/source/blender/blenkernel/BKE_grease_pencil.hh b/source/blender/blenkernel/BKE_grease_pencil.hh index cc2acab50fc..091f667f769 100644 --- a/source/blender/blenkernel/BKE_grease_pencil.hh +++ b/source/blender/blenkernel/BKE_grease_pencil.hh @@ -1111,6 +1111,10 @@ void BKE_grease_pencil_duplicate_drawing_array(const GreasePencil *grease_pencil * \note Used for "move only origins" in object_data_transform.cc. */ int BKE_grease_pencil_stroke_point_count(const GreasePencil &grease_pencil); +/** + * \note Used for "move only origins" in object_data_transform.cc. + */ +bool BKE_grease_pencil_has_curve_with_type(const GreasePencil &grease_pencil, CurveType type); /** * \note Used for "move only origins" in object_data_transform.cc. */ diff --git a/source/blender/blenkernel/intern/grease_pencil.cc b/source/blender/blenkernel/intern/grease_pencil.cc index 2851ca0a201..56206881c03 100644 --- a/source/blender/blenkernel/intern/grease_pencil.cc +++ b/source/blender/blenkernel/intern/grease_pencil.cc @@ -2393,6 +2393,25 @@ void BKE_grease_pencil_duplicate_drawing_array(const GreasePencil *grease_pencil * \note Used for "move only origins" in object_data_transform.cc. * \{ */ +bool BKE_grease_pencil_has_curve_with_type(const GreasePencil &grease_pencil, const CurveType type) +{ + using namespace blender; + + for (const GreasePencilDrawingBase *base : grease_pencil.drawings()) { + if (base->type != GP_DRAWING) { + continue; + } + const bke::greasepencil::Drawing &drawing = + reinterpret_cast(base)->wrap(); + const bke::CurvesGeometry &curves = drawing.strokes(); + if (curves.has_curve_with_type(type)) { + return true; + } + } + + return false; +} + int BKE_grease_pencil_stroke_point_count(const GreasePencil &grease_pencil) { using namespace blender; @@ -2440,10 +2459,26 @@ void BKE_grease_pencil_point_coords_get(const GreasePencil &grease_pencil, const Span positions = curves.positions(); const VArray radii = drawing.radii(); - for (const int i : curves.points_range()) { - all_positions[index] = math::transform_point(layer_to_object, positions[i]); - all_radii[index] = radii[i]; - index++; + if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + for (const int i : curves.points_range()) { + all_positions[index] = math::transform_point(layer_to_object, positions[i]); + all_radii[index] = radii[i]; + index++; + } + } + else { + const Span handle_positions_left = curves.handle_positions_left(); + const Span handle_positions_right = curves.handle_positions_right(); + for (const int i : curves.points_range()) { + const int index_pos = index * 3; + all_positions[index_pos] = math::transform_point(layer_to_object, + handle_positions_left[i]); + all_positions[index_pos + 1] = math::transform_point(layer_to_object, positions[i]); + all_positions[index_pos + 2] = math::transform_point(layer_to_object, + handle_positions_right[i]); + all_radii[index] = radii[i]; + index++; + } } }); } @@ -2471,10 +2506,26 @@ void BKE_grease_pencil_point_coords_apply(GreasePencil &grease_pencil, MutableSpan positions = curves.positions_for_write(); MutableSpan radii = drawing.radii_for_write(); - for (const int i : curves.points_range()) { - positions[i] = math::transform_point(object_to_layer, all_positions[index]); - radii[i] = all_radii[index]; - index++; + if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + for (const int i : curves.points_range()) { + positions[i] = math::transform_point(object_to_layer, all_positions[index]); + radii[i] = all_radii[index]; + index++; + } + } + else { + MutableSpan handle_positions_left = curves.handle_positions_left_for_write(); + MutableSpan handle_positions_right = curves.handle_positions_right_for_write(); + for (const int i : curves.points_range()) { + const int index_pos = index * 3; + handle_positions_left[i] = math::transform_point(object_to_layer, + all_positions[index_pos]); + positions[i] = math::transform_point(object_to_layer, all_positions[index_pos + 1]); + handle_positions_right[i] = math::transform_point(object_to_layer, + all_positions[index_pos + 2]); + radii[i] = all_radii[index]; + index++; + } } curves.tag_radii_changed(); @@ -2507,10 +2558,27 @@ void BKE_grease_pencil_point_coords_apply_with_mat4(GreasePencil &grease_pencil, MutableSpan positions = curves.positions_for_write(); MutableSpan radii = drawing.radii_for_write(); - for (const int i : curves.points_range()) { - positions[i] = math::transform_point(object_to_layer * mat, all_positions[index]); - radii[i] = all_radii[index] * scalef; - index++; + if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + for (const int i : curves.points_range()) { + positions[i] = math::transform_point(object_to_layer * mat, all_positions[index]); + radii[i] = all_radii[index] * scalef; + index++; + } + } + else { + MutableSpan handle_positions_left = curves.handle_positions_left_for_write(); + MutableSpan handle_positions_right = curves.handle_positions_right_for_write(); + for (const int i : curves.points_range()) { + const int index_pos = index * 3; + handle_positions_left[i] = math::transform_point(object_to_layer * mat, + all_positions[index_pos]); + positions[i] = math::transform_point(object_to_layer * mat, + all_positions[index_pos + 1]); + handle_positions_right[i] = math::transform_point(object_to_layer * mat, + all_positions[index_pos + 2]); + radii[i] = all_radii[index] * scalef; + index++; + } } curves.tag_radii_changed(); diff --git a/source/blender/editors/object/object_data_transform.cc b/source/blender/editors/object/object_data_transform.cc index ade3cb05339..0dee7366c33 100644 --- a/source/blender/editors/object/object_data_transform.cc +++ b/source/blender/editors/object/object_data_transform.cc @@ -31,6 +31,7 @@ #include "BKE_armature.hh" #include "BKE_curve.hh" +#include "BKE_curves_utils.hh" #include "BKE_editmesh.hh" #include "BKE_grease_pencil.hh" #include "BKE_key.hh" @@ -455,7 +456,12 @@ static std::unique_ptr data_xform_create_ex(ID *id, bool is_edi const int elem_array_len = BKE_grease_pencil_stroke_point_count(*grease_pencil); auto xod = std::make_unique(); xod->id = id; - xod->positions.reinitialize(elem_array_len); + if (!BKE_grease_pencil_has_curve_with_type(*grease_pencil, CURVE_TYPE_BEZIER)) { + xod->positions.reinitialize(elem_array_len); + } + else { + xod->positions.reinitialize(elem_array_len * 3); + } xod->radii.reinitialize(elem_array_len); BKE_grease_pencil_point_coords_get(*grease_pencil, xod->positions, xod->radii); return xod; @@ -463,9 +469,17 @@ static std::unique_ptr data_xform_create_ex(ID *id, bool is_edi case ID_CV: { Curves *curves_id = reinterpret_cast(id); const bke::CurvesGeometry &curves = curves_id->geometry.wrap(); - auto xod = std::make_unique(); + auto xod = std::make_unique(); xod->id = id; - xod->positions = curves.positions(); + + if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + xod->positions = curves.positions(); + } + else { + xod->positions = bke::curves::bezier::retrieve_all_positions(curves, + curves.curves_range()); + } + xod->radii.reinitialize(curves.points_num()); curves.radius().materialize(xod->radii); return xod; @@ -626,7 +640,15 @@ void data_xform_by_mat4(XFormObjectData &xod_base, const float4x4 &transform) Curves *curves_id = reinterpret_cast(xod_base.id); bke::CurvesGeometry &curves = curves_id->geometry.wrap(); const auto &xod = reinterpret_cast(xod_base); - copy_transformed_positions(xod.positions, transform, curves.positions_for_write()); + if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + copy_transformed_positions(xod.positions, transform, curves.positions_for_write()); + } + else { + Array transformed_positions(xod.positions.size()); + copy_transformed_positions(xod.positions, transform, transformed_positions); + bke::curves::bezier::write_all_positions( + curves, curves.curves_range(), transformed_positions); + } copy_transformed_radii(xod.radii, transform, curves.radius_for_write()); break; } @@ -741,7 +763,12 @@ void data_xform_restore(XFormObjectData &xod_base) Curves *curves_id = reinterpret_cast(xod_base.id); bke::CurvesGeometry &curves = curves_id->geometry.wrap(); const auto &xod = reinterpret_cast(xod_base); - curves.positions_for_write().copy_from(xod.positions); + if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + curves.positions_for_write().copy_from(xod.positions); + } + else { + bke::curves::bezier::write_all_positions(curves, curves.curves_range(), xod.positions); + } curves.radius_for_write().copy_from(xod.radii); break; }