mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 18:21:59 +00:00
Fix fillet partner missing from wall unjoin gizmo
GizmoWallUnjoinSingle.poll accepts fillet-corner walls via the looser tool.Parametric.is_path_connectable_wall predicate (fillet corners have no LAYER2 usage by IFC spec, but they still participate in IfcRelConnectsPathElements). The partner filter inside _iter_path_connections used the stricter tool.Blender.Modifier.is_wall (LAYER2-only), so adjacent LAYER2 walls silently dropped their fillet-corner partners from the connection list — the unjoin icon appeared when the fillet wall itself was selected but not on either of its LAYER2 neighbours. Switch the partner filter to is_path_connectable_wall so host and partner predicates match. Add a regression test for the fillet case and an AST forward-compat guard pinning the predicate symbol so a future "tidy the imports" can't silently re-introduce the asymmetry. Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -202,11 +202,11 @@ def _make_path_rel(relating, related, relating_ct, related_ct, kind="IfcRelConne
|
||||
)
|
||||
|
||||
|
||||
def _run_iter_path_connections(elem, *, is_wall_predicate=lambda _e: True):
|
||||
def _run_iter_path_connections(elem, *, partner_predicate=lambda _e: True):
|
||||
from bonsai import tool
|
||||
from bonsai.bim.module.model.wall import _iter_path_connections
|
||||
|
||||
with patch.object(tool.Blender.Modifier, "is_wall", side_effect=is_wall_predicate):
|
||||
with patch.object(tool.Parametric, "is_path_connectable_wall", side_effect=partner_predicate):
|
||||
return _iter_path_connections(elem)
|
||||
|
||||
|
||||
@@ -260,14 +260,30 @@ def test_iter_path_connections_skips_non_wall_partners():
|
||||
relating=self_elem, related=non_wall_partner, relating_ct="ATEND", related_ct="ATSTART"
|
||||
)
|
||||
elem = SimpleNamespace(ConnectedTo=[rel_wall, rel_non_wall], ConnectedFrom=[])
|
||||
result = _run_iter_path_connections(elem, is_wall_predicate=lambda e: e is wall_partner)
|
||||
result = _run_iter_path_connections(elem, partner_predicate=lambda e: e is wall_partner)
|
||||
assert result == [(wall_partner, "ATEND", "ATSTART")]
|
||||
|
||||
|
||||
def test_iter_path_connections_includes_fillet_corner_partner():
|
||||
# Fillet-corner walls carry no LAYER2 usage but are still valid path
|
||||
# partners. The enumeration must use the same predicate the gizmo group's
|
||||
# poll uses for the host wall — otherwise the corner is silently dropped
|
||||
# from the neighbour's connection list and looks unconnected from the
|
||||
# LAYER2 wall's perspective.
|
||||
self_elem = object()
|
||||
fillet_partner = object()
|
||||
rel = _make_path_rel(
|
||||
relating=self_elem, related=fillet_partner, relating_ct="ATEND", related_ct="ATSTART"
|
||||
)
|
||||
elem = SimpleNamespace(ConnectedTo=[rel], ConnectedFrom=[])
|
||||
result = _run_iter_path_connections(elem, partner_predicate=lambda e: e is fillet_partner)
|
||||
assert result == [(fillet_partner, "ATEND", "ATSTART")]
|
||||
|
||||
|
||||
def test_iter_path_connections_tolerates_none_partner_refs():
|
||||
# Malformed / partial IFC files can leave a rel's element ref unset.
|
||||
# Without a None guard, `Modifier.is_wall(None)` would raise on
|
||||
# `None.is_a(...)` mid-frame and silently break the gizmo group.
|
||||
# Without a None guard, the partner predicate would receive None and
|
||||
# raise on `.is_a(...)` mid-frame, silently breaking the gizmo group.
|
||||
self_elem = object()
|
||||
other = object()
|
||||
rel_none = _make_path_rel(relating=self_elem, related=None, relating_ct="ATEND", related_ct="ATSTART")
|
||||
|
||||
Reference in New Issue
Block a user