diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index e64a7c9731b..c1730f33798 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -137,7 +137,6 @@ class _defs_view3d_generic: idname="builtin.measure", label="Measure", description=description, - cursor='CROSSHAIR', icon="ops.view3d.ruler", widget="VIEW3D_GGT_ruler", keymap="3D View Tool: Measure", @@ -407,7 +406,6 @@ class _defs_view3d_select: label="Select Lasso", icon="ops.generic.select_lasso", widget=None, - cursor='DEFAULT', keymap="3D View Tool: Select Lasso", draw_settings=draw_settings, ) @@ -432,7 +430,6 @@ class _defs_view3d_select: label="Select Circle", icon="ops.generic.select_circle", widget=None, - cursor='DEFAULT', keymap="3D View Tool: Select Circle", draw_settings=draw_settings, draw_cursor=draw_cursor, diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 679fe703b13..56bc3db2762 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -951,6 +951,10 @@ static void childof_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar /* If requested, compute the inverse matrix from the computed parent matrix. */ if (data->flag & CHILDOF_SET_INVERSE) { invert_m4_m4(data->invmat, parmat); + if (cob->pchan != NULL) { + mul_m4_series(data->invmat, data->invmat, cob->ob->obmat); + } + copy_m4_m4(inverse_matrix, data->invmat); data->flag &= ~CHILDOF_SET_INVERSE; diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a5c99016434..51607c5daa3 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1082,9 +1082,19 @@ static void view3d_main_region_message_subscribe(const struct bContext *C, } } +/* concept is to retrieve cursor type context-less */ static void view3d_main_region_cursor(wmWindow *win, ScrArea *area, ARegion *region) { - if (!WM_cursor_set_from_tool(win, area, region)) { + if (WM_cursor_set_from_tool(win, area, region)) { + return; + } + + ViewLayer *view_layer = WM_window_get_active_view_layer(win); + Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); + if (obedit) { + WM_cursor_set(win, WM_CURSOR_EDIT); + } + else { WM_cursor_set(win, WM_CURSOR_DEFAULT); } } diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c index 3301e28c90c..3ce4c8dc9a8 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c @@ -536,9 +536,12 @@ static int gizmo_axis_test_select(bContext *UNUSED(C), wmGizmo *gz, const int mv return -1; } -static int gizmo_axis_cursor_get(wmGizmo *UNUSED(gz)) +static int gizmo_axis_cursor_get(wmGizmo *gz) { - return WM_CURSOR_DEFAULT; + if (gz->highlight_part > 0) { + return WM_CURSOR_EDIT; + } + return WM_CURSOR_NSEW_SCROLL; } void VIEW3D_GT_navigate_rotate(wmGizmoType *gzt) diff --git a/tests/python/bl_constraints.py b/tests/python/bl_constraints.py index 9fce8acc84e..323dd874ac0 100644 --- a/tests/python/bl_constraints.py +++ b/tests/python/bl_constraints.py @@ -56,21 +56,37 @@ class AbstractConstraintTests(unittest.TestCase): actual, expect, places=places, delta=delta, msg=f'Matrix of object {object_name!r} failed: {actual} != {expect} at element [{row}][{col}]') - def matrix(self, object_name: str) -> Matrix: - """Return the evaluated world matrix.""" + def _get_eval_object(self, object_name: str) -> bpy.types.Object: + """Return the evaluated object.""" depsgraph = bpy.context.view_layer.depsgraph depsgraph.update() ob_orig = bpy.context.scene.objects[object_name] ob_eval = ob_orig.evaluated_get(depsgraph) + return ob_eval + + def matrix(self, object_name: str) -> Matrix: + """Return the evaluated world matrix.""" + ob_eval = self._get_eval_object(object_name) return ob_eval.matrix_world + def bone_matrix(self, object_name: str, bone_name: str) -> Matrix: + """Return the evaluated world matrix of the bone.""" + ob_eval = self._get_eval_object(object_name) + bone = ob_eval.pose.bones[bone_name] + return ob_eval.matrix_world @ bone.matrix + def matrix_test(self, object_name: str, expect: Matrix): """Assert that the object's world matrix is as expected.""" actual = self.matrix(object_name) self.assert_matrix(actual, expect, object_name) + def bone_matrix_test(self, object_name: str, bone_name: str, expect: Matrix): + """Assert that the bone's world matrix is as expected.""" + actual = self.bone_matrix(object_name, bone_name) + self.assert_matrix(actual, expect, object_name) + def constraint_context(self, constraint_name: str, owner_name: str='') -> dict: - """Return a context suitable for calling constraint operators. + """Return a context suitable for calling object constraint operators. Assumes the owner is called "{constraint_name}.owner" if owner_name=''. """ @@ -84,6 +100,30 @@ class AbstractConstraintTests(unittest.TestCase): } return context + def bone_constraint_context(self, constraint_name: str, owner_name: str='', bone_name: str='') -> dict: + """Return a context suitable for calling bone constraint operators. + + Assumes the owner's object is called "{constraint_name}.owner" if owner_name=''. + Assumes the bone is called "{constraint_name}.bone" if bone_name=''. + """ + + owner_name = owner_name or f'{constraint_name}.owner' + bone_name = bone_name or f'{constraint_name}.bone' + + owner = bpy.context.scene.objects[owner_name] + pose_bone = owner.pose.bones[bone_name] + + constraint = pose_bone.constraints[constraint_name] + context = { + **bpy.context.copy(), + 'object': owner, + 'active_object': owner, + 'active_pose_bone': pose_bone, + 'constraint': constraint, + 'owner': pose_bone, + } + return context + class ChildOfTest(AbstractConstraintTests): layer_collection = 'Child Of' @@ -153,7 +193,7 @@ class ChildOfTest(AbstractConstraintTests): )) self.matrix_test('Child Of.object.owner', initial_matrix) - context = self.constraint_context('Child Of', owner_name='Child Of.object.owner') + context = self.constraint_context('Child Of', owner_name='Child Of.object.owner',) bpy.ops.constraint.childof_set_inverse(context, constraint='Child Of') self.matrix_test('Child Of.object.owner', Matrix(( (0.9992386102676392, 0.019843991845846176, -0.03359176218509674, 0.10000000149011612), @@ -188,6 +228,29 @@ class ChildOfTest(AbstractConstraintTests): bpy.ops.constraint.childof_clear_inverse(context, constraint='Child Of') self.matrix_test('Child Of.armature.owner', initial_matrix) + def test_bone_owner(self): + """Child Of: bone owns constraint, targeting object.""" + initial_matrix = Matrix(( + (0.9992387890815735, -0.03359174728393555, -0.019843988120555878, -2.999999523162842), + (-0.02588011883199215, -0.1900751143693924, -0.9814283847808838, 2.0), + (0.029196053743362427, 0.9811949133872986, -0.190799742937088, 0.9999999403953552), + (0.0, 0.0, 0.0, 1.0), + )) + self.bone_matrix_test('Child Of.bone.owner', 'Child Of.bone', initial_matrix) + + context = self.bone_constraint_context('Child Of', owner_name='Child Of.bone.owner') + bpy.ops.constraint.childof_set_inverse(context, constraint='Child Of', owner='BONE') + + self.bone_matrix_test('Child Of.bone.owner', 'Child Of.bone', Matrix(( + (0.9659260511398315, 0.2588191032409668, 4.656613428188905e-10, -2.999999761581421), + (-3.725290742551124e-09, 1.4901162970204496e-08, -1.0, 0.9999999403953552), + (-0.2588191032409668, 0.965925931930542, 0.0, 0.9999999403953552), + (0.0, 0.0, 0.0, 1.0), + ))) + + bpy.ops.constraint.childof_clear_inverse(context, constraint='Child Of', owner='BONE') + self.bone_matrix_test('Child Of.bone.owner', 'Child Of.bone', initial_matrix) + def test_vertexgroup_simple_parent(self): """Child Of: simple evaluation of vertex group parent.""" initial_matrix = Matrix((