You can now clash multiple IFC files against multiple IFC files

This commit is contained in:
Dion Moult
2020-05-17 14:35:08 +10:00
parent ef5f86273e
commit ca4b257188
+46 -34
View File
@@ -17,28 +17,28 @@ class Mesh:
class IfcClasher: class IfcClasher:
def __init__(self, a_file, b_file, settings): def __init__(self, settings):
self.settings = settings self.settings = settings
self.geom_settings = ifcopenshell.geom.settings() self.geom_settings = ifcopenshell.geom.settings()
self.tolerance = 0.01 self.tolerance = 0.01
self.a = None self.a = []
self.b = None self.b = []
self.a_file = a_file self.a_cm = None
self.b_file = b_file self.b_cm = None
self.clashes = {} self.clashes = {}
self.a_meshes = {}
self.b_meshes = {}
def clash(self): def clash(self):
for ab in ['a', 'b']: for ab in ['a', 'b']:
self.settings.logger.info(f'Loading file {ab} ...')
setattr(self, ab, ifcopenshell.open(getattr(self, f'{ab}_file')))
self.patch_ifc(ab)
self.settings.logger.info(f'Purging unnecessary elements {ab} ...')
self.purge_elements(ab)
self.settings.logger.info(f'Creating collision manager {ab} ...') self.settings.logger.info(f'Creating collision manager {ab} ...')
setattr(self, f'{ab}_cm', collision.CollisionManager()) setattr(self, f'{ab}_cm', collision.CollisionManager())
self.add_collision_objects(ab) self.settings.logger.info(f'Loading files {ab} ...')
for data in getattr(self, ab):
data['ifc'] = ifcopenshell.open(data['file'])
self.patch_ifc(data['ifc'])
self.settings.logger.info(f'Purging unnecessary elements {ab} ...')
self.purge_elements(data['ifc'])
self.settings.logger.info(f'Creating collision data for {ab} ...')
self.add_collision_objects(data, getattr(self, f'{ab}_cm'))
results = self.a_cm.in_collision_other(self.b_cm, return_data=True) results = self.a_cm.in_collision_other(self.b_cm, return_data=True)
if not results[0]: if not results[0]:
@@ -46,8 +46,8 @@ class IfcClasher:
for contact in results[1]: for contact in results[1]:
a_global_id, b_global_id = contact.names a_global_id, b_global_id = contact.names
a = self.a.by_guid(a_global_id) a = self.get_element('a', a_global_id)
b = self.b.by_guid(b_global_id) b = self.get_element('b', b_global_id)
if contact.raw.penetration_depth < self.tolerance: if contact.raw.penetration_depth < self.tolerance:
continue continue
self.clashes[f'{a_global_id}-{b_global_id}'] = { self.clashes[f'{a_global_id}-{b_global_id}'] = {
@@ -66,14 +66,22 @@ class IfcClasher:
with open(self.settings.output, 'w', encoding='utf-8') as clashes_file: with open(self.settings.output, 'w', encoding='utf-8') as clashes_file:
json.dump(list(self.clashes.values()), clashes_file, indent=4) json.dump(list(self.clashes.values()), clashes_file, indent=4)
def purge_elements(self, ab): def get_element(self, ab, global_id):
# TODO: more filtering abilities for data in getattr(self, ab):
for element in getattr(self, ab).by_type('IfcSpace'): try:
getattr(self, ab).remove(element) element = data['ifc'].by_guid(global_id)
if element:
return element
except:
pass
def add_collision_objects(self, ab): def purge_elements(self, ifc_file):
self.settings.logger.info('Creating collision data for {}'.format(ab)) # TODO: more filtering abilities
iterator = ifcopenshell.geom.iterator(self.geom_settings, getattr(self, ab), multiprocessing.cpu_count()) for element in ifc_file.by_type('IfcSpace'):
ifc_file.remove(element)
def add_collision_objects(self, data, cm):
iterator = ifcopenshell.geom.iterator(self.geom_settings, data['ifc'], multiprocessing.cpu_count())
valid_file = iterator.initialize() valid_file = iterator.initialize()
if not valid_file: if not valid_file:
return False return False
@@ -83,21 +91,21 @@ class IfcClasher:
if progress > old_progress: if progress > old_progress:
print("\r[" + "#" * progress + " " * (50 - progress) + "]", end="") print("\r[" + "#" * progress + " " * (50 - progress) + "]", end="")
old_progress = progress old_progress = progress
self.add_collision_object(ab, iterator.get()) self.add_collision_object(data, cm, iterator.get())
if not iterator.next(): if not iterator.next():
break break
def add_collision_object(self, ab, shape): def add_collision_object(self, data, cm, shape):
if shape is None: if shape is None:
return return
element = getattr(self, ab).by_id(shape.guid) element = data['ifc'].by_id(shape.guid)
self.settings.logger.info('Creating object {}'.format(element)) self.settings.logger.info('Creating object {}'.format(element))
mesh_name = f'mesh-{shape.geometry.id}' mesh_name = f'mesh-{shape.geometry.id}'
if mesh_name in getattr(self, f'{ab}_meshes'): if mesh_name in data['meshes']:
mesh = getattr(self, f'{ab}_meshes')[mesh_name] mesh = data['meshes'][mesh_name]
else: else:
mesh = self.create_mesh(shape) mesh = self.create_mesh(shape)
getattr(self, f'{ab}_meshes')[mesh_name] = mesh data['meshes'][mesh_name] = mesh
m = shape.transformation.matrix.data m = shape.transformation.matrix.data
mat = np.array( mat = np.array(
@@ -109,7 +117,7 @@ class IfcClasher:
] ]
) )
mat.transpose() mat.transpose()
getattr(self, f'{ab}_cm').add_object(shape.guid, mesh, mat) cm.add_object(shape.guid, mesh, mat)
def create_mesh(self, shape): def create_mesh(self, shape):
f = shape.geometry.faces f = shape.geometry.faces
@@ -121,8 +129,8 @@ class IfcClasher:
for i in range(0, len(f), 3)]) for i in range(0, len(f), 3)])
return mesh return mesh
def patch_ifc(self, ab): def patch_ifc(self, ifc_file):
project = getattr(self, ab).by_type('IfcProject')[0] project = ifc_file.by_type('IfcProject')[0]
sites = self.find_decomposed_ifc_class(project, 'IfcSite') sites = self.find_decomposed_ifc_class(project, 'IfcSite')
for site in sites: for site in sites:
self.patch_placement_to_origin(site) self.patch_placement_to_origin(site)
@@ -160,12 +168,14 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Clashes geometry between two IFC files') description='Clashes geometry between two IFC files')
parser.add_argument( parser.add_argument(
'a', '-a',
type=str, type=str,
nargs='+',
help='The IFC file containing group A of objects to clash') help='The IFC file containing group A of objects to clash')
parser.add_argument( parser.add_argument(
'b', '-b',
type=str, type=str,
nargs='+',
help='The IFC file containing group B of objects to clash') help='The IFC file containing group B of objects to clash')
parser.add_argument( parser.add_argument(
'-o', '-o',
@@ -182,6 +192,8 @@ if __name__ == '__main__':
handler = logging.StreamHandler(sys.stdout) handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG) handler.setLevel(logging.DEBUG)
settings.logger.addHandler(handler) settings.logger.addHandler(handler)
ifc_clasher = IfcClasher(args.a, args.b, settings) ifc_clasher = IfcClasher(settings)
ifc_clasher.a.extend([{'file': a, 'meshes': {}} for a in args.a])
ifc_clasher.b.extend([{'file': b, 'meshes': {}} for b in args.b])
ifc_clasher.clash() ifc_clasher.clash()
ifc_clasher.export() ifc_clasher.export()