diff --git a/src/bonsai/bonsai/tool/loader.py b/src/bonsai/bonsai/tool/loader.py index eae2f35c52..4758bcefd3 100644 --- a/src/bonsai/bonsai/tool/loader.py +++ b/src/bonsai/bonsai/tool/loader.py @@ -1090,11 +1090,12 @@ class Loader(bonsai.core.tool.Loader): no = Vector([0.0, 0.0, 1.0]) co = Vector((0.0, 0.0, offset)) # Bisect planes are always horizontal (world Z) for AXIS3. - # For well-formed IFC data, the mesh local Z span equals total_perp_thickness - # (extrusion.Depth is always set to thickness / extrusion_vec.z so that - # extrusion.Depth × extrusion_vec.z = thickness). depth_scale is kept as - # a safety net for IFC files from other authoring tools where the extrusion - # depth may not exactly match the sum of LayerThicknesses. + # The mesh local Z span should equal total_perp_thickness × unit_scale: + # extrusion.Depth × extrusion_vec.z = total_perp_thickness + # If the IFC data is inconsistent (e.g. from old Bonsai code that incorrectly + # scaled extrusion.Depth for ObjectPlacement-rotated slabs), we self-heal: + # scale the mesh vertices to the correct Z span and fix the stored depth so + # future imports load correctly without this correction. extrusion_vec = cls.get_extrusion_vector(element).normalized() ifc_extrusion_depth = None if body_rep := ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW"): @@ -1107,6 +1108,22 @@ class Loader(bonsai.core.tool.Loader): total_perp_thickness = sum(l.LayerThickness for l in layer_set.MaterialLayers) if ifc_extrusion_depth and total_perp_thickness: depth_scale = abs(extrusion_vec.z) * (ifc_extrusion_depth / total_perp_thickness) + if abs(depth_scale - 1.0) > 1e-6 and ifc_extrusion_depth and body_rep: + # Z_span / depth_scale == total_perp_thickness × unit_scale for any + # extrusion direction, so scaling from the mesh bottom is always correct. + min_z = min(v.co.z for v in bm.verts) + for v in bm.verts: + v.co.z = min_z + (v.co.z - min_z) / depth_scale + # Fix the IFC data so future imports don't require this correction. + extrusion_vec_z = abs(extrusion_vec.z) + correct_depth = total_perp_thickness / extrusion_vec_z if extrusion_vec_z > 1e-6 else total_perp_thickness + for item in ifcopenshell.util.representation.resolve_representation(body_rep).Items: + while item.is_a("IfcBooleanResult"): + item = item.FirstOperand + if item.is_a("IfcExtrudedAreaSolid"): + item.Depth = correct_depth + break + depth_scale = 1.0 elif usage.LayerSetDirection == "AXIS1": co = Vector((0.0, 0.0, offset)) no = cls.get_extrusion_vector(element).normalized()