From d5a83ede163bcff0cc463b5d94a031e3fd5334a9 Mon Sep 17 00:00:00 2001 From: Li Date: Mon, 5 Feb 2024 17:55:24 +0100 Subject: [PATCH 1/8] TessellatedBrep without OCC --- src/compas_ifc/__init__.py | 4 + src/compas_ifc/entities/product.py | 15 ++- src/compas_ifc/representation.py | 113 ++++++++++++------ src/compas_ifc/resources/__init__.py | 1 + src/compas_ifc/resources/geometricmodel.py | 8 ++ src/compas_ifc/shapes/__init__.py | 12 ++ src/compas_ifc/shapes/tessellatedbrep.py | 23 ++++ .../shapes/tessellatedbrepobject.py | 24 ++++ 8 files changed, 163 insertions(+), 37 deletions(-) create mode 100644 src/compas_ifc/shapes/__init__.py create mode 100644 src/compas_ifc/shapes/tessellatedbrep.py create mode 100644 src/compas_ifc/shapes/tessellatedbrepobject.py diff --git a/src/compas_ifc/__init__.py b/src/compas_ifc/__init__.py index fc71fe43..83902fa6 100644 --- a/src/compas_ifc/__init__.py +++ b/src/compas_ifc/__init__.py @@ -40,3 +40,7 @@ __all__ = ["HOME", "DATA", "DOCS", "TEMP"] + +__all_plugins__ = [ + "compas_ifc.shapes", +] diff --git a/src/compas_ifc/entities/product.py b/src/compas_ifc/entities/product.py index 1ac87eb8..c4a7478c 100644 --- a/src/compas_ifc/entities/product.py +++ b/src/compas_ifc/entities/product.py @@ -27,6 +27,7 @@ def __init__(self, entity, model) -> None: self._body = None self._opening = None self._body_with_opening = None + self._transformation = None def classifications(self) -> List[Dict[str, str]]: """ @@ -141,9 +142,21 @@ def body_with_opening(self): from compas_ifc.representation import entity_body_with_opening_geometry if not self._body_with_opening: - self._body_with_opening = entity_body_with_opening_geometry(self, self.body, self.opening) + self._body_with_opening = entity_body_with_opening_geometry(self) return self._body_with_opening @body_with_opening.setter def body_with_opening(self, value): self._body_with_opening = value + + @property + def transformation(self): + from compas_ifc.representation import entity_transformation + + if not self._transformation: + self._transformation = entity_transformation(self) + return self._transformation + + @transformation.setter + def transformation(self, value): + self._transformation = value diff --git a/src/compas_ifc/representation.py b/src/compas_ifc/representation.py index 7e5dd884..c59c47c9 100644 --- a/src/compas_ifc/representation.py +++ b/src/compas_ifc/representation.py @@ -49,12 +49,14 @@ from compas.geometry import Scale from compas.geometry import Transformation from compas_ifc.entities.entity import Entity +from compas_ifc.shapes import TessellatedBrep from compas_ifc.resources import IfcAxis2Placement3D_to_frame from compas_ifc.resources import IfcBoundingBox_to_box from compas_ifc.resources import IfcCartesianTransformationOperator3D_to_frame from compas_ifc.resources import IfcIndexedPolyCurve_to_lines from compas_ifc.resources import IfcLocalPlacement_to_transformation from compas_ifc.resources import IfcShape_to_brep +from compas_ifc.resources import IfcShape_to_tessellatedbrep # this does not belong here @@ -76,7 +78,24 @@ def IfcMappedItem_to_transformation(item) -> Transformation: return reduce(mul, matrices[::-1]) -def entity_body_geometry(entity: Entity, context="Model"): +def entity_transformation(entity: Entity): + """ + Construct the transformation of an entity. + """ + return _entity_transformation(entity._entity, scale=entity.model.project.length_scale) + + +def _entity_transformation(_entity, scale=1.0): + + if _entity.ObjectPlacement: + scaled_placement = IfcLocalPlacement_to_transformation(_entity.ObjectPlacement, scale=scale) + else: + scaled_placement = Scale.from_factors([scale, scale, scale]) + + return scaled_placement + + +def entity_body_geometry(entity: Entity, context="Model", use_occ=True, apply_transformation=True): """ Construct the body geometry representations of an entity. @@ -97,44 +116,53 @@ def entity_body_geometry(entity: Entity, context="Model"): if not representation: return bodies - scale = entity.model.project.length_scale - if entity._entity.ObjectPlacement: - scaled_placement = IfcLocalPlacement_to_transformation(entity._entity.ObjectPlacement, scale=scale) - else: - scaled_placement = Transformation() if representation.ContextOfItems.ContextType == context: for item in representation.Items: - brep = IfcShape_to_brep(item) - brep.transform(scaled_placement) - bodies.append(brep) + if use_occ: + brep = IfcShape_to_brep(item) + bodies.append(brep) + else: + tessellatedbrep = IfcShape_to_tessellatedbrep(item) + bodies.append(tessellatedbrep) + + if apply_transformation: + transformation = entity_transformation(entity) + for body in bodies: + body.transform(transformation) return bodies -def entity_opening_geometry(entity: Entity): +def entity_opening_geometry(entity: Entity, use_occ=True, apply_transformation=True): """ Construct the opening geometry representations of an entity. """ voids = [] if hasattr(entity._entity, "HasOpenings"): - scale = entity.model.project.length_scale for opening in entity._entity.HasOpenings: element = opening.RelatedOpeningElement - if element.ObjectPlacement: - scaled_placement = IfcLocalPlacement_to_transformation(element.ObjectPlacement, scale=scale) - else: - scaled_placement = Transformation() + + if apply_transformation: + transformation = _entity_transformation(element, entity.model.project.length_scale) + for representation in element.Representation.Representations: for item in representation.Items: - brep = IfcShape_to_brep(item) - brep.transform(scaled_placement) - voids.append(brep) + if use_occ: + brep = IfcShape_to_brep(item) + if apply_transformation: + brep.transform(transformation) + voids.append(brep) + else: + tessellatedbrep = IfcShape_to_tessellatedbrep(item) + if apply_transformation: + tessellatedbrep.transform(transformation) + voids.append(tessellatedbrep) return voids -def entity_body_with_opening_geometry(entity: Entity = None, bodies=None, voids=None, context="Model"): +def entity_body_with_opening_geometry(entity: Entity = None, bodies=None, voids=None, context="Model", use_occ=False): """ Construct the body geometry representations of an entity. @@ -143,28 +171,41 @@ def entity_body_with_opening_geometry(entity: Entity = None, bodies=None, voids= .. [1] :ifc:`body-geometry` """ - bodies = bodies or entity_body_geometry(entity, context=context) - voids = voids or entity_opening_geometry(entity) + bodies = bodies or entity_body_geometry(entity, context=context, use_occ=use_occ, apply_transformation=True) + voids = voids or entity_opening_geometry(entity, use_occ=use_occ, apply_transformation=True) if not voids: return bodies - compound = TopoDS_Compound() - builder = BRep_Builder() - builder.MakeCompound(compound) - for brep in voids: - builder.Add(compound, brep.occ_shape) - B = OCCBrep.from_shape(compound) - - shapes = [] - for A in bodies: - try: - C: OCCBrep = A - B - C.make_solid() + if use_occ: + print("Using OCC for boolean operations.") + compound = TopoDS_Compound() + builder = BRep_Builder() + builder.MakeCompound(compound) + for brep in voids: + builder.Add(compound, brep.occ_shape) + B = OCCBrep.from_shape(compound) + + shapes = [] + for A in bodies: + try: + C: OCCBrep = A - B + C.make_solid() + shapes.append(C) + except Exception: + shapes.append(A) + print("Warning: Failed to subtract voids from body.") + else: + print("Using CGAL for boolean operations.") + from compas_cgal.booleans import boolean_difference + + shapes = [] + for A in bodies: + C = A + for B in voids: + vertices, faces = boolean_difference(C.to_vertices_and_faces(), B.to_vertices_and_faces()) + C = TessellatedBrep(vertices=vertices, faces=faces) shapes.append(C) - except Exception: - shapes.append(A) - print("Warning: Failed to subtract voids from body.") return shapes diff --git a/src/compas_ifc/resources/__init__.py b/src/compas_ifc/resources/__init__.py index 3ec137c4..bb74bd77 100644 --- a/src/compas_ifc/resources/__init__.py +++ b/src/compas_ifc/resources/__init__.py @@ -110,6 +110,7 @@ from .geometricmodel import IfcTessellatedFaceSet_to_brep # noqa: F401 from .geometricmodel import IfcTriangulatedFaceSet_to_brep # noqa: F401 from .geometricmodel import IfcShape_to_brep # noqa: F401 +from .geometricmodel import IfcShape_to_tessellatedbrep # noqa: F401 # from .geometricmodel import IfcBoxedHalfSpace # from .geometricmodel import IfcCartesianPointList diff --git a/src/compas_ifc/resources/geometricmodel.py b/src/compas_ifc/resources/geometricmodel.py index 845d0a2f..e725b76d 100644 --- a/src/compas_ifc/resources/geometricmodel.py +++ b/src/compas_ifc/resources/geometricmodel.py @@ -5,9 +5,11 @@ from compas.geometry import Line from compas.geometry import Point from compas.geometry import Transformation +from compas.datastructures import Mesh from compas_ifc.resources.geometry import IfcAxis2Placement3D_to_frame from compas_ifc.resources.geometry import IfcDirection_to_vector from compas_ifc.resources.geometry import IfcProfileDef_to_curve +from compas_ifc.shapes import TessellatedBrep from ifcopenshell import geom @@ -239,6 +241,12 @@ def IfcShape_to_brep(ifc_shape): return brep +def IfcShape_to_tessellatedbrep(ifc_shape): + settings = geom.settings() + shape = geom.create_shape(settings, ifc_shape) + return TessellatedBrep(vertices=shape.verts, edges=shape.edges, faces=shape.faces) + + # # IfcTessellationItem_to_mesh # def tessellation_to_mesh(item) -> Mesh: # """ diff --git a/src/compas_ifc/shapes/__init__.py b/src/compas_ifc/shapes/__init__.py new file mode 100644 index 00000000..d292d295 --- /dev/null +++ b/src/compas_ifc/shapes/__init__.py @@ -0,0 +1,12 @@ +from .tessellatedbrep import TessellatedBrep +from .tessellatedbrepobject import TessellatedBrepObject +from compas.plugins import plugin +from compas.scene import register + + +@plugin(category="factories", requires=["compas_viewer"]) +def register_scene_objects(): + register(TessellatedBrep, TessellatedBrepObject, context="Viewer") + + +__all__ = ["TessellatedBrep", "TessellatedBrepObject"] \ No newline at end of file diff --git a/src/compas_ifc/shapes/tessellatedbrep.py b/src/compas_ifc/shapes/tessellatedbrep.py new file mode 100644 index 00000000..0aac8648 --- /dev/null +++ b/src/compas_ifc/shapes/tessellatedbrep.py @@ -0,0 +1,23 @@ +from compas.geometry import Geometry +from compas.geometry import transform_points_numpy +import numpy as np + + +class TessellatedBrep(Geometry): + def __init__(self, vertices=None, edges=None, faces=None, **kwargs): + super().__init__(**kwargs) + if vertices is None: + vertices = [] + if edges is None: + edges = [] + if faces is None: + faces = [] + self.vertices = np.array(vertices).reshape(-1, 3) + self.edges = np.array(edges).reshape(-1, 2) + self.faces = np.array(faces).reshape(-1, 3) + + def transform(self, transformation): + self.vertices = transform_points_numpy(self.vertices, transformation) + + def to_vertices_and_faces(self): + return self.vertices, self.faces \ No newline at end of file diff --git a/src/compas_ifc/shapes/tessellatedbrepobject.py b/src/compas_ifc/shapes/tessellatedbrepobject.py new file mode 100644 index 00000000..aec44cd9 --- /dev/null +++ b/src/compas_ifc/shapes/tessellatedbrepobject.py @@ -0,0 +1,24 @@ +from compas_viewer.scene import ViewerSceneObject +from .tessellatedbrep import TessellatedBrep +import numpy as np + +class TessellatedBrepObject(ViewerSceneObject): + def __init__(self, tessellatedbrep: TessellatedBrep, **kwargs): + super().__init__(item=tessellatedbrep, **kwargs) + self.tessellatedbrep = tessellatedbrep + + def _read_lines_data(self): + positions = self.tessellatedbrep.vertices + elements = self.tessellatedbrep.edges + colors = [] + default_color = self.linescolor["_default"] + colors = np.full(shape=(len(elements), 3), fill_value=default_color) + return positions.tolist(), colors.tolist(), elements.tolist() + + def _read_frontfaces_data(self): + positions = self.tessellatedbrep.vertices + elements = self.tessellatedbrep.faces + colors = [] + default_color = self.facescolor["_default"] + colors = np.full(shape=(len(elements), 3), fill_value=default_color) + return positions.tolist(), colors.tolist(), elements.tolist() From e1bccc7952bba7c3845d4f1769f962d20b374979 Mon Sep 17 00:00:00 2001 From: Li Date: Tue, 6 Feb 2024 11:36:44 +0100 Subject: [PATCH 2/8] deal with color --- src/compas_ifc/shapes/tessellatedbrepobject.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/compas_ifc/shapes/tessellatedbrepobject.py b/src/compas_ifc/shapes/tessellatedbrepobject.py index aec44cd9..5a63f56c 100644 --- a/src/compas_ifc/shapes/tessellatedbrepobject.py +++ b/src/compas_ifc/shapes/tessellatedbrepobject.py @@ -3,9 +3,10 @@ import numpy as np class TessellatedBrepObject(ViewerSceneObject): - def __init__(self, tessellatedbrep: TessellatedBrep, **kwargs): + def __init__(self, tessellatedbrep: TessellatedBrep, facecolors=None, **kwargs): super().__init__(item=tessellatedbrep, **kwargs) self.tessellatedbrep = tessellatedbrep + self.facecolors = facecolors def _read_lines_data(self): positions = self.tessellatedbrep.vertices @@ -16,9 +17,6 @@ def _read_lines_data(self): return positions.tolist(), colors.tolist(), elements.tolist() def _read_frontfaces_data(self): - positions = self.tessellatedbrep.vertices - elements = self.tessellatedbrep.faces - colors = [] - default_color = self.facescolor["_default"] - colors = np.full(shape=(len(elements), 3), fill_value=default_color) - return positions.tolist(), colors.tolist(), elements.tolist() + positions = self.tessellatedbrep.vertices[self.tessellatedbrep.faces].reshape(-1, 3) + elements = np.arange(len(positions)*3).reshape(-1, 3) + return positions.tolist(), self.facecolors, elements.tolist() From d043e43c9d8f579afaebe2234549fd7b99ee25e8 Mon Sep 17 00:00:00 2001 From: Li Chen Date: Tue, 6 Feb 2024 18:30:26 +0100 Subject: [PATCH 3/8] iterator --- src/compas_ifc/__init__.py | 2 +- src/compas_ifc/{shapes => brep}/__init__.py | 0 .../{shapes => brep}/tessellatedbrep.py | 0 .../{shapes => brep}/tessellatedbrepobject.py | 0 src/compas_ifc/entities/product.py | 2 +- src/compas_ifc/reader.py | 66 +++++++++++++++++++ src/compas_ifc/representation.py | 2 +- src/compas_ifc/resources/geometricmodel.py | 2 +- 8 files changed, 70 insertions(+), 4 deletions(-) rename src/compas_ifc/{shapes => brep}/__init__.py (100%) rename src/compas_ifc/{shapes => brep}/tessellatedbrep.py (100%) rename src/compas_ifc/{shapes => brep}/tessellatedbrepobject.py (100%) diff --git a/src/compas_ifc/__init__.py b/src/compas_ifc/__init__.py index 83902fa6..2fd33b28 100644 --- a/src/compas_ifc/__init__.py +++ b/src/compas_ifc/__init__.py @@ -42,5 +42,5 @@ __all__ = ["HOME", "DATA", "DOCS", "TEMP"] __all_plugins__ = [ - "compas_ifc.shapes", + "compas_ifc.brep", ] diff --git a/src/compas_ifc/shapes/__init__.py b/src/compas_ifc/brep/__init__.py similarity index 100% rename from src/compas_ifc/shapes/__init__.py rename to src/compas_ifc/brep/__init__.py diff --git a/src/compas_ifc/shapes/tessellatedbrep.py b/src/compas_ifc/brep/tessellatedbrep.py similarity index 100% rename from src/compas_ifc/shapes/tessellatedbrep.py rename to src/compas_ifc/brep/tessellatedbrep.py diff --git a/src/compas_ifc/shapes/tessellatedbrepobject.py b/src/compas_ifc/brep/tessellatedbrepobject.py similarity index 100% rename from src/compas_ifc/shapes/tessellatedbrepobject.py rename to src/compas_ifc/brep/tessellatedbrepobject.py diff --git a/src/compas_ifc/entities/product.py b/src/compas_ifc/entities/product.py index c4a7478c..7176869a 100644 --- a/src/compas_ifc/entities/product.py +++ b/src/compas_ifc/entities/product.py @@ -142,7 +142,7 @@ def body_with_opening(self): from compas_ifc.representation import entity_body_with_opening_geometry if not self._body_with_opening: - self._body_with_opening = entity_body_with_opening_geometry(self) + self._body_with_opening = self.model.reader.get(self._entity.id()) or entity_body_with_opening_geometry(self) return self._body_with_opening @body_with_opening.setter diff --git a/src/compas_ifc/reader.py b/src/compas_ifc/reader.py index 407f5b69..1e467c25 100644 --- a/src/compas_ifc/reader.py +++ b/src/compas_ifc/reader.py @@ -2,6 +2,11 @@ import ifcopenshell import os +import multiprocessing +import numpy as np +from .brep import TessellatedBrep +from compas.geometry import Transformation +import time from compas_ifc.entities.entity import Entity @@ -52,6 +57,7 @@ def __init__(self, model, entity_types: dict = None): self._file = ifcopenshell.file() self._schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self._file.schema) self._entitymap = {} + self._geometrymap = {} def open(self, filepath: str): self.filepath = filepath @@ -59,6 +65,7 @@ def open(self, filepath: str): self._schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self._file.schema) self._entitymap = {} print("Opened file: {}".format(filepath)) + self.load_geometries() def get_entity(self, entity: ifcopenshell.entity_instance): """ @@ -123,3 +130,62 @@ def file_size(self): size_in_mb = file_stats.st_size / (1024 * 1024) size_in_mb = round(size_in_mb, 2) return size_in_mb + + def get_preloaded_geometry(self, entity): + return self._geometrymap.get(entity._entity.id()) + + def load_geometries(self, include=None, exclude=None): + """Load all the geometries of the IFC file using a fast multithreaded iterator.""" + print("Loading geometries...") + settings = ifcopenshell.geom.settings() + iterator = ifcopenshell.geom.iterator(settings, self._file, multiprocessing.cpu_count(), include=include, exclude=exclude) + start = time.time() + if iterator.initialize(): + while True: + shape = iterator.get() + matrix = shape.transformation.matrix.data + faces = shape.geometry.faces + edges = shape.geometry.edges + verts = shape.geometry.verts + brep = TessellatedBrep(vertices=verts, edges=edges, faces=faces) + + + matrix = np.array(matrix).reshape((4, 3)) + matrix = np.hstack([ + matrix, + np.array([[0], [0], [0], [1]]) + ]) + matrix = matrix.transpose() + transformation = Transformation.from_matrix(matrix.tolist()) + + facecolors = [] + opacity=0 + for m_id in shape.geometry.material_ids: + if m_id == -1: + facecolors.append([0.5, 0.5, 0.5]) + facecolors.append([0.5, 0.5, 0.5]) + facecolors.append([0.5, 0.5, 0.5]) + continue + material = shape.geometry.materials[m_id] + color = material.diffuse + facecolors.append(color) + facecolors.append(color) + facecolors.append(color) + opacity += 1 - material.transparency + + opacity /= len(shape.geometry.material_ids) + + if opacity < 1 and opacity > 0.5: + opacity = 0.5 + + self._geometrymap[shape.id] = { + "brep": brep, + "transformation": transformation, + "facecolors": facecolors, + "opacity": opacity + } + + if not iterator.next(): + break + + print("Time to load all geometries ", time.time() - start) \ No newline at end of file diff --git a/src/compas_ifc/representation.py b/src/compas_ifc/representation.py index c59c47c9..4a52552b 100644 --- a/src/compas_ifc/representation.py +++ b/src/compas_ifc/representation.py @@ -49,7 +49,7 @@ from compas.geometry import Scale from compas.geometry import Transformation from compas_ifc.entities.entity import Entity -from compas_ifc.shapes import TessellatedBrep +from compas_ifc.brep import TessellatedBrep from compas_ifc.resources import IfcAxis2Placement3D_to_frame from compas_ifc.resources import IfcBoundingBox_to_box from compas_ifc.resources import IfcCartesianTransformationOperator3D_to_frame diff --git a/src/compas_ifc/resources/geometricmodel.py b/src/compas_ifc/resources/geometricmodel.py index e725b76d..858c8f8f 100644 --- a/src/compas_ifc/resources/geometricmodel.py +++ b/src/compas_ifc/resources/geometricmodel.py @@ -9,7 +9,7 @@ from compas_ifc.resources.geometry import IfcAxis2Placement3D_to_frame from compas_ifc.resources.geometry import IfcDirection_to_vector from compas_ifc.resources.geometry import IfcProfileDef_to_curve -from compas_ifc.shapes import TessellatedBrep +from compas_ifc.brep import TessellatedBrep from ifcopenshell import geom From b72326e5a864f93327fd503f1e61ba59f987921a Mon Sep 17 00:00:00 2001 From: Li Date: Tue, 6 Feb 2024 18:34:31 +0100 Subject: [PATCH 4/8] Auto stash before merge of "tessellatedbrep" and "origin/tessellatedbrep" --- scripts/3.2_element_view.py | 20 ++++++++++++++------ src/compas_ifc/brep/tessellatedbrepobject.py | 4 ++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/scripts/3.2_element_view.py b/scripts/3.2_element_view.py index 263b0d33..6b4a785d 100644 --- a/scripts/3.2_element_view.py +++ b/scripts/3.2_element_view.py @@ -1,12 +1,20 @@ from compas_viewer import Viewer from compas_ifc.model import Model -model = Model("data/wall-with-opening-and-window.ifc") -viewer = Viewer() +# model = Model("data/wall-with-opening-and-window.ifc") +model = Model("temp/Duplex_A_20110907.ifc") +viewer = Viewer(rendermode="lighted") -for entity in model.get_entities_by_type("IfcWall"): - print("Converting to brep:", entity) - print(entity.body_with_opening) - viewer.add(entity.body_with_opening, name=entity.name) +for entity in model.get_entities_by_type("IfcBuildingElement"): + print("Converting:", entity) + + # for a in entity.body: + # obj = viewer.add(a, name="{}.Body".format(entity.name)) + + # for a in entity.opening: + # obj = viewer.add(a, name="{}.opening".format(entity.name)) + + for a in entity.body_with_opening: + obj = viewer.add(a, name="{}.opening".format(entity.name)) viewer.show() diff --git a/src/compas_ifc/brep/tessellatedbrepobject.py b/src/compas_ifc/brep/tessellatedbrepobject.py index 5a63f56c..279d8af1 100644 --- a/src/compas_ifc/brep/tessellatedbrepobject.py +++ b/src/compas_ifc/brep/tessellatedbrepobject.py @@ -1,5 +1,6 @@ from compas_viewer.scene import ViewerSceneObject from .tessellatedbrep import TessellatedBrep +from compas.datastructures import Mesh import numpy as np class TessellatedBrepObject(ViewerSceneObject): @@ -20,3 +21,6 @@ def _read_frontfaces_data(self): positions = self.tessellatedbrep.vertices[self.tessellatedbrep.faces].reshape(-1, 3) elements = np.arange(len(positions)*3).reshape(-1, 3) return positions.tolist(), self.facecolors, elements.tolist() + + def to_mesh(self): + return Mesh.from_vertices_and_faces(self.tessellatedbrep.vertices, self.tessellatedbrep.faces) From c1eb4059672a58a22a4164ceed3bb7fe1cff686f Mon Sep 17 00:00:00 2001 From: Li Date: Tue, 6 Feb 2024 22:59:07 +0100 Subject: [PATCH 5/8] style map + nightly push --- src/compas_ifc/brep/tessellatedbrep.py | 6 +- src/compas_ifc/brep/tessellatedbrepobject.py | 24 ++++- src/compas_ifc/entities/product.py | 17 +++- src/compas_ifc/model.py | 4 +- src/compas_ifc/reader.py | 102 +++++++++++-------- 5 files changed, 99 insertions(+), 54 deletions(-) diff --git a/src/compas_ifc/brep/tessellatedbrep.py b/src/compas_ifc/brep/tessellatedbrep.py index 0aac8648..4ff1c42e 100644 --- a/src/compas_ifc/brep/tessellatedbrep.py +++ b/src/compas_ifc/brep/tessellatedbrep.py @@ -15,9 +15,9 @@ def __init__(self, vertices=None, edges=None, faces=None, **kwargs): self.vertices = np.array(vertices).reshape(-1, 3) self.edges = np.array(edges).reshape(-1, 2) self.faces = np.array(faces).reshape(-1, 3) - + def transform(self, transformation): self.vertices = transform_points_numpy(self.vertices, transformation) - + def to_vertices_and_faces(self): - return self.vertices, self.faces \ No newline at end of file + return self.vertices, self.faces diff --git a/src/compas_ifc/brep/tessellatedbrepobject.py b/src/compas_ifc/brep/tessellatedbrepobject.py index 279d8af1..8dd2efac 100644 --- a/src/compas_ifc/brep/tessellatedbrepobject.py +++ b/src/compas_ifc/brep/tessellatedbrepobject.py @@ -3,11 +3,17 @@ from compas.datastructures import Mesh import numpy as np + class TessellatedBrepObject(ViewerSceneObject): def __init__(self, tessellatedbrep: TessellatedBrep, facecolors=None, **kwargs): super().__init__(item=tessellatedbrep, **kwargs) self.tessellatedbrep = tessellatedbrep self.facecolors = facecolors + self.use_rgba = True + + # TODO: it is not facecolors, it is verexcolor + if not self.facecolors: + self.facecolors = [[0.5, 0.5, 0.5, 1.0] for _ in range(len(self.tessellatedbrep.faces) * 3)] def _read_lines_data(self): positions = self.tessellatedbrep.vertices @@ -16,11 +22,21 @@ def _read_lines_data(self): default_color = self.linescolor["_default"] colors = np.full(shape=(len(elements), 3), fill_value=default_color) return positions.tolist(), colors.tolist(), elements.tolist() - + def _read_frontfaces_data(self): - positions = self.tessellatedbrep.vertices[self.tessellatedbrep.faces].reshape(-1, 3) - elements = np.arange(len(positions)*3).reshape(-1, 3) - return positions.tolist(), self.facecolors, elements.tolist() + positions = self.tessellatedbrep.vertices[self.tessellatedbrep.faces].reshape(-1, 3).tolist() + elements = np.arange(len(positions) * 3).reshape(-1, 3).tolist() + colors = [color[:3] for color in self.facecolors] + opacities = [color[3] for color in self.facecolors] + return positions, colors, opacities, elements + + def _read_backfaces_data(self): + positions = self.tessellatedbrep.vertices[self.tessellatedbrep.faces].reshape(-1, 3).tolist() + elements = np.arange(len(positions) * 3).reshape(-1, 3) + elements = elements[:, ::-1].tolist() + colors = [color[:3] for color in self.facecolors] + opacities = [color[3] for color in self.facecolors] + return positions, colors, opacities, elements def to_mesh(self): return Mesh.from_vertices_and_faces(self.tessellatedbrep.vertices, self.tessellatedbrep.faces) diff --git a/src/compas_ifc/entities/product.py b/src/compas_ifc/entities/product.py index 7176869a..7e6af622 100644 --- a/src/compas_ifc/entities/product.py +++ b/src/compas_ifc/entities/product.py @@ -28,6 +28,7 @@ def __init__(self, entity, model) -> None: self._opening = None self._body_with_opening = None self._transformation = None + self._style = None def classifications(self) -> List[Dict[str, str]]: """ @@ -142,7 +143,14 @@ def body_with_opening(self): from compas_ifc.representation import entity_body_with_opening_geometry if not self._body_with_opening: - self._body_with_opening = self.model.reader.get(self._entity.id()) or entity_body_with_opening_geometry(self) + cached_geometry = self.model.reader.get_preloaded_geometry(self) + if cached_geometry: + self._body_with_opening = cached_geometry + else: + # TODO: double check if this is still triggered with preloaded geometry + # raise + self._body_with_opening = entity_body_with_opening_geometry(self) + return self._body_with_opening @body_with_opening.setter @@ -160,3 +168,10 @@ def transformation(self): @transformation.setter def transformation(self, value): self._transformation = value + + @property + def style(self): + if not self._style: + self._style = self.model.reader.get_preloaded_style(self) + # TODO: handle non-preloaded situation + return self._style diff --git a/src/compas_ifc/model.py b/src/compas_ifc/model.py index a86c7719..28af6832 100644 --- a/src/compas_ifc/model.py +++ b/src/compas_ifc/model.py @@ -58,8 +58,8 @@ class Model: """ - def __init__(self, filepath: str = None, entity_types: dict = None) -> None: - self.reader = IFCReader(model=self, entity_types=entity_types) + def __init__(self, filepath: str = None, entity_types: dict = None, use_occ=True) -> None: + self.reader = IFCReader(model=self, entity_types=entity_types, use_occ=use_occ) self.writer = IFCWriter(model=self) self._new_entities = set() self._projects = None diff --git a/src/compas_ifc/reader.py b/src/compas_ifc/reader.py index 1e467c25..79a1ee3e 100644 --- a/src/compas_ifc/reader.py +++ b/src/compas_ifc/reader.py @@ -50,14 +50,16 @@ class IFCReader(object): """ - def __init__(self, model, entity_types: dict = None): + def __init__(self, model, entity_types: dict = None, use_occ=True): self.filepath = None self.model = model self.entity_types = entity_types + self.use_occ = use_occ self._file = ifcopenshell.file() self._schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self._file.schema) self._entitymap = {} self._geometrymap = {} + self._stylemap = {} def open(self, filepath: str): self.filepath = filepath @@ -134,58 +136,70 @@ def file_size(self): def get_preloaded_geometry(self, entity): return self._geometrymap.get(entity._entity.id()) + def get_preloaded_style(self, entity): + return self._stylemap.get(entity._entity.id(), {}) + def load_geometries(self, include=None, exclude=None): """Load all the geometries of the IFC file using a fast multithreaded iterator.""" print("Loading geometries...") settings = ifcopenshell.geom.settings() - iterator = ifcopenshell.geom.iterator(settings, self._file, multiprocessing.cpu_count(), include=include, exclude=exclude) + if self.use_occ: + settings.set(settings.USE_PYTHON_OPENCASCADE, True) + + iterator = ifcopenshell.geom.iterator( + settings, self._file, multiprocessing.cpu_count(), include=include, exclude=exclude + ) start = time.time() if iterator.initialize(): while True: shape = iterator.get() - matrix = shape.transformation.matrix.data - faces = shape.geometry.faces - edges = shape.geometry.edges - verts = shape.geometry.verts - brep = TessellatedBrep(vertices=verts, edges=edges, faces=faces) - - - matrix = np.array(matrix).reshape((4, 3)) - matrix = np.hstack([ - matrix, - np.array([[0], [0], [0], [1]]) - ]) - matrix = matrix.transpose() - transformation = Transformation.from_matrix(matrix.tolist()) - - facecolors = [] - opacity=0 - for m_id in shape.geometry.material_ids: - if m_id == -1: - facecolors.append([0.5, 0.5, 0.5]) - facecolors.append([0.5, 0.5, 0.5]) - facecolors.append([0.5, 0.5, 0.5]) - continue - material = shape.geometry.materials[m_id] - color = material.diffuse - facecolors.append(color) - facecolors.append(color) - facecolors.append(color) - opacity += 1 - material.transparency - - opacity /= len(shape.geometry.material_ids) - - if opacity < 1 and opacity > 0.5: - opacity = 0.5 - - self._geometrymap[shape.id] = { - "brep": brep, - "transformation": transformation, - "facecolors": facecolors, - "opacity": opacity - } + if self.use_occ: + from compas_occ.brep import OCCBrep + + brep = OCCBrep.from_shape(shape.geometry) + + shellcolors = [] + for style_id, style in zip(shape.style_ids, shape.styles): + if style_id == -1: + shellcolors.append((0.5, 0.5, 0.5, 1.0)) + else: + shellcolors.append(style) + + self._geometrymap[shape.data.id] = brep + self._stylemap[shape.data.id] = {"shellcolors": shellcolors, "use_rgba": True} + + else: + + matrix = shape.transformation.matrix.data + faces = shape.geometry.faces + edges = shape.geometry.edges + verts = shape.geometry.verts + + matrix = np.array(matrix).reshape((4, 3)) + matrix = np.hstack([matrix, np.array([[0], [0], [0], [1]])]) + matrix = matrix.transpose() + transformation = Transformation.from_matrix(matrix.tolist()) + + facecolors = [] + for m_id in shape.geometry.material_ids: + if m_id == -1: + facecolors.append([0.5, 0.5, 0.5, 1]) + facecolors.append([0.5, 0.5, 0.5, 1]) + facecolors.append([0.5, 0.5, 0.5, 1]) + continue + material = shape.geometry.materials[m_id] + color = (*material.diffuse, 1 - material.transparency) + facecolors.append(color) + facecolors.append(color) + facecolors.append(color) + + brep = TessellatedBrep(vertices=verts, edges=edges, faces=faces) + + brep.transform(transformation) + self._geometrymap[shape.id] = brep + self._stylemap[shape.id] = {"facecolors": facecolors} if not iterator.next(): break - print("Time to load all geometries ", time.time() - start) \ No newline at end of file + print("Time to load all geometries ", time.time() - start) From ad4b94dc48433987d33905e3d19f966f10ee358f Mon Sep 17 00:00:00 2001 From: Li Chen Date: Mon, 19 Feb 2024 16:45:05 +0100 Subject: [PATCH 6/8] deal with IFC2x3 --- src/compas_ifc/brep/__init__.py | 2 +- src/compas_ifc/model.py | 9 ++- src/compas_ifc/resources/geometricmodel.py | 3 +- src/compas_ifc/resources/mesh.py | 21 +++++++ src/compas_ifc/resources/representation.py | 13 +++- src/compas_ifc/writer.py | 70 ++++++++++++++-------- 6 files changed, 86 insertions(+), 32 deletions(-) diff --git a/src/compas_ifc/brep/__init__.py b/src/compas_ifc/brep/__init__.py index d292d295..bc2180ae 100644 --- a/src/compas_ifc/brep/__init__.py +++ b/src/compas_ifc/brep/__init__.py @@ -9,4 +9,4 @@ def register_scene_objects(): register(TessellatedBrep, TessellatedBrepObject, context="Viewer") -__all__ = ["TessellatedBrep", "TessellatedBrepObject"] \ No newline at end of file +__all__ = ["TessellatedBrep", "TessellatedBrepObject"] diff --git a/src/compas_ifc/model.py b/src/compas_ifc/model.py index 28af6832..920857fc 100644 --- a/src/compas_ifc/model.py +++ b/src/compas_ifc/model.py @@ -13,6 +13,7 @@ from .reader import IFCReader from .writer import IFCWriter +import ifcopenshell class Model: @@ -58,7 +59,7 @@ class Model: """ - def __init__(self, filepath: str = None, entity_types: dict = None, use_occ=True) -> None: + def __init__(self, filepath: str = None, entity_types: dict = None, use_occ=True, schema=None) -> None: self.reader = IFCReader(model=self, entity_types=entity_types, use_occ=use_occ) self.writer = IFCWriter(model=self) self._new_entities = set() @@ -69,6 +70,10 @@ def __init__(self, filepath: str = None, entity_types: dict = None, use_occ=True self._elements = None self._building_elements = None self._geographic_elements = None + self._schema = None + if schema: + self._schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema) + if filepath: self.open(filepath) @@ -124,7 +129,7 @@ def print_summary(self): @property def schema(self) -> str: - return self.reader._schema + return self._schema or self.reader._schema @property def project(self) -> Project: diff --git a/src/compas_ifc/resources/geometricmodel.py b/src/compas_ifc/resources/geometricmodel.py index 858c8f8f..a71712ba 100644 --- a/src/compas_ifc/resources/geometricmodel.py +++ b/src/compas_ifc/resources/geometricmodel.py @@ -5,7 +5,8 @@ from compas.geometry import Line from compas.geometry import Point from compas.geometry import Transformation -from compas.datastructures import Mesh + +# from compas.datastructures import Mesh from compas_ifc.resources.geometry import IfcAxis2Placement3D_to_frame from compas_ifc.resources.geometry import IfcDirection_to_vector from compas_ifc.resources.geometry import IfcProfileDef_to_curve diff --git a/src/compas_ifc/resources/mesh.py b/src/compas_ifc/resources/mesh.py index b9251cd1..7d0b39d5 100644 --- a/src/compas_ifc/resources/mesh.py +++ b/src/compas_ifc/resources/mesh.py @@ -25,3 +25,24 @@ def mesh_to_IfcPolygonalFaceSet(file: ifcopenshell.file, mesh: Mesh) -> ifcopens Coordinates=file.createIfcCartesianPointList3D(vertices), Faces=faces, ) + + +def mesh_to_IfcShellBasedSurfaceModel(file: ifcopenshell.file, mesh: Mesh) -> ifcopenshell.entity_instance: + """ + Convert a COMPAS mesh to an IFC PolygonalFaceSet. + """ + vertices = {} + for key, attr in mesh.vertices(True): + vertex = file.createIfcCartesianPoint((float(attr["x"]), float(attr["y"]), float(attr["z"]))) + vertices[key] = vertex + + faces = [] + for fkey in mesh.faces(): + indexes = [vertices[key] for key in mesh.face_vertices(fkey)] + polyloop = file.create_entity("IfcPolyLoop", indexes) + bound = file.create_entity("IfcFaceOuterBound", polyloop, True) + face = file.create_entity("IfcFace", [bound]) + faces.append(face) + + shell = file.create_entity("IfcOpenShell", faces) + return file.create_entity("IfcShellBasedSurfaceModel", [shell]) diff --git a/src/compas_ifc/resources/representation.py b/src/compas_ifc/resources/representation.py index 612e11e3..b7c0f470 100644 --- a/src/compas_ifc/resources/representation.py +++ b/src/compas_ifc/resources/representation.py @@ -8,6 +8,7 @@ from .brep import brep_to_ifc_advanced_brep from .mesh import mesh_to_IfcPolygonalFaceSet +from .mesh import mesh_to_IfcShellBasedSurfaceModel from .shapes import box_to_IfcBlock from .shapes import cone_to_IfcRightCircularCone from .shapes import cylinder_to_IfcRightCircularCylinder @@ -27,7 +28,10 @@ def _body_to_shape(body): elif isinstance(body, Cylinder): shape = cylinder_to_IfcRightCircularCylinder(file, body) elif isinstance(body, Mesh): - shape = mesh_to_IfcPolygonalFaceSet(file, body) + if file.schema == "IFC4" or file.schema == "IFC4x3": + shape = mesh_to_IfcPolygonalFaceSet(file, body) + else: + shape = mesh_to_IfcShellBasedSurfaceModel(file, body) elif isinstance(body, OCCBrep): shape = brep_to_ifc_advanced_brep(file, body) else: @@ -47,11 +51,16 @@ def _body_to_shape(body): if not isinstance(shape, list): shape = [shape] + RepresentationType = "SolidModel" + + if shape and shape[0].is_a("IfcShellBasedSurfaceModel"): + RepresentationType = "SurfaceModel" + representation = file.create_entity( "IfcShapeRepresentation", ContextOfItems=context, RepresentationIdentifier="Body", - RepresentationType="SolidModel", + RepresentationType=RepresentationType, Items=shape, ) diff --git a/src/compas_ifc/writer.py b/src/compas_ifc/writer.py index 32ef1041..a33cc175 100644 --- a/src/compas_ifc/writer.py +++ b/src/compas_ifc/writer.py @@ -75,8 +75,8 @@ def default_body_context(self): def default_project(self): if not self._default_project: if not self.model.projects: - self._default_project = run( - "root.create_entity", self.file, ifc_class="IfcProject", name="Default Project" + self._default_project = self.file.create_entity( + "IfcProject", GlobalId=self.create_guid(), Name="Default Project" ) run("unit.assign_unit", self.file) else: @@ -87,12 +87,14 @@ def default_project(self): def default_site(self): if not self._default_site: if not self.model.sites: - self._default_site = run("root.create_entity", self.file, ifc_class="IfcSite", name="Default Site") - run( - "aggregate.assign_object", - self.file, - product=self._default_site, - relating_object=self.default_project, + self._default_site = self.file.create_entity( + "IfcSite", Name="Default Site", GlobalId=self.create_guid() + ) + self.file.create_entity( + "IfcRelAggregates", + GlobalId=self.create_guid(), + RelatingObject=self.default_project, + RelatedObjects=[self._default_site], ) else: self._default_site = self.write_entity(self.model.sites[0]) @@ -102,14 +104,14 @@ def default_site(self): def default_building(self): if not self._default_building: if not self.model.buildings: - self._default_building = run( - "root.create_entity", self.file, ifc_class="IfcBuilding", name="Default Building" + self._default_building = self.file.create_entity( + "IfcBuilding", GlobalId=self.create_guid(), Name="Default Building" ) - run( - "aggregate.assign_object", - self.file, - product=self._default_building, - relating_object=self.default_site, + self.file.create_entity( + "IfcRelAggregates", + GlobalId=self.create_guid(), + RelatingObject=self.default_site, + RelatedObjects=[self._default_building], ) else: self._default_building = self.write_entity(self.model.buildings[0]) @@ -119,22 +121,25 @@ def default_building(self): def default_building_storey(self): if not self._default_building_storey: if not self.model.building_storeys: - self._default_building_storey = run( - "root.create_entity", self.file, ifc_class="IfcBuildingStorey", name="Default Storey" + self._default_building_storey = self.file.create_entity( + "IfcBuildingStorey", GlobalId=self.create_guid(), Name="Default Storey" ) - run( - "aggregate.assign_object", - self.file, - product=self._default_building_storey, - relating_object=self.default_building, + self.file.create_entity( + "IfcRelAggregates", + GlobalId=self.create_guid(), + RelatingObject=self.default_building, + RelatedObjects=[self._default_building_storey], ) else: self._default_building_storey = self.write_entity(self.model.building_storeys[0]) return self._default_building_storey + def create_guid(self): + return ifcopenshell.guid.new() + def reset(self): """Resets the writer to start with a new ifc file.""" - self.file = ifcopenshell.file() + self.file = ifcopenshell.file(schema=self.model.schema.name()) self._entitymap = {} self._default_context = None self._default_body_context = None @@ -232,9 +237,16 @@ def create_new_relation(self, entity: ObjectDefinition): child = self._entitymap[entity] if isinstance(entity, Element): - run("spatial.assign_container", self.file, relating_structure=parent, product=child) + self.file.create_entity( + "IfcRelContainedInSpatialStructure", + GlobalId=self.create_guid(), + RelatingStructure=parent, + RelatedElements=[child], + ) else: - run("aggregate.assign_object", self.file, relating_object=parent, product=child) + self.file.create_entity( + "IfcRelAggregates", GlobalId=self.create_guid(), RelatingObject=parent, RelatedObjects=[child] + ) def write_entity(self, entity: Entity) -> None: """Writes the given entity recursively with all its referencing attributes to the ifc file.""" @@ -250,7 +262,13 @@ def write_entity(self, entity: Entity) -> None: else: attributes[key] = value - ifc_entity = self.file.create_entity(entity.ifc_type, **attributes) + # ifc_entity = self.file.create_entity(entity.ifc_type, **attributes) + ifc_entity = self.file.create_entity(entity.ifc_type) + for key, value in attributes.items(): + if value is not None: + print(key, value) + setattr(ifc_entity, key, value) + self._entitymap[entity] = ifc_entity if not entity._entity: From 1da6388052e93753124a6ca98e99339b0a10855d Mon Sep 17 00:00:00 2001 From: Li Date: Fri, 23 Feb 2024 16:31:44 +0100 Subject: [PATCH 7/8] optmize import time --- src/compas_ifc/reader.py | 4 +++- src/compas_ifc/resources/geometricmodel.py | 15 +++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/compas_ifc/reader.py b/src/compas_ifc/reader.py index 79a1ee3e..e1cf0b3b 100644 --- a/src/compas_ifc/reader.py +++ b/src/compas_ifc/reader.py @@ -4,7 +4,6 @@ import os import multiprocessing import numpy as np -from .brep import TessellatedBrep from compas.geometry import Transformation import time @@ -142,6 +141,8 @@ def get_preloaded_style(self, entity): def load_geometries(self, include=None, exclude=None): """Load all the geometries of the IFC file using a fast multithreaded iterator.""" print("Loading geometries...") + import ifcopenshell.geom + settings = ifcopenshell.geom.settings() if self.use_occ: settings.set(settings.USE_PYTHON_OPENCASCADE, True) @@ -169,6 +170,7 @@ def load_geometries(self, include=None, exclude=None): self._stylemap[shape.data.id] = {"shellcolors": shellcolors, "use_rgba": True} else: + from .brep import TessellatedBrep matrix = shape.transformation.matrix.data faces = shape.geometry.faces diff --git a/src/compas_ifc/resources/geometricmodel.py b/src/compas_ifc/resources/geometricmodel.py index a71712ba..7cf3663e 100644 --- a/src/compas_ifc/resources/geometricmodel.py +++ b/src/compas_ifc/resources/geometricmodel.py @@ -5,14 +5,11 @@ from compas.geometry import Line from compas.geometry import Point from compas.geometry import Transformation - -# from compas.datastructures import Mesh from compas_ifc.resources.geometry import IfcAxis2Placement3D_to_frame from compas_ifc.resources.geometry import IfcDirection_to_vector from compas_ifc.resources.geometry import IfcProfileDef_to_curve -from compas_ifc.brep import TessellatedBrep -from ifcopenshell import geom +import ifcopenshell def IfcAdvancedBrep_to_brep(advanced_brep): @@ -230,9 +227,9 @@ def IfcTriangulatedFaceSet_to_brep(triangulated_face_set): def IfcShape_to_brep(ifc_shape): from compas_occ.brep import OCCBrep - settings = geom.settings() + settings = ifcopenshell.geom.settings() settings.set(settings.USE_PYTHON_OPENCASCADE, True) - shape = geom.create_shape(settings, ifc_shape) + shape = ifcopenshell.geom.create_shape(settings, ifc_shape) brep = OCCBrep.from_shape(shape) brep.sew() @@ -243,8 +240,10 @@ def IfcShape_to_brep(ifc_shape): def IfcShape_to_tessellatedbrep(ifc_shape): - settings = geom.settings() - shape = geom.create_shape(settings, ifc_shape) + from compas_ifc.brep import TessellatedBrep + + settings = ifcopenshell.geom.settings() + shape = ifcopenshell.geom.create_shape(settings, ifc_shape) return TessellatedBrep(vertices=shape.verts, edges=shape.edges, faces=shape.faces) From eb5e0ea2f622e732397a53837da99e25cabc7566 Mon Sep 17 00:00:00 2001 From: Li Date: Mon, 26 Feb 2024 15:13:40 +0100 Subject: [PATCH 8/8] use_occ False by default, make all examples work --- scripts/3.1_model_view.py | 4 +--- scripts/3.2_element_view.py | 18 ++++-------------- scripts/3.3_opening.py | 2 +- scripts/3.4_wall_sections.py | 10 +++------- src/compas_ifc/entities/product.py | 6 +++--- src/compas_ifc/model.py | 2 +- src/compas_ifc/representation.py | 4 ++-- src/compas_ifc/writer.py | 1 - 8 files changed, 15 insertions(+), 32 deletions(-) diff --git a/scripts/3.1_model_view.py b/scripts/3.1_model_view.py index fe75ba29..3b0b2d95 100644 --- a/scripts/3.1_model_view.py +++ b/scripts/3.1_model_view.py @@ -6,8 +6,6 @@ viewer = Viewer() for entity in model.get_entities_by_type("IfcBuildingElement"): - print("Converting to brep:", entity) - print(entity.body_with_opening) - viewer.add(entity.body_with_opening, name=entity.name) + viewer.add(entity.body_with_opening, name=entity.name, **entity.style) viewer.show() diff --git a/scripts/3.2_element_view.py b/scripts/3.2_element_view.py index 6b4a785d..020d0172 100644 --- a/scripts/3.2_element_view.py +++ b/scripts/3.2_element_view.py @@ -1,20 +1,10 @@ from compas_viewer import Viewer from compas_ifc.model import Model -# model = Model("data/wall-with-opening-and-window.ifc") -model = Model("temp/Duplex_A_20110907.ifc") -viewer = Viewer(rendermode="lighted") +model = Model("data/wall-with-opening-and-window.ifc") +viewer = Viewer() -for entity in model.get_entities_by_type("IfcBuildingElement"): - print("Converting:", entity) - - # for a in entity.body: - # obj = viewer.add(a, name="{}.Body".format(entity.name)) - - # for a in entity.opening: - # obj = viewer.add(a, name="{}.opening".format(entity.name)) - - for a in entity.body_with_opening: - obj = viewer.add(a, name="{}.opening".format(entity.name)) +for entity in model.get_entities_by_type("IfcWall"): + viewer.add(entity.body_with_opening, name=entity.name) viewer.show() diff --git a/scripts/3.3_opening.py b/scripts/3.3_opening.py index 7eab66be..bdac6bea 100644 --- a/scripts/3.3_opening.py +++ b/scripts/3.3_opening.py @@ -12,7 +12,7 @@ obj = viewer.add(entity.opening, name="opening") obj.transformation = Translation.from_vector([0, 1, 0]) - obj = viewer.add(entity.body_with_opening, name="combined", show_faces=True) + obj = viewer.add(entity.body_with_opening, name="combined") obj.transformation = Translation.from_vector([0, 2, 0]) diff --git a/scripts/3.4_wall_sections.py b/scripts/3.4_wall_sections.py index faf59f44..805cbbe8 100644 --- a/scripts/3.4_wall_sections.py +++ b/scripts/3.4_wall_sections.py @@ -3,7 +3,7 @@ from compas_ifc.model import Model -model = Model("data/wall-with-opening-and-window.ifc") +model = Model("data/wall-with-opening-and-window.ifc", use_occ=True) viewer = Viewer() sectionPlane = Plane([0, 0, 1], [0, 0, 1]) @@ -13,12 +13,8 @@ viewer.add(wall.body_with_opening, name="{}.Body".format(wall.name), opacity=0.5) print("creating section...") - sections = [] - for shape in wall.body_with_opening: - brep = shape.slice(sectionPlane) - sections.append(shape.slice(sectionPlane)) - - viewer.add(sections, name="{}.Sections".format(wall.name), linecolor=(1, 0, 0), linewidth=5, show_lines=True) + section= wall.body_with_opening.slice(sectionPlane) + viewer.add(section, name="{}.Sections".format(wall.name), linecolor=(1, 0, 0), edgewidth=5, show_edges=True) print("Finished") viewer.show() diff --git a/src/compas_ifc/entities/product.py b/src/compas_ifc/entities/product.py index 7e6af622..28189742 100644 --- a/src/compas_ifc/entities/product.py +++ b/src/compas_ifc/entities/product.py @@ -119,7 +119,7 @@ def body(self): from compas_ifc.representation import entity_body_geometry if not self._body: - self._body = entity_body_geometry(self) + self._body = entity_body_geometry(self, use_occ=self.model.reader.use_occ) return self._body @body.setter @@ -131,7 +131,7 @@ def opening(self): from compas_ifc.representation import entity_opening_geometry if not self._opening: - self._opening = entity_opening_geometry(self) + self._opening = entity_opening_geometry(self, use_occ=self.model.reader.use_occ) return self._opening @opening.setter @@ -149,7 +149,7 @@ def body_with_opening(self): else: # TODO: double check if this is still triggered with preloaded geometry # raise - self._body_with_opening = entity_body_with_opening_geometry(self) + self._body_with_opening = entity_body_with_opening_geometry(self, use_occ=self.model.reader.use_occ) return self._body_with_opening diff --git a/src/compas_ifc/model.py b/src/compas_ifc/model.py index 920857fc..f0cabd32 100644 --- a/src/compas_ifc/model.py +++ b/src/compas_ifc/model.py @@ -59,7 +59,7 @@ class Model: """ - def __init__(self, filepath: str = None, entity_types: dict = None, use_occ=True, schema=None) -> None: + def __init__(self, filepath: str = None, entity_types: dict = None, use_occ=False, schema=None) -> None: self.reader = IFCReader(model=self, entity_types=entity_types, use_occ=use_occ) self.writer = IFCWriter(model=self) self._new_entities = set() diff --git a/src/compas_ifc/representation.py b/src/compas_ifc/representation.py index 4a52552b..acbe4910 100644 --- a/src/compas_ifc/representation.py +++ b/src/compas_ifc/representation.py @@ -95,7 +95,7 @@ def _entity_transformation(_entity, scale=1.0): return scaled_placement -def entity_body_geometry(entity: Entity, context="Model", use_occ=True, apply_transformation=True): +def entity_body_geometry(entity: Entity, context="Model", use_occ=False, apply_transformation=True): """ Construct the body geometry representations of an entity. @@ -133,7 +133,7 @@ def entity_body_geometry(entity: Entity, context="Model", use_occ=True, apply_tr return bodies -def entity_opening_geometry(entity: Entity, use_occ=True, apply_transformation=True): +def entity_opening_geometry(entity: Entity, use_occ=False, apply_transformation=True): """ Construct the opening geometry representations of an entity. """ diff --git a/src/compas_ifc/writer.py b/src/compas_ifc/writer.py index a33cc175..75eafc97 100644 --- a/src/compas_ifc/writer.py +++ b/src/compas_ifc/writer.py @@ -266,7 +266,6 @@ def write_entity(self, entity: Entity) -> None: ifc_entity = self.file.create_entity(entity.ifc_type) for key, value in attributes.items(): if value is not None: - print(key, value) setattr(ifc_entity, key, value) self._entitymap[entity] = ifc_entity