mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
Add LAYER2 wall mirror support
Three changes: 1. Mirror LAYER2 (wall) geometry correctly: exclude LAYER2 from the assign_inverted_type path (which only flips the type geometry and leaves the instance body unchanged) and route it through invert_representation like LAYER3 slabs. For Y-axis flips also invert DirectionSense and negate OffsetFromReferenceLine on the IfcMaterialLayerSetUsage so regenerated walls stay correct. 2. Surface bim.mirror_geometry in the right-click context menu and Object menu so it is reachable without activating the BIM Tool. 3. Guard bpy.ops.bim.extend_to_underside.__doc__ with try/except so a missing operator (branch without that feature) does not crash the entire tool-header draw and hide all other buttons. Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -367,6 +367,8 @@ def register():
|
||||
)
|
||||
|
||||
bpy.types.VIEW3D_MT_add.prepend(ui.add_menu)
|
||||
bpy.types.VIEW3D_MT_object_context_menu.append(ui.object_menu)
|
||||
bpy.types.VIEW3D_MT_object.append(ui.object_menu)
|
||||
bpy.app.handlers.load_post.append(handler.load_post)
|
||||
|
||||
workspace.load_custom_icons()
|
||||
@@ -399,5 +401,7 @@ def unregister():
|
||||
|
||||
bpy.app.handlers.load_post.remove(handler.load_post)
|
||||
bpy.types.VIEW3D_MT_add.remove(ui.add_menu)
|
||||
bpy.types.VIEW3D_MT_object_context_menu.remove(ui.object_menu)
|
||||
bpy.types.VIEW3D_MT_object.remove(ui.object_menu)
|
||||
|
||||
workspace.unload_custom_icons()
|
||||
|
||||
@@ -757,17 +757,30 @@ class TrueMirrorElements(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
usage_type = tool.Model.get_usage_type(element)
|
||||
type_has_reps = bool(type_element and (type_element.RepresentationMaps or []))
|
||||
is_assign_type_path = bool(type_element and element.id() != type_element.id() and usage_type != "LAYER3" and type_has_reps)
|
||||
# LAYER2 (walls) and LAYER3 (slabs) generate instance-specific bodies via DumbWallGenerator /
|
||||
# DumbSlabGenerator rather than mapping the type's RepresentationMaps. assign_inverted_type
|
||||
# only flips the *type* geometry and would leave the instance body unchanged, so both layer
|
||||
# usage types must go through invert_representation instead.
|
||||
is_assign_type_path = bool(type_element and element.id() != type_element.id() and usage_type not in ("LAYER2", "LAYER3") and type_has_reps)
|
||||
if is_assign_type_path:
|
||||
self.assign_inverted_type(element, mirror_axes)
|
||||
else:
|
||||
# invert representation of entity directly;
|
||||
# LAYER3 (slabs) and elements whose type carries no geometry use this path
|
||||
# LAYER2/LAYER3 (walls/slabs) and elements whose type carries no geometry use this path
|
||||
# Update opening placements BEFORE invert_representation so its internal reload
|
||||
# sees the correct positions. No element rotation change on this path → frame_change = I.
|
||||
if opening_placements_before and M_slab_before is not None:
|
||||
self._apply_opening_mirror(element, mirror_axes, M_slab_before, opening_placements_before, np.eye(3))
|
||||
self.invert_representation(element, mirror_axes)
|
||||
# For LAYER2 walls, layers stack along local Y (LayerSetDirection=AXIS2). A Y-axis flip
|
||||
# reverses that direction, so DirectionSense and OffsetFromReferenceLine must both invert.
|
||||
# (X/Z flips don't touch local Y, and the assign_inverted_type path handles Y-mirror via
|
||||
# a 180°Z element rotation which implicitly flips local Y without changing the IFC attribute.)
|
||||
if usage_type == "LAYER2" and mirror_axes[1] > 0.5:
|
||||
mat_usage = ifcopenshell.util.element.get_material(element, should_inherit=False)
|
||||
if mat_usage and mat_usage.is_a("IfcMaterialLayerSetUsage"):
|
||||
mat_usage.DirectionSense = "NEGATIVE" if mat_usage.DirectionSense == "POSITIVE" else "POSITIVE"
|
||||
mat_usage.OffsetFromReferenceLine = -mat_usage.OffsetFromReferenceLine
|
||||
|
||||
context.view_layer.update()
|
||||
|
||||
|
||||
@@ -189,6 +189,12 @@ class LaunchTypeManager(bpy.types.Operator):
|
||||
op.relating_type = relating_type["id"]
|
||||
|
||||
|
||||
def object_menu(self, context):
|
||||
if context.active_object and tool.Ifc.get_entity(context.active_object):
|
||||
self.layout.separator()
|
||||
self.layout.operator("bim.mirror_geometry", icon="MOD_MIRROR")
|
||||
|
||||
|
||||
class BIM_PT_authoring(Panel):
|
||||
bl_label = "Architectural"
|
||||
bl_idname = "BIM_PT_authoring"
|
||||
|
||||
Reference in New Issue
Block a user