mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-10-02 04:59:52 +00:00
Geometry caching is now enabled for model importing
This commit is contained in:
@@ -41,6 +41,7 @@ class IfcExporter:
|
|||||||
def export(self):
|
def export(self):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
self.set_header()
|
self.set_header()
|
||||||
|
IfcStore.update_cache()
|
||||||
if bpy.context.scene.BIMProjectProperties.is_authoring:
|
if bpy.context.scene.BIMProjectProperties.is_authoring:
|
||||||
self.sync_deletions()
|
self.sync_deletions()
|
||||||
self.sync_object_placements()
|
self.sync_object_placements()
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
import os
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
import uuid
|
import uuid
|
||||||
|
import hashlib
|
||||||
import zipfile
|
import zipfile
|
||||||
import tempfile
|
import tempfile
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
@@ -30,6 +31,8 @@ class IfcStore:
|
|||||||
path = ""
|
path = ""
|
||||||
file = None
|
file = None
|
||||||
schema = None
|
schema = None
|
||||||
|
cache = None
|
||||||
|
cache_path = None
|
||||||
id_map = {}
|
id_map = {}
|
||||||
guid_map = {}
|
guid_map = {}
|
||||||
deleted_ids = set()
|
deleted_ids = set()
|
||||||
@@ -50,6 +53,8 @@ class IfcStore:
|
|||||||
IfcStore.path = ""
|
IfcStore.path = ""
|
||||||
IfcStore.file = None
|
IfcStore.file = None
|
||||||
IfcStore.schema = None
|
IfcStore.schema = None
|
||||||
|
IfcStore.cache = None
|
||||||
|
IfcStore.cache_path = None
|
||||||
IfcStore.id_map = {}
|
IfcStore.id_map = {}
|
||||||
IfcStore.guid_map = {}
|
IfcStore.guid_map = {}
|
||||||
IfcStore.deleted_ids = set()
|
IfcStore.deleted_ids = set()
|
||||||
@@ -72,6 +77,27 @@ class IfcStore:
|
|||||||
pass
|
pass
|
||||||
return IfcStore.file
|
return IfcStore.file
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_cache():
|
||||||
|
if IfcStore.cache is None and IfcStore.path:
|
||||||
|
ifc_key = IfcStore.path + IfcStore.file.wrapped_data.header.file_name.time_stamp
|
||||||
|
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
|
||||||
|
IfcStore.cache_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", f"{ifc_hash}.h5")
|
||||||
|
cache_settings = ifcopenshell.geom.settings()
|
||||||
|
IfcStore.cache = ifcopenshell.geom.serializers.hdf5(IfcStore.cache_path, cache_settings)
|
||||||
|
return IfcStore.cache
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_cache():
|
||||||
|
if not IfcStore.cache:
|
||||||
|
return
|
||||||
|
ifc_key = IfcStore.path + IfcStore.file.wrapped_data.header.file_name.time_stamp
|
||||||
|
ifc_hash = hashlib.md5(ifc_key.encode("utf-8")).hexdigest()
|
||||||
|
new_cache_path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "cache", f"{ifc_hash}.h5")
|
||||||
|
os.replace(IfcStore.cache_path, new_cache_path)
|
||||||
|
IfcStore.cache = None
|
||||||
|
IfcStore.get_cache()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_file(path):
|
def load_file(path):
|
||||||
extension = path.split(".")[-1]
|
extension = path.split(".")[-1]
|
||||||
|
|||||||
@@ -625,6 +625,7 @@ class IfcImporter:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
iterator = ifcopenshell.geom.iterator(self.settings, self.file, include=products)
|
iterator = ifcopenshell.geom.iterator(self.settings, self.file, include=products)
|
||||||
|
iterator.set_cache(IfcStore.get_cache())
|
||||||
valid_file = iterator.initialize()
|
valid_file = iterator.initialize()
|
||||||
if not valid_file:
|
if not valid_file:
|
||||||
return results
|
return results
|
||||||
@@ -770,6 +771,7 @@ class IfcImporter:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
iterator = ifcopenshell.geom.iterator(self.settings_2d, self.file, include=products)
|
iterator = ifcopenshell.geom.iterator(self.settings_2d, self.file, include=products)
|
||||||
|
iterator.set_cache(IfcStore.get_cache())
|
||||||
valid_file = iterator.initialize()
|
valid_file = iterator.initialize()
|
||||||
if not valid_file:
|
if not valid_file:
|
||||||
return results
|
return results
|
||||||
|
|||||||
@@ -335,16 +335,12 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
elements = set(ifc.by_type("IfcElement")) - set(ifc.by_type("IfcOpeningElement"))
|
elements = set(ifc.by_type("IfcElement")) - set(ifc.by_type("IfcOpeningElement"))
|
||||||
|
|
||||||
self.setup_serialiser(ifc)
|
self.setup_serialiser(ifc)
|
||||||
cache_settings = ifcopenshell.geom.settings(
|
cache_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True)
|
||||||
APPLY_DEFAULT_MATERIALS=True, DISABLE_TRIANGULATION=True, SEW_SHELLS=True
|
cache = IfcStore.get_cache()
|
||||||
)
|
|
||||||
cache = ifcopenshell.geom.serializers.hdf5(ifc_cache_path, cache_settings)
|
|
||||||
[cache.remove(guid) for guid in invalidated_guids]
|
[cache.remove(guid) for guid in invalidated_guids]
|
||||||
|
|
||||||
if target_view_contexts and elements:
|
if target_view_contexts and elements:
|
||||||
geom_settings = ifcopenshell.geom.settings(
|
geom_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True, INCLUDE_CURVES=True)
|
||||||
APPLY_DEFAULT_MATERIALS=True, DISABLE_TRIANGULATION=True, SEW_SHELLS=True, INCLUDE_CURVES=True
|
|
||||||
)
|
|
||||||
geom_settings.set_context_ids(target_view_contexts)
|
geom_settings.set_context_ids(target_view_contexts)
|
||||||
it = ifcopenshell.geom.iterator(geom_settings, ifc, multiprocessing.cpu_count(), include=elements)
|
it = ifcopenshell.geom.iterator(geom_settings, ifc, multiprocessing.cpu_count(), include=elements)
|
||||||
it.set_cache(cache)
|
it.set_cache(cache)
|
||||||
@@ -356,7 +352,7 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
|
|
||||||
if body_contexts and elements:
|
if body_contexts and elements:
|
||||||
geom_settings = ifcopenshell.geom.settings(
|
geom_settings = ifcopenshell.geom.settings(
|
||||||
APPLY_DEFAULT_MATERIALS=True, DISABLE_TRIANGULATION=True, SEW_SHELLS=True
|
DISABLE_TRIANGULATION=True,
|
||||||
)
|
)
|
||||||
geom_settings.set_context_ids(body_contexts)
|
geom_settings.set_context_ids(body_contexts)
|
||||||
it = ifcopenshell.geom.iterator(geom_settings, ifc, multiprocessing.cpu_count(), include=elements)
|
it = ifcopenshell.geom.iterator(geom_settings, ifc, multiprocessing.cpu_count(), include=elements)
|
||||||
@@ -365,7 +361,7 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
self.serialiser.write(elem)
|
self.serialiser.write(elem)
|
||||||
|
|
||||||
geom_settings = ifcopenshell.geom.settings(
|
geom_settings = ifcopenshell.geom.settings(
|
||||||
APPLY_DEFAULT_MATERIALS=True, DISABLE_TRIANGULATION=True, SEW_SHELLS=True
|
DISABLE_TRIANGULATION=True,
|
||||||
)
|
)
|
||||||
it = ifcopenshell.geom.iterator(geom_settings, ifc, include=[self.camera_element])
|
it = ifcopenshell.geom.iterator(geom_settings, ifc, include=[self.camera_element])
|
||||||
for elem in self.yield_from_iterator(it):
|
for elem in self.yield_from_iterator(it):
|
||||||
@@ -383,9 +379,7 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
return svg_path
|
return svg_path
|
||||||
|
|
||||||
def setup_serialiser(self, ifc):
|
def setup_serialiser(self, ifc):
|
||||||
self.svg_settings = ifcopenshell.geom.settings(
|
self.svg_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True, INCLUDE_CURVES=True)
|
||||||
APPLY_DEFAULT_MATERIALS=True, DISABLE_TRIANGULATION=True, INCLUDE_CURVES=True
|
|
||||||
)
|
|
||||||
self.svg_buffer = ifcopenshell.geom.serializers.buffer()
|
self.svg_buffer = ifcopenshell.geom.serializers.buffer()
|
||||||
self.serialiser = ifcopenshell.geom.serializers.svg(self.svg_buffer, self.svg_settings)
|
self.serialiser = ifcopenshell.geom.serializers.svg(self.svg_buffer, self.svg_settings)
|
||||||
self.serialiser.setFile(ifc)
|
self.serialiser.setFile(ifc)
|
||||||
|
|||||||
@@ -45,10 +45,13 @@ class AuthoringData:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def relating_types(cls):
|
def relating_types(cls):
|
||||||
if not cls.ifc_classes():
|
ifc_classes = cls.ifc_classes()
|
||||||
|
if not ifc_classes:
|
||||||
return []
|
return []
|
||||||
results = []
|
results = []
|
||||||
ifc_class = bpy.context.scene.BIMModelProperties.ifc_class
|
ifc_class = bpy.context.scene.BIMModelProperties.ifc_class
|
||||||
|
if not ifc_class and ifc_classes:
|
||||||
|
ifc_class = ifc_classes[0][0]
|
||||||
if ifc_class:
|
if ifc_class:
|
||||||
elements = [(str(e.id()), e.Name, "") for e in tool.Ifc.get().by_type(ifc_class)]
|
elements = [(str(e.id()), e.Name, "") for e in tool.Ifc.get().by_type(ifc_class)]
|
||||||
results.extend(sorted(elements, key=lambda s: s[1]))
|
results.extend(sorted(elements, key=lambda s: s[1]))
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import blenderbim.core.style
|
|||||||
def edit_object_placement(ifc, geometry, surveyor, obj=None):
|
def edit_object_placement(ifc, geometry, surveyor, obj=None):
|
||||||
element = ifc.get_entity(obj)
|
element = ifc.get_entity(obj)
|
||||||
if element:
|
if element:
|
||||||
|
geometry.clear_cache(element)
|
||||||
geometry.clear_scale(obj)
|
geometry.clear_scale(obj)
|
||||||
ifc.run("geometry.edit_object_placement", product=element, matrix=surveyor.get_absolute_matrix(obj))
|
ifc.run("geometry.edit_object_placement", product=element, matrix=surveyor.get_absolute_matrix(obj))
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ class Context:
|
|||||||
@interface
|
@interface
|
||||||
class Geometry:
|
class Geometry:
|
||||||
def change_object_data(cls, obj, data, is_global=False): pass
|
def change_object_data(cls, obj, data, is_global=False): pass
|
||||||
|
def clear_cache(cls, element): pass
|
||||||
def clear_modifiers(cls, obj): pass
|
def clear_modifiers(cls, obj): pass
|
||||||
def clear_scale(cls, obj): pass
|
def clear_scale(cls, obj): pass
|
||||||
def create_dynamic_voids(cls, obj): pass
|
def create_dynamic_voids(cls, obj): pass
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ class Geometry(blenderbim.core.tool.Geometry):
|
|||||||
else:
|
else:
|
||||||
obj.data = data
|
obj.data = data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clear_cache(cls, element):
|
||||||
|
IfcStore.get_cache().remove(element.GlobalId)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def clear_modifiers(cls, obj):
|
def clear_modifiers(cls, obj):
|
||||||
for modifier in obj.modifiers:
|
for modifier in obj.modifiers:
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ from test.core.bootstrap import ifc, surveyor, geometry, style
|
|||||||
class TestEditObjectPlacement:
|
class TestEditObjectPlacement:
|
||||||
def predict(self, ifc, geometry, surveyor):
|
def predict(self, ifc, geometry, surveyor):
|
||||||
ifc.get_entity("obj").should_be_called().will_return("element")
|
ifc.get_entity("obj").should_be_called().will_return("element")
|
||||||
|
geometry.clear_cache("element").should_be_called()
|
||||||
geometry.clear_scale("obj").should_be_called()
|
geometry.clear_scale("obj").should_be_called()
|
||||||
surveyor.get_absolute_matrix("obj").should_be_called().will_return("matrix")
|
surveyor.get_absolute_matrix("obj").should_be_called().will_return("matrix")
|
||||||
ifc.run("geometry.edit_object_placement", product="element", matrix="matrix").should_be_called()
|
ifc.run("geometry.edit_object_placement", product="element", matrix="matrix").should_be_called()
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ class TestChangeObjectData(NewFile):
|
|||||||
assert obj2.data == data2
|
assert obj2.data == data2
|
||||||
|
|
||||||
|
|
||||||
|
class TestClearCache(NewFile):
|
||||||
|
def test_nothing(self):
|
||||||
|
pass # TODO I don't know how to test this
|
||||||
|
|
||||||
|
|
||||||
class TestClearModifiers(NewFile):
|
class TestClearModifiers(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||||
|
|||||||
Reference in New Issue
Block a user