generate_demo_library - sync guid on regeneration

This commit is contained in:
Andrej730
2025-03-27 11:42:51 +05:00
parent 013d7f6659
commit fdb283b7af
3 changed files with 99 additions and 5 deletions
@@ -1,7 +1,7 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1');
FILE_NAME('/dev/null','2024-03-03T16:03:58+11:00',(),(),'IfcOpenShell v0.7.0-eaa2aa05a','IfcOpenShell v0.7.0-eaa2aa05a','Nobody');
FILE_NAME('/dev/null','2025-03-27T16:04:10+05:00',(''),(''),'IfcOpenShell 0.0.0','IfcOpenShell 0.0.0','Nobody');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
@@ -11,7 +11,7 @@ DATA;
#4=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
#5=IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.);
#6=IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.);
#7=IFCUNITASSIGNMENT((#6,#4,#5));
#7=IFCUNITASSIGNMENT((#4,#5,#6));
#8=IFCCARTESIANPOINT((0.,0.,0.));
#9=IFCDIRECTION((0.,0.,1.));
#10=IFCDIRECTION((1.,0.,0.));
@@ -29,7 +29,7 @@ DATA;
#22=IFCMATERIALLAYERSET((#24),$,$);
#23=IFCRELASSOCIATESMATERIAL('0khP_Smdj12f2JrOTRbRKj',$,$,$,(#21),#22);
#24=IFCMATERIALLAYER(#20,0.05,$,$,$,$,$);
#25=IFCRELDECLARES('31JriXdev6BP2sC9lcxdC1',$,$,$,#2,(#26,#48,#72,#59,#92,#30,#96,#63,#21,#54,#87,#210,#153,#34,#67,#38,#82,#77,#42));
#25=IFCRELDECLARES('31JriXdev6BP2sC9lcxdC1',$,$,$,#2,(#96,#77,#63,#42,#21,#54,#87,#153,#210,#34,#67,#26,#48,#72,#59,#92,#38,#82,#30));
#26=IFCWALLTYPE('3R5onkTtX1IxdCMPoThRaw',$,'WAL100',$,$,$,$,$,$,.NOTDEFINED.);
#27=IFCMATERIALLAYERSET((#29),$,$);
#28=IFCRELASSOCIATESMATERIAL('0$mXRPCT16nP6Q70XFNvZv',$,$,$,(#26),#27);
Binary file not shown.
+96 -2
View File
@@ -32,13 +32,104 @@ import bonsai.tool as tool
from pathlib import Path
def sync_guids(target_file: ifcopenshell.file, source_file: ifcopenshell.file) -> None:
def sync_guid(element: ifcopenshell.entity_instance) -> None:
"""Try to sync `element` guid with `source_file`.
Sync is assigning guid based on the first element from `source_file` that has a matching type and name.
"""
if element.is_a("IfcTypeProduct") or element.is_a("IfcContext"):
assert element.Name, element
elements = source_file.by_type(element.is_a(), False)
for element_ in elements:
if element.Name == element_.Name:
element.GlobalId = element_.GlobalId
return
print(
f"WARNING! Couldn't find a matching element in the GUID source to sync guid for '{element.Name}' ({element})."
)
def sync_pset(pset: ifcopenshell.entity_instance) -> None:
element_types = pset.DefinesType
assert len(element_types) == 1
element_type: ifcopenshell.entity_instance = element_types[0]
try:
element_type_ = source_file.by_guid(element_type.GlobalId)
except RuntimeError:
# New type with pset was added.
print(f"WARNING! Couldn't find a matching pset for '{pset.Name}' ({pset}).")
return
assert len(element_type_.HasPropertySets) == 1
pset_ = element_type_.HasPropertySets[0]
assert pset.Name == pset_.Name
pset.GlobalId = pset_.GlobalId
def sync_rel(rel: ifcopenshell.entity_instance) -> None:
if rel.is_a("IfcRelAssociatesMaterial"):
related_objects = rel.RelatedObjects
assert len(related_objects) == 1
related_object = related_objects[0]
try:
related_object_ = source_file.by_guid(related_object.GlobalId)
except RuntimeError:
# New type with material was added.
print(f"WARNING! Couldn't find a matching rel for '{rel}'.")
return
rel_ = next((r for r in related_object_.HasAssociations if r.is_a("IfcRelAssociatesMaterial")))
assert rel_
rel.GlobalId = rel_.GlobalId
elif rel.is_a("IfcRelDeclares"):
context = rel.RelatingContext
try:
context_ = source_file.by_guid(context.GlobalId)
except RuntimeError:
# New context was added.
print(f"WARNING! Couldn't find a matching context for '{context}'.")
return
rel_ = context_.Declares[0]
rel.GlobalId = rel_.GlobalId
else:
print(f"WARNING! Couldn't find a matching element in the GUID source to sync guid for '{rel}'.")
roots = target_file.by_type("IfcRoot")
psets: list[ifcopenshell.entity_instance] = []
rels: list[ifcopenshell.entity_instance] = []
for element in roots:
if element.is_a("IfcPropertySet"):
psets.append(element)
continue
elif element.is_a("IfcRelationship"):
rels.append(element)
continue
sync_guid(element)
for rel in rels:
sync_rel(rel)
for pset in psets:
sync_pset(pset)
class LibraryGenerator:
output_filename = "Demo Library.ifc"
guid_source: ifcopenshell.file
"""Guid sync is needed to avoid unnecessary diffs
and to avoid `append_asset` from identifying existing elements and as new ones.
"""
def generate(self) -> None:
assert bpy.context.blend_data
opened_blend_file = Path(bpy.context.blend_data.filepath)
assert (
opened_blend_file.name == "demo-library.blend"
), "This script must be run from the demo-library.blend file as it's using Blender objects to create representations."
libraries_path = opened_blend_file.parent.parent / "bonsai" / "bim" / "data" / "libraries"
guid_source_filepath = libraries_path / f"IFC4 {self.output_filename}"
self.guid_source = ifcopenshell.open(guid_source_filepath)
ifcopenshell.api.pre_listeners = {}
ifcopenshell.api.post_listeners = {}
@@ -48,6 +139,7 @@ class LibraryGenerator:
# Basic project setup.
self.project = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcProject", name="Bonsai Demo")
self.library = ifcopenshell.api.root.create_entity(
self.file, ifc_class="IfcProjectLibrary", name="Bonsai Demo Library"
)
@@ -188,7 +280,8 @@ class LibraryGenerator:
self.create_text_type("TYPE-TAG", "capsule-tag", ["{{type.Name}}"])
self.create_text_type("NAME-TAG", "capsule-tag", ["{{Name}}"])
self.file.write("IFC4 Demo Library.ifc")
sync_guids(self.file, self.guid_source)
self.file.write(libraries_path / f"{self.file.schema} {self.output_filename}")
def create_symbol_type(self, name: str, symbol: str) -> None:
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcTypeProduct", name=name)
@@ -288,4 +381,5 @@ class LibraryGenerator:
ifcopenshell.api.project.assign_declaration(self.file, definitions=[element], relating_context=self.library)
LibraryGenerator().generate()
if __name__ == "__main__":
LibraryGenerator().generate()