diff --git a/src/bonsai/bonsai/bim/module/model/handler.py b/src/bonsai/bonsai/bim/module/model/handler.py index 824d93e989..305a76262b 100644 --- a/src/bonsai/bonsai/bim/module/model/handler.py +++ b/src/bonsai/bonsai/bim/module/model/handler.py @@ -58,6 +58,11 @@ def load_post(*args): ifcopenshell.api.add_post_listener( "material.edit_layer", "Bonsai.DumbSlab.RegenerateFromLayer", slab.DumbSlabPlaner().regenerate_from_layer ) + ifcopenshell.api.add_post_listener( + "material.edit_layer_usage", + "Bonsai.DumbSlab.RegenerateFromLayerSetUsage", + slab.DumbSlabPlaner().regenerate_from_layer_set_usage, + ) ifcopenshell.api.add_post_listener( "type.assign_type", "Bonsai.DumbSlab.RegenerateFromType", slab.DumbSlabPlaner().regenerate_from_type ) diff --git a/src/bonsai/bonsai/bim/module/model/slab.py b/src/bonsai/bonsai/bim/module/model/slab.py index 43337f090e..d34b213197 100644 --- a/src/bonsai/bonsai/bim/module/model/slab.py +++ b/src/bonsai/bonsai/bim/module/model/slab.py @@ -131,9 +131,9 @@ class DumbSlabGenerator: matrix_world = Matrix() matrix_world.translation = self.location if self.container_obj: - matrix_world.translation.z = self.container_obj.location.z - self.depth + matrix_world.translation.z = self.container_obj.location.z else: - matrix_world.translation.z -= self.depth + matrix_world.translation.z obj.matrix_world = Matrix.Rotation(self.x_angle, 4, "X") @ matrix_world bpy.context.view_layer.update() @@ -195,6 +195,26 @@ class DumbSlabGenerator: class DumbSlabPlaner: + def regenerate_from_layer_set_usage(self, usecase_path, ifc_file, settings): + self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file) + obj = bpy.context.active_object + element = tool.Ifc.get_entity(obj) + + if tool.Model.get_usage_type(element) != "LAYER3": + return + + # Called from materil.add_layer or material.remove_layer + material = ifcopenshell.util.element.get_material(element) + material_set_usage = tool.Ifc.get().by_id(material.id()) + layer_set = material_set_usage.ForLayerSet + + total_thickness = sum([l.LayerThickness for l in layer_set.MaterialLayers]) + if not total_thickness: + return + + self.change_thickness(element, total_thickness) + + def regenerate_from_layer(self, usecase_path, ifc_file, settings): self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file) @@ -267,20 +287,38 @@ class DumbSlabPlaner: self.change_thickness(related_object, new_thickness) def change_thickness(self, element: ifcopenshell.entity_instance, thickness: float) -> None: + layer_params = tool.Model.get_material_layer_parameters(element) body_context = ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW") obj = tool.Ifc.get_object(element) if not obj: return - delta_thickness = (thickness * self.unit_scale) - obj.dimensions.z - if round(delta_thickness, 2) == 0: + if thickness == 0: return representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") if representation: extrusion = tool.Model.get_extrusion(representation) if extrusion: + x, y, z = extrusion.ExtrudedDirection.DirectionRatios + offset = layer_params["offset"] + offset_vector = Vector((0.0, 0.0, offset / self.unit_scale)) + if layer_params["direction_sense"] == "POSITIVE": + y = abs(y) + z = abs(z) + if layer_params["direction_sense"] == "NEGATIVE": + y = -abs(y) + z = -abs(z) + offset_vector = -offset_vector + extrusion.ExtrudedDirection.DirectionRatios = (x, y, z) extrusion.Depth = thickness + + if offset != 0.0 and not extrusion.Position: + tool.Model.add_extrusion_position(extrusion, offset) + if extrusion.Position: + extrusion.Position.Location.Coordinates = tuple(offset_vector) + + else: props = bpy.context.scene.BIMModelProperties x_angle = 0 if tool.Cad.is_x(props.x_angle, 0, tolerance=0.001) else props.x_angle @@ -330,8 +368,6 @@ class DumbSlabPlaner: should_sync_changes_first=False, ) - obj.location[2] -= delta_thickness - class EnableEditingSketchExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.enable_editing_sketch_extrusion_profile" diff --git a/src/bonsai/bonsai/tool/model.py b/src/bonsai/bonsai/tool/model.py index 4b348fc31b..240d00d495 100644 --- a/src/bonsai/bonsai/tool/model.py +++ b/src/bonsai/bonsai/tool/model.py @@ -2044,3 +2044,16 @@ class Model(bonsai.core.tool.Model): @classmethod def add_filled_opening(cls, voided_obj: bpy.types.Object, filling_obj: bpy.types.Object) -> None: FilledOpeningGenerator().generate(filling_obj, voided_obj) + + @classmethod + def add_extrusion_position(cls, extrusion: ifcopenshell.entity_instance, offset: float) -> None: + ifc_file = tool.Ifc.get() + + position = ifc_file.createIfcAxis2Placement3D( + ifc_file.createIfcCartesianPoint((0.0, 0.0, offset)), + ifc_file.createIfcDirection((0.0, 0.0, 1.0)), + ifc_file.createIfcDirection((1.0, 0.0, 0.0)), + ) + + extrusion.Position = position + diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py index 894d0e316b..9016c1ae12 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py @@ -27,6 +27,8 @@ def add_slab_representation( file: ifcopenshell.file, context: ifcopenshell.entity_instance, depth: float = 0.2, + direction_sense: str = "POSITIVE", + offset: float = 0.0, x_angle: float = 0.0, clippings: Optional[list[Union[Clipping, dict[str, Any]]]] = None, polyline: Optional[list[tuple[float, float]]] = None, @@ -56,6 +58,8 @@ def add_slab_representation( usecase.settings = { "context": context, "depth": depth, + "direction_sense": direction_sense, + "offset": offset, "x_angle": x_angle, "clippings": clippings if clippings is not None else [], "polyline": polyline, @@ -80,7 +84,7 @@ class Usecase: size = self.convert_si_to_unit(1) points = ((0.0, 0.0), (size, 0.0), (size, size), (0.0, size), (0.0, 0.0)) if self.settings["polyline"]: - points = [(self.convert_si_to_unit(p[0]), self.convert_si_to_unit(p[1])) for p in self.settings["polyline"]] + points = [(self.convert_si_to_unit(p[0]), self.convert_si_to_unit(p[1] * (1 / cos(self.settings["x_angle"])))) for p in self.settings["polyline"]] if self.file.schema == "IFC2X3": curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points]) else: @@ -94,9 +98,9 @@ class Usecase: position = None # default position for IFC2X3 where .Position is not optional - if self.file.schema == "IFC2X3": + if self.file.schema == "IFC2X3" or self.settings["offset"] != 0: position = self.file.createIfcAxis2Placement3D( - self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)), + self.file.createIfcCartesianPoint((0.0, 0.0, self.convert_si_to_unit(self.settings["offset"]))), self.file.createIfcDirection((0.0, 0.0, 1.0)), self.file.createIfcDirection((1.0, 0.0, 0.0)), )