mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 06:39:13 +00:00
IFC Clash now supports clashing multiple clash sets and recording the results for each clash set
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Clashset foo",
|
||||||
|
"tolerance": 0.01,
|
||||||
|
"a": [
|
||||||
|
{ "file": "/home/dion/one.ifc" }
|
||||||
|
],
|
||||||
|
"b": [
|
||||||
|
{
|
||||||
|
"file": "/home/dion/two.ifc",
|
||||||
|
"selector": ".IfcProduct",
|
||||||
|
"mode": "i"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
+48
-83
@@ -21,48 +21,52 @@ class IfcClasher:
|
|||||||
def __init__(self, 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.clash_sets = []
|
||||||
self.a = []
|
#self.tolerance = 0.01
|
||||||
self.b = []
|
|
||||||
self.a_cm = None
|
|
||||||
self.b_cm = None
|
|
||||||
self.clashes = {}
|
|
||||||
|
|
||||||
def clash(self):
|
def clash(self):
|
||||||
|
for clash_set in self.clash_sets:
|
||||||
|
self.process_clash_set(clash_set)
|
||||||
|
|
||||||
|
def process_clash_set(self, clash_set):
|
||||||
for ab in ['a', 'b']:
|
for ab in ['a', 'b']:
|
||||||
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())
|
clash_set[f'{ab}_cm'] = collision.CollisionManager()
|
||||||
self.settings.logger.info(f'Loading files {ab} ...')
|
self.settings.logger.info(f'Loading files {ab} ...')
|
||||||
for data in getattr(self, ab):
|
for data in clash_set[ab]:
|
||||||
data['ifc'] = ifcopenshell.open(data['file'])
|
data['ifc'] = ifcopenshell.open(data['file'])
|
||||||
self.patch_ifc(data['ifc'])
|
self.patch_ifc(data['ifc'])
|
||||||
self.settings.logger.info(f'Purging unnecessary elements {ab} ...')
|
self.settings.logger.info(f'Purging unnecessary elements {ab} ...')
|
||||||
self.purge_elements(data)
|
self.purge_elements(data)
|
||||||
self.settings.logger.info(f'Creating collision data for {ab} ...')
|
self.settings.logger.info(f'Creating collision data for {ab} ...')
|
||||||
if len(data['ifc'].by_type('IfcElement')) > 0:
|
if len(data['ifc'].by_type('IfcElement')) > 0:
|
||||||
self.add_collision_objects(data, getattr(self, f'{ab}_cm'))
|
self.add_collision_objects(data, clash_set[f'{ab}_cm'])
|
||||||
if self.b:
|
|
||||||
results = self.a_cm.in_collision_other(self.b_cm, return_data=True)
|
if 'b' in clash_set:
|
||||||
|
results = clash_set['a_cm'].in_collision_other(clash_set['b_cm'], return_data=True)
|
||||||
else:
|
else:
|
||||||
results = self.a_cm.in_collision_internal(return_data=True)
|
results = clash_set['a_cm'].in_collision_internal(return_data=True)
|
||||||
|
|
||||||
if not results[0]:
|
if not results[0]:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
tolerance = clash_set['tolerance'] if 'tolerance' in clash_set else 0.01
|
||||||
|
clash_set['clashes'] = {}
|
||||||
|
|
||||||
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.get_element('a', a_global_id)
|
a = self.get_element(clash_set['a'], a_global_id)
|
||||||
if self.b:
|
if 'b' in clash_set:
|
||||||
b = self.get_element('b', b_global_id)
|
b = self.get_element(clash_set['b'], b_global_id)
|
||||||
else:
|
else:
|
||||||
b = self.get_element('a', b_global_id)
|
b = self.get_element(clash_set['a'], b_global_id)
|
||||||
if contact.raw.penetration_depth < self.tolerance:
|
if contact.raw.penetration_depth < tolerance:
|
||||||
continue
|
continue
|
||||||
key = f'{a_global_id}-{b_global_id}'
|
key = f'{a_global_id}-{b_global_id}'
|
||||||
if key in self.clashes \
|
if key in clash_set['clashes'] \
|
||||||
and self.clashes[key]['penetration_depth'] > contact.raw.penetration_depth:
|
and clash_set['clashes'][key]['penetration_depth'] > contact.raw.penetration_depth:
|
||||||
continue
|
continue
|
||||||
self.clashes[key] = {
|
clash_set['clashes'][key] = {
|
||||||
'a_global_id': a_global_id,
|
'a_global_id': a_global_id,
|
||||||
'b_global_id': b_global_id,
|
'b_global_id': b_global_id,
|
||||||
'a_ifc_class': a.is_a(),
|
'a_ifc_class': a.is_a(),
|
||||||
@@ -75,11 +79,23 @@ class IfcClasher:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def export(self):
|
def export(self):
|
||||||
with open(self.settings.output, 'w', encoding='utf-8') as clashes_file:
|
results = self.clash_sets.copy()
|
||||||
json.dump(list(self.clashes.values()), clashes_file, indent=4)
|
for result in results:
|
||||||
|
del result['a_cm']
|
||||||
|
del result['b_cm']
|
||||||
|
for data in result['a']:
|
||||||
|
del data['ifc']
|
||||||
|
del data['meshes']
|
||||||
|
for data in result['b']:
|
||||||
|
del data['ifc']
|
||||||
|
del data['meshes']
|
||||||
|
print(results)
|
||||||
|
|
||||||
def get_element(self, ab, global_id):
|
with open(self.settings.output, 'w', encoding='utf-8') as clashes_file:
|
||||||
for data in getattr(self, ab):
|
json.dump(results, clashes_file, indent=4)
|
||||||
|
|
||||||
|
def get_element(self, clash_group, global_id):
|
||||||
|
for data in clash_group:
|
||||||
try:
|
try:
|
||||||
element = data['ifc'].by_guid(global_id)
|
element = data['ifc'].by_guid(global_id)
|
||||||
if element:
|
if element:
|
||||||
@@ -88,7 +104,7 @@ class IfcClasher:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def purge_elements(self, data):
|
def purge_elements(self, data):
|
||||||
if not data['selector']:
|
if 'selector' not in data:
|
||||||
for element in data['ifc'].by_type('IfcSpace'):
|
for element in data['ifc'].by_type('IfcSpace'):
|
||||||
ifc_file.remove(element)
|
ifc_file.remove(element)
|
||||||
return
|
return
|
||||||
@@ -106,6 +122,7 @@ class IfcClasher:
|
|||||||
data['ifc'].remove(element)
|
data['ifc'].remove(element)
|
||||||
|
|
||||||
def add_collision_objects(self, data, cm):
|
def add_collision_objects(self, data, cm):
|
||||||
|
data['meshes'] = {}
|
||||||
iterator = ifcopenshell.geom.iterator(self.geom_settings, data['ifc'], multiprocessing.cpu_count())
|
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:
|
||||||
@@ -193,51 +210,15 @@ 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',
|
'input',
|
||||||
type=str,
|
type=str,
|
||||||
nargs='+',
|
help='A JSON dataset describing a series of clashsets')
|
||||||
help='The IFC files containing group A of objects to clash')
|
|
||||||
parser.add_argument(
|
|
||||||
'-b',
|
|
||||||
type=str,
|
|
||||||
nargs='+',
|
|
||||||
help='The IFC files containing group B of objects to clash')
|
|
||||||
parser.add_argument(
|
|
||||||
'-as',
|
|
||||||
'--a-selector',
|
|
||||||
type=str,
|
|
||||||
nargs='+',
|
|
||||||
help='Selector queries for each IFC file in group A')
|
|
||||||
parser.add_argument(
|
|
||||||
'-bs',
|
|
||||||
'--b-selector',
|
|
||||||
type=str,
|
|
||||||
nargs='+',
|
|
||||||
help='Selector queries for each IFC file in group B')
|
|
||||||
parser.add_argument(
|
|
||||||
'-am',
|
|
||||||
'--a-mode',
|
|
||||||
type=str,
|
|
||||||
nargs='+',
|
|
||||||
help='Selection mode for each file in group A. "i" for include, and "e" for exclude')
|
|
||||||
parser.add_argument(
|
|
||||||
'-bm',
|
|
||||||
'--b-mode',
|
|
||||||
type=str,
|
|
||||||
nargs='+',
|
|
||||||
help='Selection mode for each file in group B. "i" for include, and "e" for exclude')
|
|
||||||
parser.add_argument(
|
|
||||||
'-t',
|
|
||||||
'--tolerance',
|
|
||||||
type=float,
|
|
||||||
help='The distance tolerance that clashes should exceed',
|
|
||||||
default=0.01)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-o',
|
'-o',
|
||||||
'--output',
|
'--output',
|
||||||
type=str,
|
type=str,
|
||||||
help='The JSON diff file to output. Defaults to clashes.json',
|
help='The JSON diff file to output. Defaults to output.json',
|
||||||
default='clashes.json')
|
default='output.json')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
settings = IfcClashSettings()
|
settings = IfcClashSettings()
|
||||||
@@ -248,23 +229,7 @@ if __name__ == '__main__':
|
|||||||
handler.setLevel(logging.DEBUG)
|
handler.setLevel(logging.DEBUG)
|
||||||
settings.logger.addHandler(handler)
|
settings.logger.addHandler(handler)
|
||||||
ifc_clasher = IfcClasher(settings)
|
ifc_clasher = IfcClasher(settings)
|
||||||
for ab in ['a', 'b']:
|
with open(args.input, 'r') as clash_sets_file:
|
||||||
if not getattr(args, ab):
|
ifc_clasher.clash_sets = json.loads(clash_sets_file.read())
|
||||||
continue
|
|
||||||
getattr(ifc_clasher, ab).extend([{
|
|
||||||
'file': a,
|
|
||||||
'meshes': {},
|
|
||||||
'selector': '',
|
|
||||||
'mode': 'e'
|
|
||||||
} for i, a in enumerate(getattr(args, ab))])
|
|
||||||
|
|
||||||
if getattr(args, f'{ab}_selector'):
|
|
||||||
for i, selector in enumerate(getattr(args, f'{ab}_selector')):
|
|
||||||
getattr(ifc_clasher, ab)[i]['selector'] = selector
|
|
||||||
|
|
||||||
if getattr(args, f'{ab}_mode'):
|
|
||||||
for i, mode in enumerate(getattr(args, f'{ab}_mode')):
|
|
||||||
getattr(ifc_clasher, ab)[i]['mode'] = mode
|
|
||||||
ifc_clasher.tolerance = args.tolerance
|
|
||||||
ifc_clasher.clash()
|
ifc_clasher.clash()
|
||||||
ifc_clasher.export()
|
ifc_clasher.export()
|
||||||
|
|||||||
Reference in New Issue
Block a user