Add wall regen helper, fillet underside, bug sweep

Wall body rebuild + slab underside re-clip are now unified behind
tool.Model.regenerate_wall and called from split / merge / extend
operators. Fillet corner walls accept extend-to-underside (poll +
operator partition switched to is_path_connectable_wall) and surface
the wall-unjoin gizmo without the parametric-edit gate, since
fillets cannot enter that lifecycle. DumbWallJoiner.split strips the
duplicate's inherited slab-trim booleans up front so wall2 lands at
the cut point. regenerate_fillet_corner_wall re-clips after the body
rewrite so a prior extend-to-slab survives neighbour recalcs.

Drive-by bug sweep: tuple typo in hotkey_S_G's IfcSpace check,
defensive .get() in draw_regen_operations for partial AuthoringData
loads, and a try/except in get_active_representation matching the
existing convention for stale mesh ifc_definition_ids after a
representation rebuild.

Tests cover the regenerate_wall branching, the get_active_representation
stale-id contract, and the GizmoWallExtendVertically fillet acceptance.

Generated with the assistance of an AI coding tool.
This commit is contained in:
Gorgious56
2026-06-14 10:56:07 +02:00
parent bb8681a954
commit 55428a0878
8 changed files with 243 additions and 15 deletions
+31
View File
@@ -135,6 +135,37 @@ class TestGetRepresentationData(NewFile):
assert subject.get_representation_data(representation) == data
class TestGetActiveRepresentation(NewFile):
def test_returns_representation_for_live_id(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
representation = ifc.createIfcShapeRepresentation()
mesh = bpy.data.meshes.new("Mesh")
obj = bpy.data.objects.new("Object", mesh)
tool.Geometry.get_mesh_props(mesh).ifc_definition_id = representation.id()
assert subject.get_active_representation(obj) == representation
def test_returns_none_when_mesh_has_no_id(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
assert subject.get_active_representation(obj) is None
def test_returns_none_when_id_is_stale(self):
"""A representation rebuild can free the old entity while obj.data
still tracks its id. Returning ``None`` keeps every UI redraw alive
instead of spamming ``RuntimeError`` from the by_id lookup."""
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
representation = ifc.createIfcShapeRepresentation()
mesh = bpy.data.meshes.new("Mesh")
obj = bpy.data.objects.new("Object", mesh)
stale_id = representation.id()
tool.Geometry.get_mesh_props(mesh).ifc_definition_id = stale_id
ifc.remove(representation)
assert subject.get_active_representation(obj) is None
class TestGetRepresentationId(NewFile):
def test_run(self):
ifc = ifcopenshell.file()