diff --git a/scripts/model.py b/scripts/model.py new file mode 100644 index 0000000..200a342 --- /dev/null +++ b/scripts/model.py @@ -0,0 +1,7 @@ +from compas_ifc.entities.buildingelements import BuildingModel + +model = BuildingModel(filepath="data/Duplex_A_20110907.ifc") +# print(model.tree.get_hierarchy_string(max_depth=10)) + +model.show() + diff --git a/src/compas_ifc/brep/__init__.py b/src/compas_ifc/brep/__init__.py index 6d131ea..53956c2 100644 --- a/src/compas_ifc/brep/__init__.py +++ b/src/compas_ifc/brep/__init__.py @@ -7,11 +7,14 @@ from compas.plugins import plugin from compas.scene import register - +from .buildingelementobject import BuildingElementObject +from compas_ifc.entities.buildingelements import BuildingElement @plugin(category="factories", requires=["compas_viewer"], trylast=True) def register_scene_objects(): register(TessellatedBrep, TessellatedBrepObject, context="Viewer") + register(BuildingElement, BuildingElementObject, context="Viewer") + try: from compas_occ.brep import OCCBrep diff --git a/src/compas_ifc/brep/buildingelementobject.py b/src/compas_ifc/brep/buildingelementobject.py new file mode 100644 index 0000000..e140509 --- /dev/null +++ b/src/compas_ifc/brep/buildingelementobject.py @@ -0,0 +1,34 @@ +from .tessellatedbrep import TessellatedBrep +from compas_model.viewer import ElementObject +from compas_model.elements import Group +from .tessellatedbrepobject import TessellatedBrepObject + + +class BuildingElementObject(ElementObject): + + def __init__(self, **kwargs): + super().__init__(**kwargs) + if self.element.ifc_type == "IfcSpace": + self.opacity = 0 + + def create_visualization_object(self, **kwargs): + if isinstance(self.element, Group): + self.visualization_object = None + elif isinstance(self.element.geometry, TessellatedBrep): + brep_kwargs = kwargs.copy() + brep_kwargs["item"] = self.element.geometry + self.visualization_object = TessellatedBrepObject(**brep_kwargs, **self.element.ifc_entity.style) + else: + self.visualization_object = None + + + global_transformation = self.element.transformation + + if self.element.parent: + parent_global_transformation = self.element.parent.transformation + local_transformation = global_transformation * parent_global_transformation.inverse() + else: + local_transformation = global_transformation + + self.transformation = local_transformation + diff --git a/src/compas_ifc/entities/buildingelements.py b/src/compas_ifc/entities/buildingelements.py new file mode 100644 index 0000000..49d1d1d --- /dev/null +++ b/src/compas_ifc/entities/buildingelements.py @@ -0,0 +1,86 @@ +from compas_model.models import Model +from compas_model.elements import Element + + +class BuildingModel(Model): + # This class will maintain and update a IFC file underneath. + + def __init__(self, filepath=None, **kwargs): + + from compas_ifc.file import IFCFile + + # The model will start an empty IFC file if not provided. + super().__init__(**kwargs) + self.file = IFCFile(None, filepath=filepath) + self.load_hierarchy() + + def load_hierarchy(self): + + self.project = self.file.get_entities_by_type("IfcProject")[0] + + def load_recursively(entity, parent=None): + for child in entity.children: + name = f"{child.Name}[{child.is_a()}]" + element = BuildingElement(ifc_entity=child, name=name, model=self) + self.add_element(element, parent=parent) + load_recursively(child, element) + + load_recursively(self.project) + + def show(self): + from compas_viewer import Viewer + from compas_viewer.components import Treeform + + viewer = Viewer() + viewer.ui.sidebar.show_objectsetting = False + + viewer.scene.add(self) + + treeform = Treeform() + viewer.ui.sidebar.widget.addWidget(treeform) + + def update_treeform(form, obj): + treeform.update_from_dict({"Attributes": obj.element.ifc_attributes, "PSets": obj.element.ifc_psets}) + + viewer.ui.sidebar.sceneform.callback = update_treeform + + viewer.show() + + +class BuildingElement(Element): + def __init__(self, ifc_entity=None, model=None, **kwargs): + super().__init__(**kwargs) + + self.model = model + self.ifc_entity = ifc_entity # Read-only, underlying IFC entity. + + # self.material = None # There needs to be some mapping mechanism between the material class and Qtos in IFC. + + # links to parents + # self.storey = None + # self.building = None + + @property + def ifc_attributes(self): + return self.ifc_entity.attributes + + @property + def ifc_psets(self): + return self.ifc_entity.property_sets + + @property + def ifc_type(self): + return self.ifc_entity.is_a() + + @property + def geometry(self): + return self.ifc_entity.geometry + + @property + def transformation(self): + # TODO: this is not 100% correct + return self.ifc_entity.frame.to_transformation() + + + +# TODO: Transparent way to work with grid and storeys etc. diff --git a/src/compas_ifc/model.py b/src/compas_ifc/model.py index 42c0120..817accb 100644 --- a/src/compas_ifc/model.py +++ b/src/compas_ifc/model.py @@ -287,7 +287,7 @@ def parse_entity(entity, parent=None): obj = viewer.scene.add(entity.geometry, name=name, parent=parent, hide_coplanaredges=True, **entity.style) obj.transformation = transformation else: - obj = viewer.scene.add([], name=name, parent=parent) + obj = viewer.scene.add_group(name=name, parent=parent) obj.attributes["entity"] = entity