Fix #121378: Can't snap parent to children with Affect Only Parent activated

The flag that skips objects for snapping (`BA_SNAP_FIX_DEPS_FIASCO`) is
set for all objects that have a `DEG_OB_COMP_TRANSFORM` relation.

However, when the **Affect Only Parent** option is enabled, children
are not transformed and therefore could still be snapped to.
Despite this, the `DEG_OB_COMP_TRANSFORM` dependency remains present on
them.

To address this, a new callback was introduced that skips
Transform-to-Transform dependencies while preserving
Transform-to-Geometry dependencies in this scenario.

This approach may introduce some false positives, leading to spurious
dependency cycles (e.g., when constraints are involved).
Nevertheless, the trade-off favors accepting these false positives
rather than disabling the feature altogether.

Also make non-functional change that just removes a flag before the snap
loop, thus avoiding checking if the flag exists inside the loop.

Ref !142007
This commit is contained in:
Germano Cavalcante
2025-07-15 20:16:47 -03:00
committed by Campbell Barton
parent 1fa2be8a9b
commit b42e4052b7
2 changed files with 30 additions and 9 deletions

View File

@@ -311,7 +311,18 @@ static void trans_object_base_deps_flag_prepare(const TransInfo *t,
}
}
static void set_trans_object_base_deps_flag_cb(ID *id, eDepsObjectComponentType component)
static void tag_trans_objects_with_geometry_dep_only_fn(ID *id, eDepsObjectComponentType component)
{
/* Here we only handle object IDs. */
if (GS(id->name) != ID_OB) {
return;
}
if (component == DEG_OB_COMP_GEOMETRY) {
id->tag |= ID_TAG_DOIT;
}
}
static void tag_trans_objects_dep_fn(ID *id, eDepsObjectComponentType component)
{
/* Here we only handle object IDs. */
if (GS(id->name) != ID_OB) {
@@ -329,11 +340,21 @@ static void flush_trans_object_base_deps_flag(const TransInfo *t, Object *object
return;
}
object->id.tag |= ID_TAG_DOIT;
DEG_foreach_dependent_ID_component(t->depsgraph,
&object->id,
DEG_OB_COMP_TRANSFORM,
DEG_FOREACH_COMPONENT_IGNORE_TRANSFORM_SOLVERS,
set_trans_object_base_deps_flag_cb);
DEG_foreach_dependent_ID_component(
t->depsgraph,
&object->id,
DEG_OB_COMP_TRANSFORM,
DEG_FOREACH_COMPONENT_IGNORE_TRANSFORM_SOLVERS,
/* When we transform parents while skipping children, we only traverse the GEOMETRY-dependent
* components. This avoids marking children as not participating in snapping but still marks
* objects with modifier dependencies.
* Unfortunately, some transform-dependent objects that are not children may also be skipped,
* such as constrained ones.
* See #121378 for details. */
(t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) ? tag_trans_objects_with_geometry_dep_only_fn :
tag_trans_objects_dep_fn);
}
static void trans_object_base_deps_flag_finish(const TransInfo *t,
@@ -690,6 +711,7 @@ static void createTransObject(bContext *C, TransInfo *t)
tdo->xcs, ob, ob_parent_recurse, object::XFORM_OB_SKIP_CHILD_PARENT_APPLY);
BLI_ghash_insert(objects_parent_root, ob, ob_parent_recurse);
base->flag_legacy |= BA_TRANSFORM_LOCKED_IN_PLACE;
base->flag_legacy &= ~BA_SNAP_FIX_DEPS_FIASCO;
}
}
}
@@ -711,6 +733,7 @@ static void createTransObject(bContext *C, TransInfo *t)
object::object_xform_skip_child_container_item_ensure(
tdo->xcs, ob, nullptr, object::XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM);
base->flag_legacy |= BA_TRANSFORM_LOCKED_IN_PLACE;
base->flag_legacy &= ~BA_SNAP_FIX_DEPS_FIASCO;
}
else {
Object *ob_parent_recurse = static_cast<Object *>(

View File

@@ -409,9 +409,7 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx,
return false;
}
if ((snap_target_select == SCE_SNAP_TARGET_ALL) ||
(base->flag_legacy & BA_TRANSFORM_LOCKED_IN_PLACE))
{
if (snap_target_select == SCE_SNAP_TARGET_ALL) {
return true;
}