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.
This commit is contained in:
Germano Cavalcante
2025-06-26 10:36:45 -03:00
parent 28bd5a7832
commit 9879132c7c
3 changed files with 160 additions and 148 deletions

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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);