Dumb walls and slabs now are always exported as parametric solids instead of meshes.

This commit is contained in:
Dion Moult
2021-06-04 17:30:37 +10:00
parent 153b6ee24d
commit 5c1d26ac91
6 changed files with 47 additions and 12 deletions
@@ -10,11 +10,14 @@ from bpy.app.handlers import persistent
def load_post(*args): def load_post(*args):
ifcopenshell.api.add_post_listener("geometry.add_representation", None, product.generate_box) 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.generate_axis)
ifcopenshell.api.add_post_listener("geometry.add_representation", None, wall.calculate_quantities) 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("material.edit_layer", None, wall.DumbWallPlaner().regenerate_from_layer)
ifcopenshell.api.add_pre_listener("type.assign_type", None, wall.DumbWallPlaner().regenerate_from_type) ifcopenshell.api.add_pre_listener("type.assign_type", None, wall.DumbWallPlaner().regenerate_from_type)
IfcStore.add_element_listener(slab.element_listener) 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("material.edit_layer", None, slab.DumbSlabPlaner().regenerate_from_layer)
ifcopenshell.api.add_pre_listener("type.assign_type", None, slab.DumbSlabPlaner().regenerate_from_type) ifcopenshell.api.add_pre_listener("type.assign_type", None, slab.DumbSlabPlaner().regenerate_from_type)
@@ -65,7 +65,7 @@ class AddTypeInstance(bpy.types.Operator):
return {"FINISHED"} 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") box_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Box", "MODEL_VIEW")
if not box_context: if not box_context:
return return
@@ -30,6 +30,7 @@ def mode_callback(obj, data):
parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric") parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric")
if not parametric or parametric["Engine"] != "BlenderBIM.DumbSlab": if not parametric or parametric["Engine"] != "BlenderBIM.DumbSlab":
return return
IfcStore.edited_objs.add(obj)
modifier = [m for m in obj.modifiers if m.type == "SOLIDIFY"] modifier = [m for m in obj.modifiers if m.type == "SOLIDIFY"]
if modifier: if modifier:
return return
@@ -52,6 +53,11 @@ def mode_callback(obj, data):
modifier.thickness = depth 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: class DumbSlabGenerator:
def __init__(self, relating_type): def __init__(self, relating_type):
self.relating_type = relating_type self.relating_type = relating_type
@@ -117,7 +123,7 @@ class DumbSlabGenerator:
class DumbSlabPlaner: 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) self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
layer = settings["layer"] layer = settings["layer"]
thickness = settings["attributes"].get("LayerThickness") thickness = settings["attributes"].get("LayerThickness")
@@ -142,7 +148,7 @@ class DumbSlabPlaner:
for element in rel.RelatedObjects: for element in rel.RelatedObjects:
self.change_thickness(element, delta_thickness) 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) self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
new_material = ifcopenshell.util.element.get_material(settings["relating_type"]) new_material = ifcopenshell.util.element.get_material(settings["relating_type"])
if not new_material.is_a("IfcMaterialLayerSet"): if not new_material.is_a("IfcMaterialLayerSet"):
@@ -7,6 +7,7 @@ import ifcopenshell.util.unit
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.representation import ifcopenshell.util.representation
import mathutils.geometry import mathutils.geometry
import blenderbim.bim.handler
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.pset.data import Data as PsetData from ifcopenshell.api.pset.data import Data as PsetData
from ifcopenshell.api.material.data import Data as MaterialData from ifcopenshell.api.material.data import Data as MaterialData
@@ -14,6 +15,27 @@ from math import pi, degrees
from mathutils import Vector, Matrix 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): class AddWall(bpy.types.Operator):
bl_idname = "bim.add_wall" bl_idname = "bim.add_wall"
bl_label = "Add Wall" bl_label = "Add Wall"
@@ -686,7 +708,11 @@ class DumbWallGenerator:
return obj 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") axis_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Axis", "GRAPH_VIEW")
if not axis_context: if not axis_context:
return return
@@ -721,7 +747,7 @@ def generate_axis(usecase_path, ifc_file, **settings):
bpy.data.meshes.remove(mesh) 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) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
obj = settings["blender_object"] obj = settings["blender_object"]
product = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id) product = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
@@ -763,7 +789,7 @@ def calculate_quantities(usecase_path, ifc_file, **settings):
class DumbWallPlaner: 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) self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
layer = settings["layer"] layer = settings["layer"]
thickness = settings["attributes"].get("LayerThickness") thickness = settings["attributes"].get("LayerThickness")
@@ -788,7 +814,7 @@ class DumbWallPlaner:
for element in rel.RelatedObjects: for element in rel.RelatedObjects:
self.change_thickness(element, delta_thickness) 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) self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
new_material = ifcopenshell.util.element.get_material(settings["relating_type"]) new_material = ifcopenshell.util.element.get_material(settings["relating_type"])
if not new_material.is_a("IfcMaterialLayerSet"): if not new_material.is_a("IfcMaterialLayerSet"):
@@ -15,12 +15,12 @@ def run(usecase_path, ifc_file=None, should_run_listeners=True, **settings):
if should_run_listeners: if should_run_listeners:
for listener in pre_listeners.get(".".join([ifc_key, usecase_path]), []): 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]: if None in registered_ifcs and ifc_key != registered_ifcs[None]:
global_key = registered_ifcs[None] global_key = registered_ifcs[None]
for listener in pre_listeners.get(".".join([global_key, usecase_path]), []): 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): def serialise_entity_instance(entity):
return {"cast_type": "entity_instance", "value": entity.id(), "Name": getattr(entity, "Name", None)} 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: if should_run_listeners:
for listener in post_listeners.get(".".join([ifc_key, usecase_path]), []): 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]: if None in registered_ifcs and ifc_key != registered_ifcs[None]:
global_key = registered_ifcs[None] global_key = registered_ifcs[None]
for listener in post_listeners.get(".".join([global_key, usecase_path]), []): 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 return result
@@ -29,7 +29,7 @@ class Usecase:
# IfcExtrudedAreaSolid/IfcRectangleProfileDef # IfcExtrudedAreaSolid/IfcRectangleProfileDef
# IfcExtrudedAreaSolid/IfcCircleProfileDef # IfcExtrudedAreaSolid/IfcCircleProfileDef
# IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef # IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef
# IfcExtrudedAreaSolid/IfcArbitraryProfileDef # IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids
"ifc_representation_class": None, # Whether to cast a mesh into a particular class "ifc_representation_class": None, # Whether to cast a mesh into a particular class
} }
self.ifc_vertices = [] self.ifc_vertices = []