You can now parametrically create profile / layer based objects of any type, no longer limited to walls, slabs, etc.

This commit is contained in:
Dion Moult
2021-08-10 18:56:15 +10:00
parent ab38e6bf7c
commit cb95c93424
6 changed files with 52 additions and 43 deletions
@@ -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
@@ -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)
@@ -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)
@@ -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")
+8
View File
@@ -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)
@@ -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"):