From 9879132c7c3a21deca16b5857481dad6abb6eaee Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 26 Jun 2025 10:36:45 -0300 Subject: [PATCH] Fix #141017: Rotation snapping increment is wrong after changing mode After changing the transform mode, `initSnapping` was called, resetting the increment values. The solution is to create the `transform_snap_reset_from_mode` function with only the changes needed when changing modes. This commit also makes the properties set in the operator take priority over the scene properties. --- source/blender/editors/transform/transform.cc | 2 +- .../editors/transform/transform_snap.cc | 305 +++++++++--------- .../editors/transform/transform_snap.hh | 1 + 3 files changed, 160 insertions(+), 148 deletions(-) diff --git a/source/blender/editors/transform/transform.cc b/source/blender/editors/transform/transform.cc index 2c2da5331d1..e54f6d40e15 100644 --- a/source/blender/editors/transform/transform.cc +++ b/source/blender/editors/transform/transform.cc @@ -1102,7 +1102,7 @@ wmOperatorStatus transformEvent(TransInfo *t, wmOperator *op, const wmEvent *eve } /* Need to reinitialize after mode change. */ - initSnapping(t, nullptr); + transform_snap_reset_from_mode(t, op); applyMouseInput(t, &t->mouse, t->mval, t->values); t->redraw |= TREDRAW_HARD; break; diff --git a/source/blender/editors/transform/transform_snap.cc b/source/blender/editors/transform/transform_snap.cc index 65f515b841f..782cce43029 100644 --- a/source/blender/editors/transform/transform_snap.cc +++ b/source/blender/editors/transform/transform_snap.cc @@ -750,60 +750,72 @@ static eSnapMode snap_mode_from_spacetype(TransInfo *t) return SCE_SNAP_TO_INCREMENT; } -static eSnapTargetOP snap_target_select_from_spacetype(TransInfo *t) +static eSnapTargetOP snap_target_select_from_spacetype_and_tool_settings(TransInfo *t) { - BKE_view_layer_synced_ensure(t->scene, t->view_layer); - Base *base_act = BKE_view_layer_active_base_get(t->view_layer); - - eSnapTargetOP ret = SCE_SNAP_TARGET_ALL; - /* `t->tsnap.target_operation` not initialized yet. */ BLI_assert(t->tsnap.target_operation == SCE_SNAP_TARGET_ALL); + eSnapTargetOP r_target_operation = SCE_SNAP_TARGET_ALL; + if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) { + BKE_view_layer_synced_ensure(t->scene, t->view_layer); + Base *base_act = BKE_view_layer_active_base_get(t->view_layer); + const int obedit_type = t->obedit_type; if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) { /* Particles edit mode. */ - return ret; } - - if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) { + else if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) { /* In "Edit Strokes" mode, * snap tool can perform snap to selected or active objects (see #49632) * TODO: perform self snap in gpencil_strokes. * * When we're moving the origins, allow snapping onto our own geometry (see #69132). */ - return ret; } - - const int obedit_type = t->obedit_type; - if (obedit_type != -1) { + else if (obedit_type != -1) { /* Edit mode. */ if (obedit_type == OB_MESH) { /* Editing a mesh. */ if ((t->flag & T_PROP_EDIT) != 0) { /* Exclude editmesh when using proportional edit. */ - ret |= SCE_SNAP_TARGET_NOT_EDITED; + r_target_operation |= SCE_SNAP_TARGET_NOT_EDITED; } /* UV editing must never snap to the selection as this is what is transformed. */ if (t->spacetype == SPACE_IMAGE) { - ret |= SCE_SNAP_TARGET_NOT_SELECTED; + r_target_operation |= SCE_SNAP_TARGET_NOT_SELECTED; } } else if (ELEM(obedit_type, OB_ARMATURE, OB_CURVES_LEGACY, OB_SURF, OB_LATTICE, OB_MBALL)) { - /* Temporary limited to edit mode armature, curves, surfaces, lattices, and meta-balls. */ - ret |= SCE_SNAP_TARGET_NOT_SELECTED; + /* Temporary limited to edit mode armature, curves, surfaces, lattices, and meta-balls. + */ + r_target_operation |= SCE_SNAP_TARGET_NOT_SELECTED; } } else { /* Object or pose mode. */ - ret |= SCE_SNAP_TARGET_NOT_SELECTED | SCE_SNAP_TARGET_NOT_ACTIVE; + r_target_operation |= SCE_SNAP_TARGET_NOT_SELECTED | SCE_SNAP_TARGET_NOT_ACTIVE; } } else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) { - ret |= SCE_SNAP_TARGET_NOT_SELECTED; + r_target_operation |= SCE_SNAP_TARGET_NOT_SELECTED; } - return ret; + /* Use scene defaults only when transform is modal. */ + if (t->flag & T_MODAL) { + ToolSettings *ts = t->settings; + SET_FLAG_FROM_TEST( + r_target_operation, (ts->snap_flag & SCE_SNAP_NOT_TO_ACTIVE), SCE_SNAP_TARGET_NOT_ACTIVE); + SET_FLAG_FROM_TEST(r_target_operation, + !(ts->snap_flag & SCE_SNAP_TO_INCLUDE_EDITED), + SCE_SNAP_TARGET_NOT_EDITED); + SET_FLAG_FROM_TEST(r_target_operation, + !(ts->snap_flag & SCE_SNAP_TO_INCLUDE_NONEDITED), + SCE_SNAP_TARGET_NOT_NONEDITED); + SET_FLAG_FROM_TEST(r_target_operation, + (ts->snap_flag & SCE_SNAP_TO_ONLY_SELECTABLE), + SCE_SNAP_TARGET_ONLY_SELECTABLE); + } + + return r_target_operation; } static void snap_object_context_init(TransInfo *t) @@ -834,6 +846,12 @@ static void initSnappingMode(TransInfo *t) /* In this case, snapping is always disabled by default. */ t->modifiers &= ~MOD_SNAP; } + else if (t->flag & T_MODAL) { + /* Use scene defaults only when transform is modal. */ + if (t->tsnap.flag & SCE_SNAP) { + t->modifiers |= MOD_SNAP; + } + } if (doForceIncrementSnap(t)) { t->tsnap.mode = SCE_SNAP_TO_INCREMENT; @@ -847,21 +865,6 @@ static void initSnappingMode(TransInfo *t) if (t->tsnap.mode & SCE_SNAP_TO_EDGE_PERPENDICULAR) { t->flag |= T_DRAW_SNAP_SOURCE; } - - setSnappingCallback(t); - - if (t->spacetype == SPACE_VIEW3D) { - if (t->tsnap.object_context == nullptr) { - SET_FLAG_FROM_TEST(t->tsnap.flag, snap_use_backface_culling(t), SCE_SNAP_BACKFACE_CULLING); - t->tsnap.object_context = snap_object_context_create(t->scene, 0); - snap_object_context_init(t); - } - } - else if (t->spacetype == SPACE_SEQ) { - if (t->tsnap.seq_context == nullptr) { - t->tsnap.seq_context = snap_sequencer_data_alloc(t); - } - } } void transform_snap_grid_init(const TransInfo *t, float r_snap[3], float *r_snap_precision) @@ -902,125 +905,123 @@ void transform_snap_grid_init(const TransInfo *t, float r_snap[3], float *r_snap } } -void initSnapping(TransInfo *t, wmOperator *op) +void transform_snap_reset_from_mode(TransInfo *t, wmOperator *op) { ToolSettings *ts = t->settings; eSnapSourceOP snap_source = eSnapSourceOP(ts->snap_target); resetSnapping(t); + t->tsnap.mode = snap_mode_from_spacetype(t); t->tsnap.flag = snap_flag_from_spacetype(t); - t->tsnap.target_operation = snap_target_select_from_spacetype(t); + t->tsnap.target_operation = snap_target_select_from_spacetype_and_tool_settings(t); t->tsnap.face_nearest_steps = max_ii(ts->snap_face_nearest_steps, 1); - /* If snap property exists. */ + initSnappingMode(t); + + /* Overwrite defaults with values ​​in properties. */ PropertyRNA *prop; - if (op && (prop = RNA_struct_find_property(op->ptr, "snap")) && - RNA_property_is_set(op->ptr, prop)) - { - if (RNA_property_boolean_get(op->ptr, prop)) { - t->modifiers |= MOD_SNAP; - - if ((prop = RNA_struct_find_property(op->ptr, "snap_elements")) && - RNA_property_is_set(op->ptr, prop)) - { - t->tsnap.mode = eSnapMode(RNA_property_enum_get(op->ptr, prop)); - } - - /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of - * "target" (now, "source" is geometry to be moved and "target" is geometry to which moved - * geometry is snapped). */ - if ((prop = RNA_struct_find_property(op->ptr, "snap_target")) && - RNA_property_is_set(op->ptr, prop)) - { - snap_source = eSnapSourceOP(RNA_property_enum_get(op->ptr, prop)); - } - - if ((prop = RNA_struct_find_property(op->ptr, "snap_point")) && - RNA_property_is_set(op->ptr, prop)) - { - RNA_property_float_get_array(op->ptr, prop, t->tsnap.snap_target); - t->modifiers |= MOD_SNAP_FORCED; - t->tsnap.status |= SNAP_TARGET_FOUND; - } - - /* Snap align only defined in specific cases. */ - if ((prop = RNA_struct_find_property(op->ptr, "snap_align")) && - RNA_property_is_set(op->ptr, prop)) - { - SET_FLAG_FROM_TEST( - t->tsnap.flag, RNA_property_boolean_get(op->ptr, prop), SCE_SNAP_ROTATE); - - RNA_float_get_array(op->ptr, "snap_normal", t->tsnap.snapNormal); - normalize_v3(t->tsnap.snapNormal); - } - - if ((prop = RNA_struct_find_property(op->ptr, "use_snap_project")) && - RNA_property_is_set(op->ptr, prop)) - { - SET_FLAG_FROM_TEST( - t->tsnap.mode, RNA_property_boolean_get(op->ptr, prop), SCE_SNAP_INDIVIDUAL_PROJECT); - } - - /* Use_snap_self is misnamed and should be use_snap_active. */ - if ((prop = RNA_struct_find_property(op->ptr, "use_snap_self")) && - RNA_property_is_set(op->ptr, prop)) - { - SET_FLAG_FROM_TEST(t->tsnap.target_operation, - !RNA_property_boolean_get(op->ptr, prop), - SCE_SNAP_TARGET_NOT_ACTIVE); - } - - if ((prop = RNA_struct_find_property(op->ptr, "use_snap_edit")) && - RNA_property_is_set(op->ptr, prop)) - { - SET_FLAG_FROM_TEST(t->tsnap.target_operation, - !RNA_property_boolean_get(op->ptr, prop), - SCE_SNAP_TARGET_NOT_EDITED); - } - - if ((prop = RNA_struct_find_property(op->ptr, "use_snap_nonedit")) && - RNA_property_is_set(op->ptr, prop)) - { - SET_FLAG_FROM_TEST(t->tsnap.target_operation, - !RNA_property_boolean_get(op->ptr, prop), - SCE_SNAP_TARGET_NOT_NONEDITED); - } - - if ((prop = RNA_struct_find_property(op->ptr, "use_snap_selectable")) && - RNA_property_is_set(op->ptr, prop)) - { - SET_FLAG_FROM_TEST(t->tsnap.target_operation, - RNA_property_boolean_get(op->ptr, prop), - SCE_SNAP_TARGET_ONLY_SELECTABLE); - } - } - } - /* Use scene defaults only when transform is modal. */ - else if (t->flag & T_MODAL) { - if (t->tsnap.flag & SCE_SNAP) { - t->modifiers |= MOD_SNAP; + if (op && (prop = RNA_struct_find_property(op->ptr, "snap"))) { + if (RNA_property_is_set(op->ptr, prop)) { + SET_FLAG_FROM_TEST(t->modifiers, RNA_property_boolean_get(op->ptr, prop), MOD_SNAP); } - SET_FLAG_FROM_TEST(t->tsnap.target_operation, - (ts->snap_flag & SCE_SNAP_NOT_TO_ACTIVE), - SCE_SNAP_TARGET_NOT_ACTIVE); - SET_FLAG_FROM_TEST(t->tsnap.target_operation, - !(ts->snap_flag & SCE_SNAP_TO_INCLUDE_EDITED), - SCE_SNAP_TARGET_NOT_EDITED); - SET_FLAG_FROM_TEST(t->tsnap.target_operation, - !(ts->snap_flag & SCE_SNAP_TO_INCLUDE_NONEDITED), - SCE_SNAP_TARGET_NOT_NONEDITED); - SET_FLAG_FROM_TEST(t->tsnap.target_operation, - (ts->snap_flag & SCE_SNAP_TO_ONLY_SELECTABLE), - SCE_SNAP_TARGET_ONLY_SELECTABLE); + if ((prop = RNA_struct_find_property(op->ptr, "snap_elements")) && + RNA_property_is_set(op->ptr, prop)) + { + t->tsnap.mode = eSnapMode(RNA_property_enum_get(op->ptr, prop)); + } + + /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of + * "target" (now, "source" is geometry to be moved and "target" is geometry to which moved + * geometry is snapped). */ + if ((prop = RNA_struct_find_property(op->ptr, "snap_target")) && + RNA_property_is_set(op->ptr, prop)) + { + snap_source = eSnapSourceOP(RNA_property_enum_get(op->ptr, prop)); + } + + if ((prop = RNA_struct_find_property(op->ptr, "snap_point")) && + RNA_property_is_set(op->ptr, prop)) + { + RNA_property_float_get_array(op->ptr, prop, t->tsnap.snap_target); + t->modifiers |= MOD_SNAP_FORCED; + t->tsnap.status |= SNAP_TARGET_FOUND; + } + + /* Snap align only defined in specific cases. */ + if ((prop = RNA_struct_find_property(op->ptr, "snap_align")) && + RNA_property_is_set(op->ptr, prop)) + { + SET_FLAG_FROM_TEST(t->tsnap.flag, RNA_property_boolean_get(op->ptr, prop), SCE_SNAP_ROTATE); + + RNA_float_get_array(op->ptr, "snap_normal", t->tsnap.snapNormal); + normalize_v3(t->tsnap.snapNormal); + } + + if ((prop = RNA_struct_find_property(op->ptr, "use_snap_project")) && + RNA_property_is_set(op->ptr, prop)) + { + SET_FLAG_FROM_TEST( + t->tsnap.mode, RNA_property_boolean_get(op->ptr, prop), SCE_SNAP_INDIVIDUAL_PROJECT); + } + + /* Use_snap_self is misnamed and should be use_snap_active. */ + if ((prop = RNA_struct_find_property(op->ptr, "use_snap_self")) && + RNA_property_is_set(op->ptr, prop)) + { + SET_FLAG_FROM_TEST(t->tsnap.target_operation, + !RNA_property_boolean_get(op->ptr, prop), + SCE_SNAP_TARGET_NOT_ACTIVE); + } + + if ((prop = RNA_struct_find_property(op->ptr, "use_snap_edit")) && + RNA_property_is_set(op->ptr, prop)) + { + SET_FLAG_FROM_TEST(t->tsnap.target_operation, + !RNA_property_boolean_get(op->ptr, prop), + SCE_SNAP_TARGET_NOT_EDITED); + } + + if ((prop = RNA_struct_find_property(op->ptr, "use_snap_nonedit")) && + RNA_property_is_set(op->ptr, prop)) + { + SET_FLAG_FROM_TEST(t->tsnap.target_operation, + !RNA_property_boolean_get(op->ptr, prop), + SCE_SNAP_TARGET_NOT_NONEDITED); + } + + if ((prop = RNA_struct_find_property(op->ptr, "use_snap_selectable")) && + RNA_property_is_set(op->ptr, prop)) + { + SET_FLAG_FROM_TEST(t->tsnap.target_operation, + RNA_property_boolean_get(op->ptr, prop), + SCE_SNAP_TARGET_ONLY_SELECTABLE); + } } t->tsnap.source_operation = snap_source; - - initSnappingMode(t); - transform_snap_grid_init(t, t->snap_spatial, &t->snap_spatial_precision); transform_snap_flag_from_modifiers_set(t); +} + +void initSnapping(TransInfo *t, wmOperator *op) +{ + transform_snap_reset_from_mode(t, op); + transform_snap_grid_init(t, t->snap_spatial, &t->snap_spatial_precision); + setSnappingCallback(t); + + if (t->spacetype == SPACE_VIEW3D) { + if (t->tsnap.object_context == nullptr) { + SET_FLAG_FROM_TEST(t->tsnap.flag, snap_use_backface_culling(t), SCE_SNAP_BACKFACE_CULLING); + t->tsnap.object_context = snap_object_context_create(t->scene, 0); + snap_object_context_init(t); + } + } + else if (t->spacetype == SPACE_SEQ) { + if (t->tsnap.seq_context == nullptr) { + t->tsnap.seq_context = snap_sequencer_data_alloc(t); + } + } /* Default increment values. */ t->increment = float3(1.0f); @@ -1046,15 +1047,23 @@ void initSnapAngleIncrements(TransInfo *t) /* The final value of increment with precision is `t->increment[0] * t->increment_precision`. * Therefore, we divide `snap_angle_increment_*_precision` by `snap_angle_increment_*` * to compute `increment_precision`. */ + float increment; + float increment_precision; if (t->spacetype == SPACE_VIEW3D) { - t->increment[0] = t->settings->snap_angle_increment_3d; - t->increment_precision = t->settings->snap_angle_increment_3d_precision / - t->settings->snap_angle_increment_3d; + increment = t->settings->snap_angle_increment_3d; + increment_precision = t->settings->snap_angle_increment_3d_precision; } else { - t->increment[0] = t->settings->snap_angle_increment_2d; - t->increment_precision = t->settings->snap_angle_increment_2d_precision / - t->settings->snap_angle_increment_2d; + increment = t->settings->snap_angle_increment_2d; + increment_precision = t->settings->snap_angle_increment_2d_precision; + } + + t->increment[0] = increment; + if (increment != 0.0f) { + t->increment_precision = float(double(increment_precision) / double(increment)); + } + else { + t->increment_precision = 1.0f; } } @@ -1469,7 +1478,7 @@ static void snap_source_closest_fn(TransInfo *t) /* Object mode. */ if (t->options & CTX_OBJECT) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { - BLI_assert(tc->sorted_index_map); + BLI_assert(tc->sorted_index_map || !(t->flag & T_PROP_EDIT_ALL)); tc->foreach_index_selected([&](const int i) { TransData *td = &tc->data[i]; @@ -1524,7 +1533,7 @@ static void snap_source_closest_fn(TransInfo *t) } else { FOREACH_TRANS_DATA_CONTAINER (t, tc) { - BLI_assert(tc->sorted_index_map); + BLI_assert(tc->sorted_index_map || !(t->flag & T_PROP_EDIT_ALL)); tc->foreach_index_selected([&](const int i) { TransData *td = &tc->data[i]; @@ -1696,7 +1705,9 @@ static void snap_increment_apply(const TransInfo *t, const float loc[3], float r for (int i = 0; i <= t->idx_max; i++) { const float iter_fac = use_precision ? t->increment[i] * t->increment_precision : t->increment[i]; - r_out[i] = iter_fac * roundf(loc[i] / iter_fac); + if (iter_fac != 0.0f) { + r_out[i] = iter_fac * roundf(loc[i] / iter_fac); + } } } diff --git a/source/blender/editors/transform/transform_snap.hh b/source/blender/editors/transform/transform_snap.hh index 91dc419dff5..ccb54cd131e 100644 --- a/source/blender/editors/transform/transform_snap.hh +++ b/source/blender/editors/transform/transform_snap.hh @@ -37,6 +37,7 @@ bool transform_snap_is_active(const TransInfo *t); bool validSnap(const TransInfo *t); void transform_snap_grid_init(const TransInfo *t, float r_snap[3], float *r_snap_precision); +void transform_snap_reset_from_mode(TransInfo *t, wmOperator *op); void initSnapping(TransInfo *t, wmOperator *op); void freeSnapping(TransInfo *t); void initSnapAngleIncrements(TransInfo *t);