From cb95c934242357251db8d713920fc274b2ea6502 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 10 Aug 2021 18:56:15 +1000 Subject: [PATCH] You can now parametrically create profile / layer based objects of any type, no longer limited to walls, slabs, etc. --- .../blenderbim/bim/module/model/product.py | 21 ++++++++------- .../blenderbim/bim/module/model/profile.py | 26 +++++++------------ .../blenderbim/bim/module/model/slab.py | 12 +++++---- .../blenderbim/bim/module/model/wall.py | 13 +++++++--- src/blenderbim/generate_demo_library.py | 8 ++++++ .../ifcopenshell/util/type.py | 15 ++++++----- 6 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 7374d66e67..0c0fb24f47 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -23,21 +23,22 @@ class AddTypeInstance(bpy.types.Operator): def _execute(self, context): tprops = context.scene.BIMTypeProperties ifc_class = self.ifc_class or tprops.ifc_class - relating_type = self.relating_type or tprops.relating_type - if not ifc_class or not relating_type: + relating_type_id = self.relating_type or tprops.relating_type + if not ifc_class or not relating_type_id: return {"FINISHED"} self.file = IfcStore.get_file() instance_class = ifcopenshell.util.type.get_applicable_entities(ifc_class, self.file.schema)[0] - if ifc_class == "IfcWallType": - obj = wall.DumbWallGenerator(self.file.by_id(int(relating_type))).generate() + relating_type = self.file.by_id(int(relating_type_id)) + material = ifcopenshell.util.element.get_material(relating_type) + if material.is_a("IfcMaterialProfileSet"): + obj = profile.DumbProfileGenerator(relating_type).generate() if obj: return {"FINISHED"} - elif ifc_class == "IfcSlabType": - obj = slab.DumbSlabGenerator(self.file.by_id(int(relating_type))).generate() - if obj: - return {"FINISHED"} - elif ifc_class in ["IfcColumnType", "IfcBeamType", "IfcMemberType"]: - obj = profile.DumbProfileGenerator(self.file.by_id(int(relating_type))).generate() + elif material.is_a("IfcMaterialLayerSet"): + if ifc_class in ["IfcSlabType", "IfcRoofType", "IfcRampType", "IfcPlateType"]: + obj = slab.DumbSlabGenerator(relating_type).generate() + else: + obj = wall.DumbWallGenerator(relating_type).generate() if obj: return {"FINISHED"} # A cube diff --git a/src/blenderbim/blenderbim/bim/module/model/profile.py b/src/blenderbim/blenderbim/bim/module/model/profile.py index 6de8a6a0c1..493d3db501 100644 --- a/src/blenderbim/blenderbim/bim/module/model/profile.py +++ b/src/blenderbim/blenderbim/bim/module/model/profile.py @@ -137,25 +137,17 @@ class DumbProfileGenerator: if self.collection_obj and self.collection_obj.BIMObjectProperties.ifc_definition_id: obj.location[2] = self.collection_obj.location[2] self.collection.objects.link(obj) - if self.relating_type.is_a("IfcColumnType"): - obj.name = "Column" - bpy.ops.bim.assign_class( - obj=obj.name, ifc_class="IfcColumn", predefined_type="COLUMN", should_add_representation=False - ) - elif self.relating_type.is_a("IfcBeamType"): - obj.name = "Beam" + + ifc_classes = ifcopenshell.util.type.get_applicable_entities(self.relating_type.is_a(), self.file.schema) + # Standard cases are deprecated, so let's cull them + ifc_class = [c for c in ifc_classes if "StandardCase" not in c][0] + obj.name = ifc_class[3:] + bpy.ops.bim.assign_class(obj=obj.name, ifc_class=ifc_class, should_add_representation=False) + + if self.relating_type.is_a() in ["IfcBeamType", "IfcMemberType"]: obj.rotation_euler[0] = math.pi / 2 obj.rotation_euler[2] = math.pi / 2 - bpy.ops.bim.assign_class( - obj=obj.name, ifc_class="IfcBeam", predefined_type="BEAM", should_add_representation=False - ) - elif self.relating_type.is_a("IfcMemberType"): - obj.name = "Member" - obj.rotation_euler[0] = math.pi / 2 - obj.rotation_euler[2] = math.pi / 2 - bpy.ops.bim.assign_class( - obj=obj.name, ifc_class="IfcMember", predefined_type="MEMBER", should_add_representation=False - ) + element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id) bpy.ops.bim.assign_type(relating_type=self.relating_type.id(), related_object=obj.name) profile_set_usage = ifcopenshell.util.element.get_material(element) diff --git a/src/blenderbim/blenderbim/bim/module/model/slab.py b/src/blenderbim/blenderbim/bim/module/model/slab.py index d716f0c0a8..b16b0b847b 100644 --- a/src/blenderbim/blenderbim/bim/module/model/slab.py +++ b/src/blenderbim/blenderbim/bim/module/model/slab.py @@ -211,8 +211,6 @@ class AddSlabOpening(bpy.types.Operator): if not slab_obj.BIMObjectProperties.ifc_definition_id: return {"FINISHED"} slab = IfcStore.get_file().by_id(slab_obj.BIMObjectProperties.ifc_definition_id) - if not slab.is_a("IfcSlab"): - return {"FINISHED"} local_location = slab_obj.matrix_world.inverted() @ context.scene.cursor.location raycast = slab_obj.closest_point_on_mesh(local_location, distance=0.01) if not raycast[0]: @@ -280,7 +278,12 @@ class DumbSlabGenerator: modifier.use_even_offset = True modifier.offset = 1 modifier.thickness = self.depth - obj.name = "Slab" + + ifc_classes = ifcopenshell.util.type.get_applicable_entities(self.relating_type.is_a(), self.file.schema) + # Standard cases are deprecated, so let's cull them + ifc_class = [c for c in ifc_classes if "StandardCase" not in c][0] + + obj.name = ifc_class[3:] obj.location = self.location if self.collection_obj and self.collection_obj.BIMObjectProperties.ifc_definition_id: obj.location[2] = self.collection_obj.location[2] - self.depth @@ -289,8 +292,7 @@ class DumbSlabGenerator: self.collection.objects.link(obj) bpy.ops.bim.assign_class( obj=obj.name, - ifc_class="IfcSlab", - predefined_type="FLOOR", + ifc_class=ifc_class, ifc_representation_class="IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids", ) bpy.ops.bim.assign_type(relating_type=self.relating_type.id(), related_object=obj.name) diff --git a/src/blenderbim/blenderbim/bim/module/model/wall.py b/src/blenderbim/blenderbim/bim/module/model/wall.py index c132a72339..340ecf7612 100644 --- a/src/blenderbim/blenderbim/bim/module/model/wall.py +++ b/src/blenderbim/blenderbim/bim/module/model/wall.py @@ -57,8 +57,6 @@ class AddWallOpening(bpy.types.Operator): if not wall_obj.BIMObjectProperties.ifc_definition_id: return {"FINISHED"} wall = IfcStore.get_file().by_id(wall_obj.BIMObjectProperties.ifc_definition_id) - if not wall.is_a("IfcWall"): - return {"FINISHED"} local_location = wall_obj.matrix_world.inverted() @ context.scene.cursor.location raycast = wall_obj.closest_point_on_mesh(local_location, distance=0.01) if not raycast[0]: @@ -773,17 +771,24 @@ class DumbWallGenerator: ] mesh = bpy.data.meshes.new(name="Wall") mesh.from_pydata(verts, [], faces) - obj = bpy.data.objects.new("Wall", mesh) + + ifc_classes = ifcopenshell.util.type.get_applicable_entities(self.relating_type.is_a(), self.file.schema) + # Standard cases are deprecated, so let's cull them + ifc_class = [c for c in ifc_classes if "StandardCase" not in c][0] + + obj = bpy.data.objects.new(ifc_class[3:], mesh) obj.location = self.location obj.rotation_euler[2] = self.rotation if self.collection_obj and self.collection_obj.BIMObjectProperties.ifc_definition_id: obj.location[2] = self.collection_obj.location[2] self.collection.objects.link(obj) + bpy.ops.bim.assign_class( obj=obj.name, - ifc_class="IfcWall", + ifc_class=ifc_class, ifc_representation_class="IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef", ) + bpy.ops.bim.assign_type(relating_type=self.relating_type.id(), related_object=obj.name) element = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id) pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric") diff --git a/src/blenderbim/generate_demo_library.py b/src/blenderbim/generate_demo_library.py index 2b17a510b6..c76f4afcc4 100644 --- a/src/blenderbim/generate_demo_library.py +++ b/src/blenderbim/generate_demo_library.py @@ -17,6 +17,14 @@ class LibraryGenerator: self.create_layer_type("IfcWallType", "DEMO200", 0.2) self.create_layer_type("IfcWallType", "DEMO300", 0.3) + self.create_layer_type("IfcCoveringType", "DEMO20", 0.02) + self.create_layer_type("IfcRampType", "DEMO200", 0.2) + + profile = self.file.create_entity( + "IfcCircleProfileDef", ProfileType="AREA", Radius=0.3 + ) + self.create_profile_type("IfcPileType", "DEMO1", profile) + self.create_layer_type("IfcSlabType", "DEMO150", 0.2) self.create_layer_type("IfcSlabType", "DEMO250", 0.3) diff --git a/src/ifcopenshell-python/ifcopenshell/util/type.py b/src/ifcopenshell-python/ifcopenshell/util/type.py index 6a6e57703c..7999afc53a 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/type.py +++ b/src/ifcopenshell-python/ifcopenshell/util/type.py @@ -12,13 +12,14 @@ with open(os.path.join(cwd, "entity_to_type_map_2x3.json")) as f: with open(os.path.join(cwd, "entity_to_type_map_4.json")) as f: entity_to_type_map["IFC4"] = json.load(f) -type_to_entity_map["IFC2X3"] = { - value: [key] for key in entity_to_type_map["IFC2X3"] for value in entity_to_type_map["IFC2X3"][key] -} - -type_to_entity_map["IFC4"] = { - value: [key] for key in entity_to_type_map["IFC4"] for value in entity_to_type_map["IFC4"][key] -} +for schema in ["IFC2X3", "IFC4"]: + type_to_entity_map[schema] = {} + for element, element_types in entity_to_type_map[schema].items(): + for element_type in element_types: + type_to_entity_map[schema].setdefault(element_type, []).append(element) + # TODO: this method just fails for IFC2X3 because 2X3 type mapping is just so broken. + # Uncomment the following line to see how bad it is. + # print(type_to_entity_map[schema]) def get_applicable_types(ifc_class, schema="IFC4"):