From 3f62f72f35bab014ac8ec0310cfe63aa1dddad0d Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 1 Feb 2025 16:15:58 +1100 Subject: [PATCH] See #2981. First attempt at having the door generator also do shape aspects, material constituents, and styles all simultaneously --- src/bonsai/bonsai/bim/module/model/data.py | 7 ++++ src/bonsai/bonsai/bim/module/model/door.py | 42 ++++++++++++++++++++++ src/bonsai/bonsai/bim/module/model/prop.py | 10 ++++++ src/bonsai/bonsai/bim/module/model/ui.py | 9 +++-- 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/model/data.py b/src/bonsai/bonsai/bim/module/model/data.py index 850ef2d31e..32af7c9286 100644 --- a/src/bonsai/bonsai/bim/module/model/data.py +++ b/src/bonsai/bonsai/bim/module/model/data.py @@ -76,6 +76,7 @@ class AuthoringData: cls.data["paginated_relating_types"] = cls.paginated_relating_types() cls.data["type_thumbnail"] = cls.type_thumbnail() # Only after .relating_type_id_current() + cls.data["materials"] = cls.materials() cls.data["is_voidable_element"] = cls.is_voidable_element() cls.data["has_visible_openings"] = cls.has_visible_openings() cls.data["has_visible_boundaries"] = cls.has_visible_boundaries() @@ -114,6 +115,12 @@ class AuthoringData: def type_thumbnail(cls): return cls.type_thumbnails.get(int(cls.data["relating_type_id_current"] or 0), 0) + @classmethod + def materials(cls): + results = [("0", "None", "No material")] + results.extend([(str(m.id()), m.Name or "Unnamed", "") for m in tool.Ifc.get().by_type("IfcMaterial")]) + return results + @classmethod def total_types(cls): return len(cls.data["type_elements_filtered"]) diff --git a/src/bonsai/bonsai/bim/module/model/door.py b/src/bonsai/bonsai/bim/module/model/door.py index d8208feaea..e2bb50d9c9 100644 --- a/src/bonsai/bonsai/bim/module/model/door.py +++ b/src/bonsai/bonsai/bim/module/model/door.py @@ -88,7 +88,49 @@ def update_door_modifier_representation(obj: bpy.types.Object) -> None: # (Model/Body defined only BEFORE Plan/Body to prevent #2744) body = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW") representation_data["context"] = body + representation_data["part_of_product"] = ifcopenshell.util.representation.get_part_of_product(element, body) model_representation = ifcopenshell.api.run("geometry.add_door_representation", ifc_file, **representation_data) + representation_data["part_of_product"] = None + if fallback_material := (int(props.lining_material) or int(props.panel_material)): + lining_material = tool.Ifc.get().by_id(int(props.lining_material) or fallback_material) + panel_material = tool.Ifc.get().by_id(int(props.panel_material) or fallback_material) + should_create_new_material_set = False + if material := ifcopenshell.util.element.get_material(element): + if ( + material.is_a("IfcMaterialConstituent") + and len(names := [c.Name for c in material.MaterialConstituents]) == 2 + and set(names) == {"Lining", "Framing"} + ): + should_create_new_material_set = False + else: + should_create_new_material_set = True + ifcopenshell.api.material.unassign_material(ifc_file, products=[element]) + if not material.is_a("IfcMaterial") and not ifc_file.get_total_inverses(material): + ifcopenshell.api.material.remove_material_set(ifc_file, material=material) + else: + should_create_new_material_set = True + if should_create_new_material_set: + material_set = ifcopenshell.api.material.add_material_set(ifc_file, set_type="IfcMaterialConstituentSet") + ifcopenshell.api.material.add_constituent( + ifc_file, constituent_set=material_set, material=lining_material, name="Lining" + ) + ifcopenshell.api.material.add_constituent( + ifc_file, constituent_set=material_set, material=panel_material, name="Framing" + ) + ifcopenshell.api.material.assign_material(ifc_file, products=[element], material=material_set) + + styles = { + "Lining": ifcopenshell.util.representation.get_material_style(lining_material, body), + "Framing": ifcopenshell.util.representation.get_material_style(panel_material, body), + } + for item in model_representation.Items: + if aspect := ifcopenshell.util.representation.get_item_shape_aspect(model_representation, item): + if style := styles.get(aspect.Name, None): + ifcopenshell.api.style.assign_item_style(ifc_file, item=item, style=style) + elif material := ifcopenshell.util.element.get_material(element): + ifcopenshell.api.material.unassign_material(ifc_file, products=[element]) + if not material.is_a("IfcMaterial") and not ifc_file.get_total_inverses(material): + ifcopenshell.api.material.remove_material_set(ifc_file, material=material) tool.Model.replace_object_ifc_representation(body, obj, model_representation) # Body/PLAN_VIEW representation diff --git a/src/bonsai/bonsai/bim/module/model/prop.py b/src/bonsai/bonsai/bim/module/model/prop.py index c6f56c207a..756f2255e0 100644 --- a/src/bonsai/bonsai/bim/module/model/prop.py +++ b/src/bonsai/bonsai/bim/module/model/prop.py @@ -49,6 +49,12 @@ def get_relating_type_id(self, context): return AuthoringData.data["relating_type_id"] +def get_materials(self, context): + if not AuthoringData.is_loaded: + AuthoringData.load() + return AuthoringData.data["materials"] + + def update_ifc_class(self, context): bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class) AuthoringData.data["relating_type_id"] = AuthoringData.relating_type_id() @@ -621,6 +627,10 @@ class BIMDoorProperties(PropertyGroup): frame_thickness: bpy.props.FloatProperty(name="Window Frame Thickness", default=0.035, subtype="DISTANCE") frame_depth: bpy.props.FloatProperty(name="Window Frame Depth", default=0.035, subtype="DISTANCE") + # Material properties + panel_material: bpy.props.EnumProperty(name="Panel Material", items=get_materials, options=set()) + lining_material: bpy.props.EnumProperty(name="Lining Material", items=get_materials, options=set()) + def get_general_kwargs(self, convert_to_project_units=False): kwargs = { "door_type": self.door_type, diff --git a/src/bonsai/bonsai/bim/module/model/ui.py b/src/bonsai/bonsai/bim/module/model/ui.py index f58ebb8087..04f103b69f 100644 --- a/src/bonsai/bonsai/bim/module/model/ui.py +++ b/src/bonsai/bonsai/bim/module/model/ui.py @@ -534,15 +534,20 @@ class BIM_PT_door(bpy.types.Panel): self.layout.prop(props, prop) lining_props = props.get_lining_kwargs() - self.layout.label(text="Lining properties") + self.layout.label(text="Lining Properties") for prop in lining_props: self.layout.prop(props, prop) panel_props = props.get_panel_kwargs() - self.layout.label(text="Panel properties") + self.layout.label(text="Panel Properties") for prop in panel_props: self.layout.prop(props, prop) + self.layout.use_property_split = True + self.layout.label(text="Material Properties") + self.layout.prop(props, "lining_material") + self.layout.prop(props, "panel_material") + update_door_modifier_bmesh(context) else: