From 5c1d26ac91f88b9134c714f2cba2d4987c9f30f1 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 4 Jun 2021 17:30:37 +1000 Subject: [PATCH] Dumb walls and slabs now are always exported as parametric solids instead of meshes. --- .../blenderbim/bim/module/model/handler.py | 3 ++ .../blenderbim/bim/module/model/product.py | 2 +- .../blenderbim/bim/module/model/slab.py | 10 ++++-- .../blenderbim/bim/module/model/wall.py | 34 ++++++++++++++++--- .../ifcopenshell/api/__init__.py | 8 ++--- .../api/geometry/add_representation.py | 2 +- 6 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/handler.py b/src/blenderbim/blenderbim/bim/module/model/handler.py index f5cedd2186..a3cc58b31c 100644 --- a/src/blenderbim/blenderbim/bim/module/model/handler.py +++ b/src/blenderbim/blenderbim/bim/module/model/handler.py @@ -10,11 +10,14 @@ from bpy.app.handlers import persistent def load_post(*args): ifcopenshell.api.add_post_listener("geometry.add_representation", None, product.generate_box) + IfcStore.add_element_listener(wall.element_listener) + ifcopenshell.api.add_pre_listener("geometry.add_representation", None, wall.ensure_solid) ifcopenshell.api.add_post_listener("geometry.add_representation", None, wall.generate_axis) ifcopenshell.api.add_post_listener("geometry.add_representation", None, wall.calculate_quantities) ifcopenshell.api.add_pre_listener("material.edit_layer", None, wall.DumbWallPlaner().regenerate_from_layer) ifcopenshell.api.add_pre_listener("type.assign_type", None, wall.DumbWallPlaner().regenerate_from_type) IfcStore.add_element_listener(slab.element_listener) + ifcopenshell.api.add_pre_listener("geometry.add_representation", None, slab.ensure_solid) ifcopenshell.api.add_pre_listener("material.edit_layer", None, slab.DumbSlabPlaner().regenerate_from_layer) ifcopenshell.api.add_pre_listener("type.assign_type", None, slab.DumbSlabPlaner().regenerate_from_type) diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 4dd2d31fc8..9b5d3e93d4 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -65,7 +65,7 @@ class AddTypeInstance(bpy.types.Operator): return {"FINISHED"} -def generate_box(usecase_path, ifc_file, **settings): +def generate_box(usecase_path, ifc_file, settings): box_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Box", "MODEL_VIEW") if not box_context: return diff --git a/src/blenderbim/blenderbim/bim/module/model/slab.py b/src/blenderbim/blenderbim/bim/module/model/slab.py index ab1eda0071..6e57496e6b 100644 --- a/src/blenderbim/blenderbim/bim/module/model/slab.py +++ b/src/blenderbim/blenderbim/bim/module/model/slab.py @@ -30,6 +30,7 @@ def mode_callback(obj, data): parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric") if not parametric or parametric["Engine"] != "BlenderBIM.DumbSlab": return + IfcStore.edited_objs.add(obj) modifier = [m for m in obj.modifiers if m.type == "SOLIDIFY"] if modifier: return @@ -52,6 +53,11 @@ def mode_callback(obj, data): modifier.thickness = depth +def ensure_solid(usecase_path, ifc_file, settings): + # TODO: check if voids are present + settings["ifc_representation_class"] = "IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef" + + class DumbSlabGenerator: def __init__(self, relating_type): self.relating_type = relating_type @@ -117,7 +123,7 @@ class DumbSlabGenerator: class DumbSlabPlaner: - def regenerate_from_layer(self, usecase_path, ifc_file, **settings): + def regenerate_from_layer(self, usecase_path, ifc_file, settings): self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file) layer = settings["layer"] thickness = settings["attributes"].get("LayerThickness") @@ -142,7 +148,7 @@ class DumbSlabPlaner: for element in rel.RelatedObjects: self.change_thickness(element, delta_thickness) - def regenerate_from_type(self, usecase_path, ifc_file, **settings): + def regenerate_from_type(self, usecase_path, ifc_file, settings): self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file) new_material = ifcopenshell.util.element.get_material(settings["relating_type"]) if not new_material.is_a("IfcMaterialLayerSet"): diff --git a/src/blenderbim/blenderbim/bim/module/model/wall.py b/src/blenderbim/blenderbim/bim/module/model/wall.py index 0c6ec6b426..764c4e1d9a 100644 --- a/src/blenderbim/blenderbim/bim/module/model/wall.py +++ b/src/blenderbim/blenderbim/bim/module/model/wall.py @@ -7,6 +7,7 @@ import ifcopenshell.util.unit import ifcopenshell.util.element import ifcopenshell.util.representation import mathutils.geometry +import blenderbim.bim.handler from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.pset.data import Data as PsetData from ifcopenshell.api.material.data import Data as MaterialData @@ -14,6 +15,27 @@ from math import pi, degrees from mathutils import Vector, Matrix +def element_listener(element, obj): + blenderbim.bim.handler.subscribe_to(obj, "mode", mode_callback) + + +def mode_callback(obj, data): + for obj in bpy.context.selected_objects + [bpy.context.active_object]: + if ( + obj.mode != "EDIT" + or not obj.data + or not isinstance(obj.data, (bpy.types.Mesh, bpy.types.Curve, bpy.types.TextCurve)) + or not obj.BIMObjectProperties.ifc_definition_id + or not bpy.context.scene.BIMProjectProperties.is_authoring + ): + return + product = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id) + parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric") + if not parametric or parametric["Engine"] != "BlenderBIM.DumbWall": + return + IfcStore.edited_objs.add(obj) + + class AddWall(bpy.types.Operator): bl_idname = "bim.add_wall" bl_label = "Add Wall" @@ -686,7 +708,11 @@ class DumbWallGenerator: return obj -def generate_axis(usecase_path, ifc_file, **settings): +def ensure_solid(usecase_path, ifc_file, settings): + settings["ifc_representation_class"] = "IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef" + + +def generate_axis(usecase_path, ifc_file, settings): axis_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Axis", "GRAPH_VIEW") if not axis_context: return @@ -721,7 +747,7 @@ def generate_axis(usecase_path, ifc_file, **settings): bpy.data.meshes.remove(mesh) -def calculate_quantities(usecase_path, ifc_file, **settings): +def calculate_quantities(usecase_path, ifc_file, settings): unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file) obj = settings["blender_object"] product = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id) @@ -763,7 +789,7 @@ def calculate_quantities(usecase_path, ifc_file, **settings): class DumbWallPlaner: - def regenerate_from_layer(self, usecase_path, ifc_file, **settings): + def regenerate_from_layer(self, usecase_path, ifc_file, settings): self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file) layer = settings["layer"] thickness = settings["attributes"].get("LayerThickness") @@ -788,7 +814,7 @@ class DumbWallPlaner: for element in rel.RelatedObjects: self.change_thickness(element, delta_thickness) - def regenerate_from_type(self, usecase_path, ifc_file, **settings): + def regenerate_from_type(self, usecase_path, ifc_file, settings): self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file) new_material = ifcopenshell.util.element.get_material(settings["relating_type"]) if not new_material.is_a("IfcMaterialLayerSet"): diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index 645dd880db..92d04d14ee 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -15,12 +15,12 @@ def run(usecase_path, ifc_file=None, should_run_listeners=True, **settings): if should_run_listeners: for listener in pre_listeners.get(".".join([ifc_key, usecase_path]), []): - listener(usecase_path, ifc_file, **settings) + listener(usecase_path, ifc_file, settings) if None in registered_ifcs and ifc_key != registered_ifcs[None]: global_key = registered_ifcs[None] for listener in pre_listeners.get(".".join([global_key, usecase_path]), []): - listener(usecase_path, ifc_file, **settings) + listener(usecase_path, ifc_file, settings) def serialise_entity_instance(entity): return {"cast_type": "entity_instance", "value": entity.id(), "Name": getattr(entity, "Name", None)} @@ -57,12 +57,12 @@ def run(usecase_path, ifc_file=None, should_run_listeners=True, **settings): if should_run_listeners: for listener in post_listeners.get(".".join([ifc_key, usecase_path]), []): - listener(usecase_path, ifc_file, **settings) + listener(usecase_path, ifc_file, settings) if None in registered_ifcs and ifc_key != registered_ifcs[None]: global_key = registered_ifcs[None] for listener in post_listeners.get(".".join([global_key, usecase_path]), []): - listener(usecase_path, ifc_file, **settings) + listener(usecase_path, ifc_file, settings) return result diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py index 069498a4c1..c5986af4aa 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py @@ -29,7 +29,7 @@ class Usecase: # IfcExtrudedAreaSolid/IfcRectangleProfileDef # IfcExtrudedAreaSolid/IfcCircleProfileDef # IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef - # IfcExtrudedAreaSolid/IfcArbitraryProfileDef + # IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids "ifc_representation_class": None, # Whether to cast a mesh into a particular class } self.ifc_vertices = []