mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Fix slab layer geometry for rotated and angled slabs
Three bugs fixed: 1. EditAssignedMaterial was sweeping all slabs that share a layer set when changing material properties, instead of updating only the selected element's assigned material. 2. slice_layerset_mesh (loader.py): layer bisect positions were scaled incorrectly due to a stale unit_scale and a reversed/missing DirectionSense guard. Now always recalculates unit_scale fresh and correctly applies sense_factor. 3. change_thickness (slab.py): extrusion depth was computed as `thickness / cos(obj.rotation_euler.x)`, which incorrectly scaled ObjectPlacement-rotated slabs (where the extrusion direction is local Z and no scaling is needed). The correct formula is `thickness / extrusion_vec.z`, which handles both ObjectPlacement rotation (extrusion_vec.z ≈ 1.0 → no scale) and ExtrudedDirection tilts (extrusion_vec.z < 1.0 → scale up) uniformly. The resulting slab was 1.414× too thick for 45°-rotated slabs, making both material layers appear fatter than specified. slice_layerset_mesh retains a depth_scale safety factor (extrusion_vec.z × ifc_depth / total_layer_thickness) as a robustness guard for IFC files from other authoring tools where extrusion depth may not match the sum of LayerThicknesses.
This commit is contained in:
@@ -281,8 +281,17 @@ class DumbSlabPlaner:
|
||||
existing_x_angle = 0 if tool.Cad.is_x(existing_x_angle, 2 * pi, tolerance=0.001) else existing_x_angle
|
||||
direction_ratios = Vector(extrusion.ExtrudedDirection.DirectionRatios)
|
||||
offset_direction = direction_ratios.copy()
|
||||
perpendicular_depth = thickness * abs(1 / cos(existing_x_angle))
|
||||
perpendicular_offset = layer_offset * abs(1 / cos(existing_x_angle)) / self.unit_scale
|
||||
# The extrusion depth needed to achieve a given perpendicular thickness depends on
|
||||
# how much the extrusion direction deviates from the slab face normal (local Z).
|
||||
# For an ObjectPlacement-rotated slab, extrusion_vec.z ≈ 1.0 → no scaling.
|
||||
# For an ExtrudedDirection-tilted slab, extrusion_vec.z < 1.0 → scale up.
|
||||
extrusion_z = abs(direction_ratios.normalized().z)
|
||||
if extrusion_z > 1e-6:
|
||||
perpendicular_depth = thickness / extrusion_z
|
||||
perpendicular_offset = layer_offset / extrusion_z / self.unit_scale
|
||||
else:
|
||||
perpendicular_depth = thickness
|
||||
perpendicular_offset = layer_offset / self.unit_scale
|
||||
|
||||
ifc_position = extrusion.Position
|
||||
|
||||
|
||||
@@ -1086,6 +1086,7 @@ class Loader(bonsai.core.tool.Loader):
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
prev_co = None
|
||||
depth_scale = 1.0
|
||||
if not usage:
|
||||
sense_factor = 1 # Assume the extrusion vector points in the direction sense
|
||||
no = cls.get_extrusion_vector(element).normalized()
|
||||
@@ -1095,9 +1096,26 @@ class Loader(bonsai.core.tool.Loader):
|
||||
no = cls.get_extrusion_vector(element).normalized()
|
||||
no = no.cross(Vector([1.0, 0.0, 0.0]))
|
||||
elif usage.LayerSetDirection == "AXIS3":
|
||||
co = Vector((0.0, 0.0, offset))
|
||||
no = cls.get_extrusion_vector(element).normalized()
|
||||
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.
|
||||
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"):
|
||||
for item in ifcopenshell.util.representation.resolve_representation(body_rep).Items:
|
||||
while item.is_a("IfcBooleanResult"):
|
||||
item = item.FirstOperand
|
||||
if item.is_a("IfcExtrudedAreaSolid"):
|
||||
ifc_extrusion_depth = item.Depth
|
||||
break
|
||||
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)
|
||||
elif usage.LayerSetDirection == "AXIS1":
|
||||
co = Vector((0.0, 0.0, offset))
|
||||
no = cls.get_extrusion_vector(element).normalized()
|
||||
@@ -1114,7 +1132,7 @@ class Loader(bonsai.core.tool.Loader):
|
||||
for i, layer in enumerate(layer_set.MaterialLayers):
|
||||
if i != last_i:
|
||||
prev_co = co.copy()
|
||||
co += no * layer.LayerThickness * unit_scale
|
||||
co += no * layer.LayerThickness * depth_scale * unit_scale
|
||||
bisect_geom = bmesh.ops.bisect_plane(
|
||||
bm, geom=bm.verts[:] + bm.edges[:] + bm.faces[:], dist=0.0001, plane_co=co, plane_no=no
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user