2020-05-07 16:57:14 +10:00
|
|
|
import bpy
|
2021-06-30 18:34:12 +10:00
|
|
|
import uuid
|
2020-05-07 16:57:14 +10:00
|
|
|
import ifcopenshell
|
2021-02-23 19:19:06 +11:00
|
|
|
import blenderbim.bim.handler
|
2020-05-07 16:57:14 +10:00
|
|
|
|
2020-11-01 20:08:48 +07:00
|
|
|
|
|
|
|
|
class IfcStore:
|
|
|
|
|
path = ""
|
2020-03-30 12:47:14 +11:00
|
|
|
file = None
|
2021-01-07 10:47:50 +11:00
|
|
|
schema = None
|
2021-02-03 13:47:09 +11:00
|
|
|
id_map = {}
|
|
|
|
|
guid_map = {}
|
2021-03-15 20:32:47 +11:00
|
|
|
edited_objs = set()
|
2020-11-01 20:08:48 +07:00
|
|
|
pset_template_path = ""
|
2020-04-24 18:05:59 +10:00
|
|
|
pset_template_file = None
|
2021-04-20 12:45:20 +10:00
|
|
|
library_path = ""
|
|
|
|
|
library_file = None
|
2021-05-31 17:43:42 +10:00
|
|
|
element_listeners = set()
|
2021-07-02 19:10:51 +10:00
|
|
|
current_transaction = ""
|
2021-06-30 18:34:12 +10:00
|
|
|
last_transaction = ""
|
|
|
|
|
history = []
|
|
|
|
|
future = []
|
2020-05-07 16:57:14 +10:00
|
|
|
|
2021-04-22 18:37:41 +10:00
|
|
|
@staticmethod
|
|
|
|
|
def purge():
|
|
|
|
|
IfcStore.path = ""
|
|
|
|
|
IfcStore.file = None
|
|
|
|
|
IfcStore.schema = None
|
|
|
|
|
IfcStore.id_map = {}
|
|
|
|
|
IfcStore.guid_map = {}
|
|
|
|
|
IfcStore.edited_objs = set()
|
|
|
|
|
IfcStore.pset_template_path = ""
|
|
|
|
|
IfcStore.pset_template_file = None
|
|
|
|
|
IfcStore.library_path = ""
|
|
|
|
|
IfcStore.library_file = None
|
|
|
|
|
|
2020-05-07 16:57:14 +10:00
|
|
|
@staticmethod
|
|
|
|
|
def get_file():
|
|
|
|
|
if IfcStore.file is None:
|
|
|
|
|
IfcStore.path = bpy.context.scene.BIMProperties.ifc_file
|
2020-12-30 13:50:10 +11:00
|
|
|
if IfcStore.path:
|
2021-02-03 19:15:25 +11:00
|
|
|
try:
|
|
|
|
|
IfcStore.file = ifcopenshell.open(IfcStore.path)
|
|
|
|
|
except:
|
|
|
|
|
IfcStore.file
|
2020-05-07 16:57:14 +10:00
|
|
|
return IfcStore.file
|
2021-01-07 10:47:50 +11:00
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def get_schema():
|
|
|
|
|
if IfcStore.file is None:
|
|
|
|
|
return
|
|
|
|
|
elif IfcStore.schema is None:
|
|
|
|
|
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema)
|
|
|
|
|
return IfcStore.schema
|
2021-02-03 13:47:09 +11:00
|
|
|
|
|
|
|
|
@staticmethod
|
2021-05-30 21:02:03 +10:00
|
|
|
def get_element(id_or_guid):
|
|
|
|
|
if isinstance(id_or_guid, int):
|
|
|
|
|
map_object = IfcStore.id_map
|
|
|
|
|
else:
|
|
|
|
|
map_object = IfcStore.guid_map
|
|
|
|
|
try:
|
|
|
|
|
obj = map_object[id_or_guid]
|
|
|
|
|
obj.type # In case the object has been deleted, this triggers an exception
|
|
|
|
|
except:
|
|
|
|
|
return
|
|
|
|
|
return obj
|
|
|
|
|
|
2021-05-31 17:43:42 +10:00
|
|
|
@staticmethod
|
|
|
|
|
def add_element_listener(callback):
|
|
|
|
|
IfcStore.element_listeners.add(callback)
|
|
|
|
|
|
2021-06-30 18:34:12 +10:00
|
|
|
@staticmethod
|
|
|
|
|
def reload_linked_elements(should_reload_selected=False):
|
|
|
|
|
file = IfcStore.get_file()
|
|
|
|
|
if not file:
|
|
|
|
|
return
|
|
|
|
|
if should_reload_selected:
|
2021-07-01 20:43:16 +10:00
|
|
|
objects = bpy.context.selected_objects
|
|
|
|
|
if bpy.context.active_object:
|
|
|
|
|
objects += [bpy.context.active_object]
|
2021-06-30 18:34:12 +10:00
|
|
|
else:
|
|
|
|
|
objects = bpy.data.objects
|
2021-07-03 10:41:06 +10:00
|
|
|
|
|
|
|
|
for obj in objects:
|
|
|
|
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
|
|
|
|
continue
|
|
|
|
|
element = file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
|
|
|
|
data = {"id": element.id(), "obj": obj.name}
|
|
|
|
|
if hasattr(element, "GlobalId"):
|
|
|
|
|
data["guid"] = element.GlobalId
|
|
|
|
|
IfcStore.commit_link_element(data)
|
2021-06-30 18:34:12 +10:00
|
|
|
|
2021-05-30 21:02:03 +10:00
|
|
|
@staticmethod
|
2021-02-03 13:47:09 +11:00
|
|
|
def link_element(element, obj):
|
|
|
|
|
IfcStore.id_map[element.id()] = obj
|
2021-02-06 15:14:38 +11:00
|
|
|
if hasattr(element, "GlobalId"):
|
|
|
|
|
IfcStore.guid_map[element.GlobalId] = obj
|
2021-02-03 13:47:09 +11:00
|
|
|
obj.BIMObjectProperties.ifc_definition_id = element.id()
|
2021-02-23 19:19:06 +11:00
|
|
|
blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback)
|
2021-02-23 21:15:27 +11:00
|
|
|
blenderbim.bim.handler.subscribe_to(obj, "name", blenderbim.bim.handler.name_callback)
|
2021-05-31 17:43:42 +10:00
|
|
|
for listener in IfcStore.element_listeners:
|
|
|
|
|
listener(element, obj)
|
2021-02-03 13:47:09 +11:00
|
|
|
|
2021-07-01 20:43:16 +10:00
|
|
|
if IfcStore.history:
|
|
|
|
|
data = {"id": element.id(), "guid": getattr(element, "GlobalId", None), "obj": obj.name}
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.history[-1]["operations"].append(
|
2021-07-01 20:43:16 +10:00
|
|
|
{"rollback": IfcStore.rollback_link_element, "commit": IfcStore.commit_link_element, "data": data}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def rollback_link_element(data):
|
|
|
|
|
del IfcStore.id_map[data["id"]]
|
|
|
|
|
if data["guid"]:
|
|
|
|
|
del IfcStore.guid_map[data["guid"]]
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def commit_link_element(data):
|
2021-07-02 19:10:51 +10:00
|
|
|
obj = bpy.data.objects.get(data["obj"])
|
|
|
|
|
IfcStore.id_map[data["id"]] = obj
|
2021-07-01 20:43:16 +10:00
|
|
|
if data["guid"]:
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.guid_map[data["guid"]] = obj
|
|
|
|
|
blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback)
|
|
|
|
|
blenderbim.bim.handler.subscribe_to(obj, "name", blenderbim.bim.handler.name_callback)
|
2021-07-01 20:43:16 +10:00
|
|
|
|
2021-02-03 13:47:09 +11:00
|
|
|
@staticmethod
|
2021-05-12 22:20:58 +10:00
|
|
|
def unlink_element(element=None, obj=None):
|
|
|
|
|
if element is None:
|
|
|
|
|
try:
|
|
|
|
|
element = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
|
2021-06-15 13:05:24 +10:00
|
|
|
try:
|
|
|
|
|
if element:
|
|
|
|
|
del IfcStore.id_map[element.id()]
|
|
|
|
|
else:
|
|
|
|
|
del IfcStore.id_map[obj.BIMObjectProperties.ifc_definition_id]
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2021-05-12 22:20:58 +10:00
|
|
|
|
2021-06-15 13:05:24 +10:00
|
|
|
try:
|
|
|
|
|
if element and hasattr(element, "GlobalId"):
|
|
|
|
|
del IfcStore.guid_map[element.GlobalId]
|
|
|
|
|
except:
|
|
|
|
|
pass
|
2021-05-12 22:20:58 +10:00
|
|
|
|
2021-02-03 13:47:09 +11:00
|
|
|
if obj:
|
|
|
|
|
obj.BIMObjectProperties.ifc_definition_id = 0
|
2021-06-30 18:34:12 +10:00
|
|
|
|
2021-07-01 20:43:16 +10:00
|
|
|
@staticmethod
|
|
|
|
|
def execute_ifc_operator(operator, context):
|
2021-07-02 19:10:51 +10:00
|
|
|
is_top_level_operator = not bool(IfcStore.current_transaction)
|
2021-07-01 20:43:16 +10:00
|
|
|
|
|
|
|
|
if is_top_level_operator:
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.begin_transaction(operator)
|
2021-07-01 20:43:16 +10:00
|
|
|
IfcStore.get_file().begin_transaction()
|
|
|
|
|
# This empty transaction ensures that each operator has at least one transaction
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.add_transaction_operation(operator, rollback=lambda data: True, commit=lambda data: True)
|
|
|
|
|
else:
|
|
|
|
|
operator.transaction_key = IfcStore.current_transaction
|
2021-07-01 20:43:16 +10:00
|
|
|
|
|
|
|
|
result = getattr(operator, "_execute")(context)
|
|
|
|
|
|
|
|
|
|
if is_top_level_operator:
|
|
|
|
|
IfcStore.get_file().end_transaction()
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.add_transaction_operation(
|
2021-07-01 20:43:16 +10:00
|
|
|
operator, rollback=IfcStore.rollback_ifc_operator, commit=IfcStore.commit_ifc_operator
|
|
|
|
|
)
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.end_transaction(operator)
|
2021-07-01 20:43:16 +10:00
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def rollback_ifc_operator(data):
|
|
|
|
|
IfcStore.get_file().undo()
|
|
|
|
|
blenderbim.bim.handler.purge_module_data()
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def commit_ifc_operator(data):
|
|
|
|
|
IfcStore.get_file().redo()
|
|
|
|
|
blenderbim.bim.handler.purge_module_data()
|
|
|
|
|
|
2021-06-30 18:34:12 +10:00
|
|
|
@staticmethod
|
2021-07-02 19:10:51 +10:00
|
|
|
def begin_transaction(operator):
|
|
|
|
|
IfcStore.current_transaction = str(uuid.uuid4())
|
|
|
|
|
operator.transaction_key = IfcStore.current_transaction
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def end_transaction(operator):
|
|
|
|
|
IfcStore.current_transaction = ""
|
|
|
|
|
operator.transaction_key = ""
|
2021-06-30 18:34:12 +10:00
|
|
|
|
|
|
|
|
@staticmethod
|
2021-07-02 19:10:51 +10:00
|
|
|
def add_transaction_operation(operator, rollback=None, commit=None):
|
2021-06-30 18:34:12 +10:00
|
|
|
key = getattr(operator, "transaction_key", None)
|
|
|
|
|
data = getattr(operator, "transaction_data", None)
|
|
|
|
|
bpy.context.scene.BIMProperties.last_transaction = key
|
|
|
|
|
IfcStore.last_transaction = key
|
|
|
|
|
rollback = rollback or getattr(operator, "rollback", lambda data: True)
|
|
|
|
|
commit = commit or getattr(operator, "commit", lambda data: True)
|
|
|
|
|
if IfcStore.history and IfcStore.history[-1]["key"] == key:
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.history[-1]["operations"].append({"rollback": rollback, "commit": commit, "data": data})
|
2021-06-30 18:34:12 +10:00
|
|
|
else:
|
|
|
|
|
IfcStore.history.append(
|
2021-07-02 19:10:51 +10:00
|
|
|
{"key": key, "operations": [{"rollback": rollback, "commit": commit, "data": data}]}
|
2021-06-30 18:34:12 +10:00
|
|
|
)
|
|
|
|
|
IfcStore.future = []
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def undo():
|
|
|
|
|
if not IfcStore.history:
|
|
|
|
|
return
|
|
|
|
|
event = IfcStore.history.pop()
|
2021-07-02 19:10:51 +10:00
|
|
|
for transaction in event["operations"][::-1]:
|
2021-06-30 18:34:12 +10:00
|
|
|
transaction["rollback"](transaction["data"])
|
|
|
|
|
IfcStore.future.append(event)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def redo():
|
|
|
|
|
if not IfcStore.future:
|
|
|
|
|
return
|
|
|
|
|
event = IfcStore.future.pop()
|
2021-07-02 19:10:51 +10:00
|
|
|
for transaction in event["operations"]:
|
2021-06-30 18:34:12 +10:00
|
|
|
transaction["commit"](transaction["data"])
|
|
|
|
|
IfcStore.history.append(event)
|