Skip to content

Commit

Permalink
Merge branch 'tessellatedbrep'
Browse files Browse the repository at this point in the history
  • Loading branch information
Licini committed Feb 26, 2024
2 parents 2e37eb8 + eb5e0ea commit f8d5718
Show file tree
Hide file tree
Showing 17 changed files with 372 additions and 87 deletions.
4 changes: 1 addition & 3 deletions scripts/3.1_model_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
2 changes: 0 additions & 2 deletions scripts/3.2_element_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
viewer = Viewer()

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)

viewer.show()
2 changes: 1 addition & 1 deletion scripts/3.3_opening.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])


Expand Down
10 changes: 3 additions & 7 deletions scripts/3.4_wall_sections.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand All @@ -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()
4 changes: 4 additions & 0 deletions src/compas_ifc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@


__all__ = ["HOME", "DATA", "DOCS", "TEMP"]

__all_plugins__ = [
"compas_ifc.brep",
]
12 changes: 12 additions & 0 deletions src/compas_ifc/brep/__init__.py
Original file line number Diff line number Diff line change
@@ -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"]
23 changes: 23 additions & 0 deletions src/compas_ifc/brep/tessellatedbrep.py
Original file line number Diff line number Diff line change
@@ -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
42 changes: 42 additions & 0 deletions src/compas_ifc/brep/tessellatedbrepobject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from compas_viewer.scene import ViewerSceneObject
from .tessellatedbrep import TessellatedBrep
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
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[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)
34 changes: 31 additions & 3 deletions src/compas_ifc/entities/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def __init__(self, entity, model) -> None:
self._body = None
self._opening = None
self._body_with_opening = None
self._transformation = None
self._style = None

def classifications(self) -> List[Dict[str, str]]:
"""
Expand Down Expand Up @@ -117,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
Expand All @@ -129,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
Expand All @@ -141,9 +143,35 @@ 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)
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, use_occ=self.model.reader.use_occ)

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

@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
11 changes: 8 additions & 3 deletions src/compas_ifc/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from .reader import IFCReader
from .writer import IFCWriter
import ifcopenshell


class Model:
Expand Down Expand Up @@ -58,8 +59,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=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()
self._projects = None
Expand All @@ -69,6 +70,10 @@ def __init__(self, filepath: str = None, entity_types: dict = None) -> None:
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)

Expand Down Expand Up @@ -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:
Expand Down
84 changes: 83 additions & 1 deletion src/compas_ifc/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import ifcopenshell
import os
import multiprocessing
import numpy as np
from compas.geometry import Transformation
import time

from compas_ifc.entities.entity import Entity

Expand Down Expand Up @@ -45,20 +49,24 @@ 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
self._file = ifcopenshell.open(filepath)
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):
"""
Expand Down Expand Up @@ -123,3 +131,77 @@ 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 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...")
import ifcopenshell.geom

settings = ifcopenshell.geom.settings()
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()
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:
from .brep import TessellatedBrep

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)
Loading

0 comments on commit f8d5718

Please sign in to comment.