2021-08-17 08:55:29 +10:00
|
|
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
|
|
|
|
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
|
|
|
|
#
|
|
|
|
|
# This file is part of BlenderBIM Add-on.
|
|
|
|
|
#
|
|
|
|
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
# (at your option) any later version.
|
|
|
|
|
#
|
|
|
|
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
|
#
|
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
2021-09-11 20:29:14 +10:00
|
|
|
import os
|
2021-01-05 21:15:52 +11:00
|
|
|
import bpy
|
2021-09-11 20:29:14 +10:00
|
|
|
import time
|
2021-01-20 16:53:34 +11:00
|
|
|
import logging
|
2021-09-11 20:29:14 +10:00
|
|
|
import tempfile
|
2021-01-05 21:15:52 +11:00
|
|
|
import ifcopenshell
|
2021-03-27 22:52:52 +11:00
|
|
|
import ifcopenshell.api
|
2021-09-12 19:19:53 +10:00
|
|
|
import ifcopenshell.util.selector
|
2021-06-30 18:34:12 +10:00
|
|
|
import ifcopenshell.util.representation
|
2021-06-18 15:54:01 +10:00
|
|
|
import blenderbim.bim.handler
|
2021-09-30 18:28:50 +10:00
|
|
|
import blenderbim.tool as tool
|
2021-10-05 14:52:41 +11:00
|
|
|
import blenderbim.core.context
|
|
|
|
|
import blenderbim.core.owner
|
2021-01-05 21:15:52 +11:00
|
|
|
from blenderbim.bim.ifc import IfcStore
|
2021-04-21 16:45:11 +10:00
|
|
|
from blenderbim.bim import import_ifc
|
2021-10-13 10:49:28 +11:00
|
|
|
from blenderbim.bim import export_ifc
|
2021-10-18 19:23:40 +11:00
|
|
|
from ifcopenshell.api.context.data import Data as ContextData
|
2021-01-05 21:15:52 +11:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class CreateProject(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.create_project"
|
|
|
|
|
bl_label = "Create Project"
|
2021-06-30 18:34:12 +10:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-10-13 00:03:36 +02:00
|
|
|
bl_description = "Create a new IFC project"
|
2021-01-05 21:15:52 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.begin_transaction(self)
|
|
|
|
|
IfcStore.add_transaction_operation(self, rollback=self.rollback, commit=lambda data: True)
|
2021-07-01 20:43:16 +10:00
|
|
|
result = self._execute(context)
|
|
|
|
|
self.transaction_data = {"file": self.file}
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.add_transaction_operation(self, rollback=lambda data: True, commit=self.commit)
|
|
|
|
|
IfcStore.end_transaction(self)
|
2021-07-01 20:43:16 +10:00
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def _execute(self, context):
|
2021-08-19 22:29:43 +02:00
|
|
|
active_object = context.view_layer.objects.active
|
2021-01-05 21:15:52 +11:00
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
if self.file:
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2021-03-27 22:52:52 +11:00
|
|
|
IfcStore.file = ifcopenshell.api.run(
|
2021-07-29 23:08:17 +02:00
|
|
|
"project.create_file", **{"version": context.scene.BIMProperties.export_schema}
|
2021-03-27 22:52:52 +11:00
|
|
|
)
|
2021-01-05 21:15:52 +11:00
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
|
2021-10-29 18:04:17 +11:00
|
|
|
if self.file.schema == "IFC2X3":
|
|
|
|
|
person = blenderbim.core.owner.add_person(tool.Ifc)
|
|
|
|
|
organisation = blenderbim.core.owner.add_organisation(tool.Ifc)
|
|
|
|
|
user = blenderbim.core.owner.add_person_and_organisation(tool.Ifc, person=person, organisation=organisation)
|
|
|
|
|
blenderbim.core.owner.set_user(tool.Owner, user=user)
|
2021-01-26 13:56:38 +11:00
|
|
|
|
2021-07-28 18:43:20 +10:00
|
|
|
project = bpy.data.objects.new(self.get_name("IfcProject", "My Project"), None)
|
|
|
|
|
site = bpy.data.objects.new(self.get_name("IfcSite", "My Site"), None)
|
|
|
|
|
building = bpy.data.objects.new(self.get_name("IfcBuilding", "My Building"), None)
|
|
|
|
|
building_storey = bpy.data.objects.new(self.get_name("IfcBuildingStorey", "My Storey"), None)
|
2021-01-05 21:15:52 +11:00
|
|
|
|
2021-07-02 19:10:51 +10:00
|
|
|
bpy.ops.bim.assign_class(obj=project.name, ifc_class="IfcProject")
|
2021-10-12 18:31:18 +11:00
|
|
|
blenderbim.core.unit.assign_scene_units(tool.Ifc, tool.Unit)
|
2021-09-30 18:28:50 +10:00
|
|
|
|
2021-10-05 14:52:41 +11:00
|
|
|
model = blenderbim.core.context.add_context(
|
2021-09-30 18:28:50 +10:00
|
|
|
tool.Ifc, context_type="Model", context_identifier="", target_view="", parent=0
|
|
|
|
|
)
|
2021-10-18 19:23:40 +11:00
|
|
|
body_context = blenderbim.core.context.add_context(
|
2021-09-30 18:28:50 +10:00
|
|
|
tool.Ifc, context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model
|
|
|
|
|
)
|
2021-10-05 14:52:41 +11:00
|
|
|
blenderbim.core.context.add_context(
|
2021-09-30 18:28:50 +10:00
|
|
|
tool.Ifc, context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent=model
|
|
|
|
|
)
|
2021-10-05 14:52:41 +11:00
|
|
|
plan = blenderbim.core.context.add_context(
|
|
|
|
|
tool.Ifc, context_type="Plan", context_identifier="", target_view="", parent=0
|
|
|
|
|
)
|
|
|
|
|
blenderbim.core.context.add_context(
|
2021-09-30 18:28:50 +10:00
|
|
|
tool.Ifc, context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent=plan
|
|
|
|
|
)
|
2021-01-06 15:19:55 +11:00
|
|
|
|
2021-10-18 19:23:40 +11:00
|
|
|
ContextData.load(tool.Ifc.get())
|
|
|
|
|
context.scene.BIMProperties.contexts = str(body_context.id())
|
2021-01-06 15:19:55 +11:00
|
|
|
|
2021-07-02 19:10:51 +10:00
|
|
|
bpy.ops.bim.assign_class(obj=site.name, ifc_class="IfcSite")
|
|
|
|
|
bpy.ops.bim.assign_class(obj=building.name, ifc_class="IfcBuilding")
|
|
|
|
|
bpy.ops.bim.assign_class(obj=building_storey.name, ifc_class="IfcBuildingStorey")
|
2021-10-06 12:50:46 +11:00
|
|
|
|
|
|
|
|
blenderbim.core.aggregate.assign_object(
|
2021-10-07 18:51:50 +11:00
|
|
|
tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=project, related_obj=site
|
2021-10-06 12:50:46 +11:00
|
|
|
)
|
|
|
|
|
blenderbim.core.aggregate.assign_object(
|
2021-10-07 18:51:50 +11:00
|
|
|
tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=site, related_obj=building
|
2021-10-06 12:50:46 +11:00
|
|
|
)
|
|
|
|
|
blenderbim.core.aggregate.assign_object(
|
2021-10-07 18:51:50 +11:00
|
|
|
tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=building, related_obj=building_storey
|
2021-10-06 12:50:46 +11:00
|
|
|
)
|
2021-06-30 18:34:12 +10:00
|
|
|
|
2021-08-19 22:29:43 +02:00
|
|
|
context.view_layer.objects.active = active_object
|
2021-01-05 21:15:52 +11:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2021-07-28 18:43:20 +10:00
|
|
|
def get_name(self, ifc_class, name):
|
|
|
|
|
if not bpy.data.objects.get(f"{ifc_class}/{name}"):
|
|
|
|
|
return name
|
|
|
|
|
i = 2
|
|
|
|
|
while bpy.data.objects.get(f"{ifc_class}/{name} {i}"):
|
|
|
|
|
i += 1
|
|
|
|
|
return f"{name} {i}"
|
|
|
|
|
|
2021-06-30 18:34:12 +10:00
|
|
|
def rollback(self, data):
|
|
|
|
|
IfcStore.file = None
|
|
|
|
|
|
|
|
|
|
def commit(self, data):
|
|
|
|
|
IfcStore.file = data["file"]
|
|
|
|
|
|
2021-01-20 16:53:34 +11:00
|
|
|
|
2021-04-20 12:45:20 +10:00
|
|
|
class SelectLibraryFile(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.select_library_file"
|
|
|
|
|
bl_label = "Select Library File"
|
2021-06-30 18:34:12 +10:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-10-21 01:10:54 +02:00
|
|
|
bl_description = "Select an IFC file that can be used as a library"
|
2021-04-20 12:45:20 +10:00
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
|
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-07-06 14:21:02 +10:00
|
|
|
IfcStore.begin_transaction(self)
|
2021-06-30 18:34:12 +10:00
|
|
|
old_filepath = IfcStore.library_path
|
2021-07-01 20:43:16 +10:00
|
|
|
result = self._execute(context)
|
|
|
|
|
self.transaction_data = {"old_filepath": old_filepath, "filepath": self.filepath}
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.add_transaction_operation(self)
|
2021-07-06 14:21:02 +10:00
|
|
|
IfcStore.end_transaction(self)
|
2021-07-01 20:43:16 +10:00
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def _execute(self, context):
|
2021-04-20 12:45:20 +10:00
|
|
|
IfcStore.library_path = self.filepath
|
|
|
|
|
IfcStore.library_file = ifcopenshell.open(self.filepath)
|
|
|
|
|
bpy.ops.bim.refresh_library()
|
2021-10-16 15:46:02 +11:00
|
|
|
if context.area:
|
|
|
|
|
context.area.tag_redraw()
|
2021-04-20 12:45:20 +10:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
context.window_manager.fileselect_add(self)
|
|
|
|
|
return {"RUNNING_MODAL"}
|
|
|
|
|
|
2021-06-30 18:34:12 +10:00
|
|
|
def rollback(self, data):
|
|
|
|
|
if data["old_filepath"]:
|
|
|
|
|
IfcStore.library_path = data["old_filepath"]
|
|
|
|
|
IfcStore.library_file = ifcopenshell.open(data["old_filepath"])
|
|
|
|
|
else:
|
|
|
|
|
IfcStore.library_path = ""
|
|
|
|
|
IfcStore.library_file = None
|
|
|
|
|
|
|
|
|
|
def commit(self, data):
|
|
|
|
|
IfcStore.library_path = data["filepath"]
|
|
|
|
|
IfcStore.library_file = ifcopenshell.open(data["filepath"])
|
|
|
|
|
|
2021-04-20 12:45:20 +10:00
|
|
|
|
|
|
|
|
class RefreshLibrary(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.refresh_library"
|
|
|
|
|
bl_label = "Refresh Library"
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
self.props = context.scene.BIMProjectProperties
|
|
|
|
|
|
2021-08-17 14:41:15 +02:00
|
|
|
self.props.library_elements.clear()
|
|
|
|
|
self.props.library_breadcrumb.clear()
|
2021-04-20 12:45:20 +10:00
|
|
|
|
|
|
|
|
self.props.active_library_element = ""
|
|
|
|
|
|
|
|
|
|
types = IfcStore.library_file.wrapped_data.types_with_super()
|
2021-08-23 09:38:24 +10:00
|
|
|
for importable_type in sorted(["IfcTypeProduct", "IfcMaterial", "IfcCostSchedule", "IfcProfileDef"]):
|
2021-08-09 11:17:10 +10:00
|
|
|
if importable_type in types:
|
|
|
|
|
new = self.props.library_elements.add()
|
|
|
|
|
new.name = importable_type
|
2021-04-20 12:45:20 +10:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChangeLibraryElement(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.change_library_element"
|
|
|
|
|
bl_label = "Change Library Element"
|
2021-06-30 18:34:12 +10:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-04-20 12:45:20 +10:00
|
|
|
element_name: bpy.props.StringProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
self.props = context.scene.BIMProjectProperties
|
2021-09-01 00:01:44 +02:00
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
self.library_file = IfcStore.library_file
|
2021-04-20 12:45:20 +10:00
|
|
|
ifc_classes = set()
|
|
|
|
|
self.props.active_library_element = self.element_name
|
|
|
|
|
crumb = self.props.library_breadcrumb.add()
|
|
|
|
|
crumb.name = self.element_name
|
2021-09-01 00:01:44 +02:00
|
|
|
elements = self.library_file.by_type(self.element_name)
|
2021-04-20 12:45:20 +10:00
|
|
|
[ifc_classes.add(e.is_a()) for e in elements]
|
2021-08-17 14:41:15 +02:00
|
|
|
self.props.library_elements.clear()
|
2021-06-18 16:05:58 +10:00
|
|
|
if len(ifc_classes) == 1 and list(ifc_classes)[0] == self.element_name:
|
2021-08-23 16:34:19 +10:00
|
|
|
for name, ifc_definition_id in sorted([(self.get_name(e), e.id()) for e in elements]):
|
2021-08-31 08:22:12 +10:00
|
|
|
self.add_library_asset(name, ifc_definition_id)
|
2021-04-20 12:45:20 +10:00
|
|
|
else:
|
2021-08-23 09:38:24 +10:00
|
|
|
for ifc_class in sorted(ifc_classes):
|
2021-08-31 08:22:12 +10:00
|
|
|
if ifc_class == self.element_name:
|
|
|
|
|
continue
|
2021-04-20 12:45:20 +10:00
|
|
|
new = self.props.library_elements.add()
|
|
|
|
|
new.name = ifc_class
|
2021-08-31 08:22:12 +10:00
|
|
|
for name, ifc_definition_id, ifc_class in sorted([(self.get_name(e), e.id(), e.is_a()) for e in elements]):
|
|
|
|
|
if ifc_class == self.element_name:
|
|
|
|
|
self.add_library_asset(name, ifc_definition_id)
|
2021-04-20 12:45:20 +10:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2021-08-23 09:38:24 +10:00
|
|
|
def get_name(self, element):
|
|
|
|
|
if element.is_a("IfcProfileDef"):
|
|
|
|
|
return element.ProfileName or "Unnamed"
|
|
|
|
|
return element.Name or "Unnamed"
|
|
|
|
|
|
2021-08-31 08:22:12 +10:00
|
|
|
def add_library_asset(self, name, ifc_definition_id):
|
|
|
|
|
new = self.props.library_elements.add()
|
|
|
|
|
new.name = name
|
|
|
|
|
new.ifc_definition_id = ifc_definition_id
|
2021-09-01 00:01:44 +02:00
|
|
|
element = self.library_file.by_id(ifc_definition_id)
|
|
|
|
|
if self.library_file.schema == "IFC2X3" or not self.library_file.by_type("IfcProjectLibrary"):
|
2021-08-31 08:22:12 +10:00
|
|
|
new.is_declared = False
|
|
|
|
|
elif getattr(element, "HasContext", None) and element.HasContext[0].RelatingContext.is_a("IfcProjectLibrary"):
|
|
|
|
|
new.is_declared = True
|
2021-09-01 00:01:44 +02:00
|
|
|
try:
|
|
|
|
|
if element.is_a("IfcMaterial"):
|
|
|
|
|
next(e for e in self.file.by_type("IfcMaterial") if e.Name == name)
|
|
|
|
|
elif element.is_a("IfcProfileDef"):
|
|
|
|
|
next(e for e in self.file.by_type("IfcProfileDef") if e.ProfileName == name)
|
|
|
|
|
else:
|
|
|
|
|
self.file.by_guid(element.GlobalId)
|
|
|
|
|
new.is_appended = True
|
|
|
|
|
except (AttributeError, RuntimeError, StopIteration):
|
|
|
|
|
new.is_appended = False
|
2021-08-31 08:22:12 +10:00
|
|
|
|
2021-04-20 12:45:20 +10:00
|
|
|
|
|
|
|
|
class RewindLibrary(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.rewind_library"
|
|
|
|
|
bl_label = "Rewind Library"
|
2021-06-30 18:34:12 +10:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-04-20 12:45:20 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
self.props = context.scene.BIMProjectProperties
|
|
|
|
|
total_breadcrumbs = len(self.props.library_breadcrumb)
|
|
|
|
|
if total_breadcrumbs < 2:
|
|
|
|
|
bpy.ops.bim.refresh_library()
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
element_name = self.props.library_breadcrumb[total_breadcrumbs - 2].name
|
|
|
|
|
self.props.library_breadcrumb.remove(total_breadcrumbs - 1)
|
|
|
|
|
self.props.library_breadcrumb.remove(total_breadcrumbs - 2)
|
2021-04-20 13:51:41 +10:00
|
|
|
bpy.ops.bim.change_library_element(element_name=element_name)
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AssignLibraryDeclaration(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.assign_library_declaration"
|
|
|
|
|
bl_label = "Assign Library Declaration"
|
2021-06-30 18:34:12 +10:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-04-20 13:51:41 +10:00
|
|
|
definition: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-07-06 14:21:02 +10:00
|
|
|
IfcStore.begin_transaction(self)
|
2021-07-01 20:43:16 +10:00
|
|
|
IfcStore.library_file.begin_transaction()
|
|
|
|
|
result = self._execute(context)
|
|
|
|
|
IfcStore.library_file.end_transaction()
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.add_transaction_operation(self)
|
2021-07-06 14:21:02 +10:00
|
|
|
IfcStore.end_transaction(self)
|
2021-07-01 20:43:16 +10:00
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def _execute(self, context):
|
2021-04-20 13:51:41 +10:00
|
|
|
self.props = context.scene.BIMProjectProperties
|
2021-06-30 18:34:12 +10:00
|
|
|
self.file = IfcStore.library_file
|
2021-04-20 13:51:41 +10:00
|
|
|
ifcopenshell.api.run(
|
|
|
|
|
"project.assign_declaration",
|
2021-06-30 18:34:12 +10:00
|
|
|
self.file,
|
|
|
|
|
definition=self.file.by_id(self.definition),
|
|
|
|
|
relating_context=self.file.by_type("IfcProjectLibrary")[0],
|
2021-04-20 13:51:41 +10:00
|
|
|
)
|
|
|
|
|
element_name = self.props.active_library_element
|
|
|
|
|
bpy.ops.bim.rewind_library()
|
2021-06-30 18:34:12 +10:00
|
|
|
bpy.ops.bim.change_library_element(element_name=element_name)
|
2021-04-20 12:45:20 +10:00
|
|
|
return {"FINISHED"}
|
2021-04-20 13:51:41 +10:00
|
|
|
|
2021-06-30 18:34:12 +10:00
|
|
|
def rollback(self, data):
|
|
|
|
|
IfcStore.library_file.undo()
|
|
|
|
|
|
|
|
|
|
def commit(self, data):
|
|
|
|
|
IfcStore.library_file.redo()
|
|
|
|
|
|
2021-04-20 13:51:41 +10:00
|
|
|
|
|
|
|
|
class UnassignLibraryDeclaration(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.unassign_library_declaration"
|
|
|
|
|
bl_label = "Unassign Library Declaration"
|
2021-06-30 18:34:12 +10:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-04-20 13:51:41 +10:00
|
|
|
definition: bpy.props.IntProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-07-06 14:21:02 +10:00
|
|
|
IfcStore.begin_transaction(self)
|
2021-07-01 20:43:16 +10:00
|
|
|
IfcStore.library_file.begin_transaction()
|
|
|
|
|
result = self._execute(context)
|
|
|
|
|
IfcStore.library_file.end_transaction()
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.add_transaction_operation(self)
|
2021-07-06 14:21:02 +10:00
|
|
|
IfcStore.end_transaction(self)
|
2021-07-01 20:43:16 +10:00
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def _execute(self, context):
|
2021-04-20 13:51:41 +10:00
|
|
|
self.props = context.scene.BIMProjectProperties
|
2021-06-30 18:34:12 +10:00
|
|
|
self.file = IfcStore.library_file
|
2021-04-20 13:51:41 +10:00
|
|
|
ifcopenshell.api.run(
|
|
|
|
|
"project.unassign_declaration",
|
2021-06-30 18:34:12 +10:00
|
|
|
self.file,
|
|
|
|
|
definition=self.file.by_id(self.definition),
|
|
|
|
|
relating_context=self.file.by_type("IfcProjectLibrary")[0],
|
2021-04-20 13:51:41 +10:00
|
|
|
)
|
|
|
|
|
element_name = self.props.active_library_element
|
|
|
|
|
bpy.ops.bim.rewind_library()
|
2021-06-30 18:34:12 +10:00
|
|
|
bpy.ops.bim.change_library_element(element_name=element_name)
|
2021-04-20 13:51:41 +10:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2021-06-30 18:34:12 +10:00
|
|
|
def rollback(self, data):
|
|
|
|
|
IfcStore.library_file.undo()
|
|
|
|
|
|
|
|
|
|
def commit(self, data):
|
|
|
|
|
IfcStore.library_file.redo()
|
|
|
|
|
|
2021-04-20 13:51:41 +10:00
|
|
|
|
|
|
|
|
class SaveLibraryFile(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.save_library_file"
|
|
|
|
|
bl_label = "Save Library File"
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
IfcStore.library_file.write(IfcStore.library_path)
|
|
|
|
|
return {"FINISHED"}
|
2021-04-21 16:45:11 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class AppendLibraryElement(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.append_library_element"
|
|
|
|
|
bl_label = "Append Library Element"
|
2021-06-30 18:34:12 +10:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-04-21 16:45:11 +10:00
|
|
|
definition: bpy.props.IntProperty()
|
2021-09-01 00:01:44 +02:00
|
|
|
prop_index: bpy.props.IntProperty()
|
2021-04-21 16:45:11 +10:00
|
|
|
|
2021-08-15 19:19:32 +02:00
|
|
|
@classmethod
|
|
|
|
|
def poll(cls, context):
|
|
|
|
|
return IfcStore.get_file()
|
|
|
|
|
|
2021-04-21 16:45:11 +10:00
|
|
|
def execute(self, context):
|
2021-07-01 20:43:16 +10:00
|
|
|
return IfcStore.execute_ifc_operator(self, context)
|
|
|
|
|
|
|
|
|
|
def _execute(self, context):
|
2021-06-30 18:34:12 +10:00
|
|
|
self.file = IfcStore.get_file()
|
2021-04-21 16:45:11 +10:00
|
|
|
element = ifcopenshell.api.run(
|
|
|
|
|
"project.append_asset",
|
2021-06-30 18:34:12 +10:00
|
|
|
self.file,
|
2021-06-18 15:54:01 +10:00
|
|
|
library=IfcStore.library_file,
|
2021-04-21 16:45:11 +10:00
|
|
|
element=IfcStore.library_file.by_id(self.definition),
|
|
|
|
|
)
|
2021-08-07 18:37:45 +10:00
|
|
|
if not element:
|
|
|
|
|
return {"FINISHED"}
|
2021-08-09 11:17:10 +10:00
|
|
|
if element.is_a("IfcTypeProduct"):
|
|
|
|
|
self.import_type_from_ifc(element, context)
|
|
|
|
|
elif element.is_a("IfcMaterial"):
|
|
|
|
|
self.import_material_from_ifc(element, context)
|
2021-09-01 00:01:44 +02:00
|
|
|
context.scene.BIMProjectProperties.library_elements[self.prop_index].is_appended = True
|
2021-06-18 15:54:01 +10:00
|
|
|
blenderbim.bim.handler.purge_module_data()
|
2021-04-21 16:45:11 +10:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2021-08-09 11:17:10 +10:00
|
|
|
def import_material_from_ifc(self, element, context):
|
|
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
logger = logging.getLogger("ImportIFC")
|
|
|
|
|
ifc_import_settings = import_ifc.IfcImportSettings.factory(context, IfcStore.path, logger)
|
|
|
|
|
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
|
|
|
|
|
ifc_importer.file = self.file
|
|
|
|
|
blender_material = ifc_importer.create_material(element)
|
|
|
|
|
self.import_material_styles(blender_material, element, ifc_importer)
|
|
|
|
|
|
2021-07-29 23:08:17 +02:00
|
|
|
def import_type_from_ifc(self, element, context):
|
2021-04-21 16:45:11 +10:00
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
logger = logging.getLogger("ImportIFC")
|
2021-07-29 23:08:17 +02:00
|
|
|
ifc_import_settings = import_ifc.IfcImportSettings.factory(context, IfcStore.path, logger)
|
2021-04-21 16:45:11 +10:00
|
|
|
|
|
|
|
|
type_collection = bpy.data.collections.get("Types")
|
|
|
|
|
if not type_collection:
|
|
|
|
|
type_collection = bpy.data.collections.new("Types")
|
2021-08-23 08:30:59 +10:00
|
|
|
for collection in bpy.context.view_layer.layer_collection.children:
|
2021-04-21 16:45:11 +10:00
|
|
|
if "IfcProject/" in collection.name:
|
2021-08-23 08:30:59 +10:00
|
|
|
collection.collection.children.link(type_collection)
|
|
|
|
|
collection.children["Types"].hide_viewport = True
|
2021-04-21 16:45:11 +10:00
|
|
|
break
|
|
|
|
|
|
|
|
|
|
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
|
|
|
|
|
ifc_importer.file = self.file
|
|
|
|
|
ifc_importer.type_collection = type_collection
|
2021-10-16 15:46:02 +11:00
|
|
|
ifc_importer.material_creator.load_existing_materials()
|
2021-08-04 20:27:00 +10:00
|
|
|
self.import_type_materials(element, ifc_importer)
|
|
|
|
|
self.import_type_styles(element, ifc_importer)
|
2021-04-21 16:45:11 +10:00
|
|
|
ifc_importer.create_type_product(element)
|
2021-09-21 10:14:07 +10:00
|
|
|
ifc_importer.place_objects_in_collections()
|
2021-05-28 09:50:55 +10:00
|
|
|
|
2021-08-04 20:27:00 +10:00
|
|
|
def import_type_materials(self, element, ifc_importer):
|
|
|
|
|
for rel in element.HasAssociations:
|
|
|
|
|
if not rel.is_a("IfcRelAssociatesMaterial"):
|
|
|
|
|
continue
|
|
|
|
|
for material in [e for e in self.file.traverse(rel) if e.is_a("IfcMaterial")]:
|
|
|
|
|
if IfcStore.get_element(material.id()):
|
|
|
|
|
continue
|
|
|
|
|
blender_material = ifc_importer.create_material(material)
|
|
|
|
|
self.import_material_styles(blender_material, material, ifc_importer)
|
|
|
|
|
|
|
|
|
|
def import_type_styles(self, element, ifc_importer):
|
|
|
|
|
for representation_map in element.RepresentationMaps or []:
|
|
|
|
|
for element in self.file.traverse(representation_map):
|
|
|
|
|
if not element.is_a("IfcRepresentationItem") or not element.StyledByItem:
|
|
|
|
|
continue
|
|
|
|
|
for element2 in self.file.traverse(element.StyledByItem[0]):
|
|
|
|
|
if element2.is_a("IfcSurfaceStyle") and not IfcStore.get_element(element2.id()):
|
|
|
|
|
ifc_importer.create_style(element2)
|
|
|
|
|
|
|
|
|
|
def import_material_styles(self, blender_material, material, ifc_importer):
|
|
|
|
|
if not material.HasRepresentation:
|
|
|
|
|
return
|
|
|
|
|
for element in self.file.traverse(material.HasRepresentation[0]):
|
|
|
|
|
if element.is_a("IfcSurfaceStyle") and not IfcStore.get_element(element.id()):
|
|
|
|
|
ifc_importer.create_style(element, blender_material)
|
|
|
|
|
|
2021-05-28 09:50:55 +10:00
|
|
|
|
|
|
|
|
class EnableEditingHeader(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.enable_editing_header"
|
|
|
|
|
bl_label = "Enable Editing Header"
|
2021-06-30 18:34:12 +10:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-10-21 01:10:54 +02:00
|
|
|
bl_description = "Edit the IFC header file such as Author, Organization, ..."
|
2021-05-28 09:50:55 +10:00
|
|
|
|
2021-08-15 19:19:32 +02:00
|
|
|
@classmethod
|
|
|
|
|
def poll(cls, context):
|
|
|
|
|
return IfcStore.get_file()
|
|
|
|
|
|
2021-05-28 09:50:55 +10:00
|
|
|
def execute(self, context):
|
|
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
props = context.scene.BIMProjectProperties
|
|
|
|
|
props.is_editing = True
|
|
|
|
|
|
|
|
|
|
mvd = "".join(IfcStore.get_file().wrapped_data.header.file_description.description)
|
|
|
|
|
if "[" in mvd:
|
|
|
|
|
props.mvd = mvd.split("[")[1][0:-1]
|
|
|
|
|
else:
|
|
|
|
|
props.mvd = ""
|
|
|
|
|
|
|
|
|
|
author = self.file.wrapped_data.header.file_name.author
|
|
|
|
|
if author:
|
|
|
|
|
props.author_name = author[0]
|
|
|
|
|
if len(author) > 1:
|
|
|
|
|
props.author_email = author[1]
|
|
|
|
|
|
|
|
|
|
organisation = self.file.wrapped_data.header.file_name.organization
|
|
|
|
|
if organisation:
|
|
|
|
|
props.organisation_name = organisation[0]
|
|
|
|
|
if len(organisation) > 1:
|
|
|
|
|
props.organisation_email = organisation[1]
|
|
|
|
|
|
|
|
|
|
props.authorisation = self.file.wrapped_data.header.file_name.authorization
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class EditHeader(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.edit_header"
|
|
|
|
|
bl_label = "Edit Header"
|
2021-06-30 18:34:12 +10:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-10-21 01:10:54 +02:00
|
|
|
bl_description = "Save header informations"
|
2021-05-28 09:50:55 +10:00
|
|
|
|
2021-08-15 19:19:32 +02:00
|
|
|
@classmethod
|
|
|
|
|
def poll(cls, context):
|
|
|
|
|
return IfcStore.get_file()
|
|
|
|
|
|
2021-05-28 09:50:55 +10:00
|
|
|
def execute(self, context):
|
2021-07-06 14:21:02 +10:00
|
|
|
IfcStore.begin_transaction(self)
|
2021-07-01 20:43:16 +10:00
|
|
|
self.transaction_data = {}
|
|
|
|
|
self.transaction_data["old"] = self.record_state()
|
|
|
|
|
result = self._execute(context)
|
|
|
|
|
self.transaction_data["new"] = self.record_state()
|
2021-07-02 19:10:51 +10:00
|
|
|
IfcStore.add_transaction_operation(self)
|
2021-07-06 14:21:02 +10:00
|
|
|
IfcStore.end_transaction(self)
|
2021-07-01 20:43:16 +10:00
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def _execute(self, context):
|
2021-05-28 09:50:55 +10:00
|
|
|
self.file = IfcStore.get_file()
|
|
|
|
|
props = context.scene.BIMProjectProperties
|
|
|
|
|
props.is_editing = True
|
|
|
|
|
|
2021-06-30 18:34:12 +10:00
|
|
|
self.file.wrapped_data.header.file_description.description = (f"ViewDefinition[{props.mvd}]",)
|
2021-05-28 09:50:55 +10:00
|
|
|
self.file.wrapped_data.header.file_name.author = (props.author_name, props.author_email)
|
|
|
|
|
self.file.wrapped_data.header.file_name.organization = (props.organisation_name, props.organisation_email)
|
|
|
|
|
self.file.wrapped_data.header.file_name.authorization = props.authorisation
|
|
|
|
|
bpy.ops.bim.disable_editing_header()
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
2021-06-30 18:34:12 +10:00
|
|
|
def record_state(self):
|
2021-07-01 20:43:16 +10:00
|
|
|
self.file = IfcStore.get_file()
|
2021-06-30 18:34:12 +10:00
|
|
|
return {
|
|
|
|
|
"description": self.file.wrapped_data.header.file_description.description,
|
|
|
|
|
"author": self.file.wrapped_data.header.file_name.author,
|
|
|
|
|
"organisation": self.file.wrapped_data.header.file_name.organization,
|
|
|
|
|
"authorisation": self.file.wrapped_data.header.file_name.authorization,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def rollback(self, data):
|
|
|
|
|
file = IfcStore.get_file()
|
|
|
|
|
file.wrapped_data.header.file_description.description = data["old"]["description"]
|
|
|
|
|
file.wrapped_data.header.file_name.author = data["old"]["author"]
|
|
|
|
|
file.wrapped_data.header.file_name.organization = data["old"]["organisation"]
|
|
|
|
|
file.wrapped_data.header.file_name.authorization = data["old"]["authorisation"]
|
|
|
|
|
|
|
|
|
|
def commit(self, data):
|
|
|
|
|
file = IfcStore.get_file()
|
|
|
|
|
file.wrapped_data.header.file_description.description = data["new"]["description"]
|
|
|
|
|
file.wrapped_data.header.file_name.author = data["new"]["author"]
|
|
|
|
|
file.wrapped_data.header.file_name.organization = data["new"]["organisation"]
|
|
|
|
|
file.wrapped_data.header.file_name.authorization = data["new"]["authorisation"]
|
|
|
|
|
|
2021-05-28 09:50:55 +10:00
|
|
|
|
|
|
|
|
class DisableEditingHeader(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.disable_editing_header"
|
|
|
|
|
bl_label = "Disable Editing Header"
|
2021-06-30 18:34:12 +10:00
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-10-21 01:10:54 +02:00
|
|
|
bl_description = "Cancel unsaved header informations"
|
2021-05-28 09:50:55 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
context.scene.BIMProjectProperties.is_editing = False
|
|
|
|
|
return {"FINISHED"}
|
2021-09-11 20:29:14 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class LoadProject(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.load_project"
|
|
|
|
|
bl_label = "Load Project"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-10-21 01:10:54 +02:00
|
|
|
bl_description = "Load an existing IFC project"
|
2021-09-11 20:29:14 +10:00
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
|
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
|
2021-09-12 19:19:53 +10:00
|
|
|
is_advanced: bpy.props.BoolProperty(name="Enable Advanced Mode", default=False)
|
2021-09-11 20:29:14 +10:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-09-12 17:29:11 +10:00
|
|
|
if not os.path.exists(self.filepath) or "ifc" not in os.path.splitext(self.filepath)[1].lower():
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
context.scene.BIMProperties.ifc_file = self.filepath
|
2021-09-11 20:29:14 +10:00
|
|
|
context.scene.BIMProjectProperties.is_loading = True
|
2021-09-12 19:19:53 +10:00
|
|
|
if not self.is_advanced:
|
|
|
|
|
bpy.ops.bim.load_project_elements()
|
2021-09-11 20:29:14 +10:00
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
context.window_manager.fileselect_add(self)
|
|
|
|
|
return {"RUNNING_MODAL"}
|
|
|
|
|
|
|
|
|
|
|
2021-09-12 15:42:59 +10:00
|
|
|
class UnloadProject(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.unload_project"
|
|
|
|
|
bl_label = "Unload Project"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
IfcStore.purge()
|
|
|
|
|
context.scene.BIMProperties.ifc_file = ""
|
|
|
|
|
context.scene.BIMProjectProperties.is_loading = False
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
2021-09-11 20:29:14 +10:00
|
|
|
class LoadProjectElements(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.load_project_elements"
|
|
|
|
|
bl_label = "Load Project Elements"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2021-09-12 15:25:03 +10:00
|
|
|
self.props = context.scene.BIMProjectProperties
|
|
|
|
|
self.file = IfcStore.get_file()
|
2021-09-11 20:29:14 +10:00
|
|
|
start = time.time()
|
|
|
|
|
logger = logging.getLogger("ImportIFC")
|
|
|
|
|
path_log = os.path.join(context.scene.BIMProperties.data_dir, "process.log")
|
|
|
|
|
if not os.access(context.scene.BIMProperties.data_dir, os.W_OK):
|
|
|
|
|
path_log = os.path.join(tempfile.mkdtemp(), "process.log")
|
|
|
|
|
logging.basicConfig(
|
|
|
|
|
filename=path_log,
|
|
|
|
|
filemode="a",
|
|
|
|
|
level=logging.DEBUG,
|
|
|
|
|
)
|
|
|
|
|
settings = import_ifc.IfcImportSettings.factory(context, context.scene.BIMProperties.ifc_file, logger)
|
2021-09-12 15:25:03 +10:00
|
|
|
settings.has_filter = self.props.filter_mode != "NONE"
|
2021-10-15 13:16:26 +11:00
|
|
|
settings.should_filter_spatial_elements = self.props.should_filter_spatial_elements
|
2021-09-12 15:25:03 +10:00
|
|
|
if self.props.filter_mode == "DECOMPOSITION":
|
|
|
|
|
settings.elements = self.get_decomposition_elements()
|
2021-09-12 16:02:08 +10:00
|
|
|
elif self.props.filter_mode == "IFC_CLASS":
|
|
|
|
|
settings.elements = self.get_ifc_class_elements()
|
2021-09-12 19:19:53 +10:00
|
|
|
elif self.props.filter_mode == "WHITELIST":
|
|
|
|
|
settings.elements = self.get_whitelist_elements()
|
|
|
|
|
elif self.props.filter_mode == "BLACKLIST":
|
|
|
|
|
settings.elements = self.get_blacklist_elements()
|
2021-09-11 20:29:14 +10:00
|
|
|
settings.logger.info("Starting import")
|
|
|
|
|
ifc_importer = import_ifc.IfcImporter(settings)
|
|
|
|
|
ifc_importer.execute()
|
|
|
|
|
settings.logger.info("Import finished in {:.2f} seconds".format(time.time() - start))
|
|
|
|
|
print("Import finished in {:.2f} seconds".format(time.time() - start))
|
|
|
|
|
context.scene.BIMProjectProperties.is_loading = False
|
|
|
|
|
return {"FINISHED"}
|
2021-09-12 15:25:03 +10:00
|
|
|
|
|
|
|
|
def get_decomposition_elements(self):
|
|
|
|
|
containers = set()
|
|
|
|
|
for filter_category in self.props.filter_categories:
|
|
|
|
|
if not filter_category.is_selected:
|
|
|
|
|
continue
|
|
|
|
|
container = self.file.by_id(filter_category.ifc_definition_id)
|
|
|
|
|
while container:
|
|
|
|
|
containers.add(container)
|
|
|
|
|
container = ifcopenshell.util.element.get_aggregate(container)
|
|
|
|
|
if container.is_a("IfcContext"):
|
|
|
|
|
container = None
|
|
|
|
|
elements = set()
|
|
|
|
|
for container in containers:
|
|
|
|
|
for rel in container.ContainsElements:
|
|
|
|
|
elements.update(rel.RelatedElements)
|
2021-09-12 17:29:11 +10:00
|
|
|
self.append_decomposed_elements(elements)
|
2021-09-12 16:02:08 +10:00
|
|
|
return elements
|
|
|
|
|
|
2021-09-12 17:29:11 +10:00
|
|
|
def append_decomposed_elements(self, elements):
|
|
|
|
|
decomposed_elements = set()
|
|
|
|
|
for element in elements:
|
|
|
|
|
if element.IsDecomposedBy:
|
|
|
|
|
for subelement in element.IsDecomposedBy[0].RelatedObjects:
|
|
|
|
|
decomposed_elements.add(subelement)
|
|
|
|
|
if decomposed_elements:
|
|
|
|
|
self.append_decomposed_elements(decomposed_elements)
|
|
|
|
|
elements.update(decomposed_elements)
|
|
|
|
|
|
2021-09-12 16:02:08 +10:00
|
|
|
def get_ifc_class_elements(self):
|
|
|
|
|
elements = set()
|
|
|
|
|
for filter_category in self.props.filter_categories:
|
|
|
|
|
if not filter_category.is_selected:
|
|
|
|
|
continue
|
|
|
|
|
elements.update(self.file.by_type(filter_category.name, include_subtypes=False))
|
|
|
|
|
return elements
|
2021-09-12 19:19:53 +10:00
|
|
|
|
|
|
|
|
def get_whitelist_elements(self):
|
|
|
|
|
selector = ifcopenshell.util.selector.Selector()
|
|
|
|
|
return set(selector.parse(self.file, self.props.filter_query))
|
|
|
|
|
|
|
|
|
|
def get_blacklist_elements(self):
|
|
|
|
|
selector = ifcopenshell.util.selector.Selector()
|
|
|
|
|
return set(self.file.by_type("IfcElement")) - set(selector.parse(self.file, self.props.filter_query))
|
2021-09-13 10:00:54 +10:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class LinkIfc(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.link_ifc"
|
|
|
|
|
bl_label = "Link IFC"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-10-21 01:10:54 +02:00
|
|
|
bl_description = "Link a Blender file"
|
2021-09-13 10:00:54 +10:00
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
|
filter_glob: bpy.props.StringProperty(default="*.blend;*.blend1", options={"HIDDEN"})
|
|
|
|
|
|
2021-09-13 10:48:38 +10:00
|
|
|
def execute(self, context):
|
|
|
|
|
new = context.scene.BIMProjectProperties.links.add()
|
|
|
|
|
new.name = self.filepath
|
|
|
|
|
bpy.ops.bim.load_link(filepath=self.filepath)
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
context.window_manager.fileselect_add(self)
|
|
|
|
|
return {"RUNNING_MODAL"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UnlinkIfc(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.unlink_ifc"
|
|
|
|
|
bl_label = "UnLink IFC"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-10-21 01:10:54 +02:00
|
|
|
bl_description = "Remove the selected file from the link list"
|
2021-09-13 10:48:38 +10:00
|
|
|
filepath: bpy.props.StringProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.ops.bim.unload_link(filepath=self.filepath)
|
|
|
|
|
index = context.scene.BIMProjectProperties.links.find(self.filepath)
|
|
|
|
|
if index != -1:
|
|
|
|
|
context.scene.BIMProjectProperties.links.remove(index)
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UnloadLink(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.unload_link"
|
|
|
|
|
bl_label = "Unload Link"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-10-21 01:10:54 +02:00
|
|
|
bl_description = "Unload the selected linked file"
|
2021-09-13 10:48:38 +10:00
|
|
|
filepath: bpy.props.StringProperty()
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
for collection in context.scene.collection.children:
|
|
|
|
|
if collection.library and collection.library.filepath == self.filepath:
|
|
|
|
|
context.scene.collection.children.unlink(collection)
|
|
|
|
|
for scene in bpy.data.scenes:
|
|
|
|
|
if scene.library and scene.library.filepath == self.filepath:
|
|
|
|
|
bpy.data.scenes.remove(scene)
|
|
|
|
|
link = context.scene.BIMProjectProperties.links.get(self.filepath)
|
|
|
|
|
link.is_loaded = False
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LoadLink(bpy.types.Operator):
|
|
|
|
|
bl_idname = "bim.load_link"
|
|
|
|
|
bl_label = "Load Link"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
2021-10-21 01:10:54 +02:00
|
|
|
bl_description = "Load the selected file"
|
2021-09-13 10:48:38 +10:00
|
|
|
filepath: bpy.props.StringProperty()
|
|
|
|
|
|
2021-09-13 10:00:54 +10:00
|
|
|
def execute(self, context):
|
|
|
|
|
with bpy.data.libraries.load(self.filepath, link=True) as (data_from, data_to):
|
|
|
|
|
data_to.scenes = data_from.scenes
|
|
|
|
|
for scene in bpy.data.scenes:
|
|
|
|
|
if not scene.library or scene.library.filepath != self.filepath:
|
|
|
|
|
continue
|
|
|
|
|
for child in scene.collection.children:
|
|
|
|
|
if "IfcProject" not in child.name:
|
|
|
|
|
continue
|
|
|
|
|
bpy.data.scenes[0].collection.children.link(child)
|
2021-09-13 10:48:38 +10:00
|
|
|
link = context.scene.BIMProjectProperties.links.get(self.filepath)
|
|
|
|
|
link.is_loaded = True
|
2021-09-13 10:00:54 +10:00
|
|
|
return {"FINISHED"}
|
2021-10-13 10:49:28 +11:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExportIFC(bpy.types.Operator):
|
|
|
|
|
bl_idname = "export_ifc.bim"
|
|
|
|
|
bl_label = "Export IFC"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
filename_ext = ".ifc"
|
|
|
|
|
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml;*.ifcjson", options={"HIDDEN"})
|
|
|
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
|
json_version: bpy.props.EnumProperty(items=[("4", "4", ""), ("5a", "5a", "")], name="IFC JSON Version")
|
|
|
|
|
json_compact: bpy.props.BoolProperty(name="Export Compact IFCJSON", default=False)
|
2021-11-06 21:41:31 +11:00
|
|
|
should_save_as: bpy.props.BoolProperty(name="Should Save As", default=False)
|
2021-11-07 13:20:24 +11:00
|
|
|
use_relative_path: bpy.props.BoolProperty(name="Use Relative Path", default=False)
|
2021-10-13 10:49:28 +11:00
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
if not IfcStore.get_file():
|
|
|
|
|
self.report({"ERROR"}, "No IFC project is available for export - create or import a project first.")
|
|
|
|
|
return {"FINISHED"}
|
2021-11-06 21:41:31 +11:00
|
|
|
if context.scene.BIMProperties.ifc_file and not self.should_save_as:
|
2021-10-13 10:49:28 +11:00
|
|
|
self.filepath = context.scene.BIMProperties.ifc_file
|
2021-11-07 13:20:24 +11:00
|
|
|
if not os.path.isabs(self.filepath):
|
|
|
|
|
self.filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), self.filepath))
|
2021-10-13 10:49:28 +11:00
|
|
|
return self.execute(context)
|
|
|
|
|
if not self.filepath:
|
|
|
|
|
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".ifc")
|
|
|
|
|
WindowManager = context.window_manager
|
|
|
|
|
WindowManager.fileselect_add(self)
|
|
|
|
|
return {"RUNNING_MODAL"}
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
return IfcStore.execute_ifc_operator(self, context)
|
|
|
|
|
|
|
|
|
|
def _execute(self, context):
|
|
|
|
|
start = time.time()
|
|
|
|
|
logger = logging.getLogger("ExportIFC")
|
|
|
|
|
path_log = os.path.join(context.scene.BIMProperties.data_dir, "process.log")
|
|
|
|
|
if not os.access(context.scene.BIMProperties.data_dir, os.W_OK):
|
|
|
|
|
path_log = os.path.join(tempfile.mkdtemp(), "process.log")
|
|
|
|
|
logging.basicConfig(
|
|
|
|
|
filename=path_log,
|
|
|
|
|
filemode="a",
|
|
|
|
|
level=logging.DEBUG,
|
|
|
|
|
)
|
|
|
|
|
extension = self.filepath.split(".")[-1]
|
|
|
|
|
if extension == "ifczip":
|
|
|
|
|
output_file = bpy.path.ensure_ext(self.filepath, ".ifczip")
|
|
|
|
|
elif extension == "ifcjson":
|
|
|
|
|
output_file = bpy.path.ensure_ext(self.filepath, ".ifcjson")
|
|
|
|
|
else:
|
|
|
|
|
output_file = bpy.path.ensure_ext(self.filepath, ".ifc")
|
|
|
|
|
|
|
|
|
|
settings = export_ifc.IfcExportSettings.factory(context, output_file, logger)
|
|
|
|
|
settings.json_version = self.json_version
|
|
|
|
|
settings.json_compact = self.json_compact
|
|
|
|
|
|
|
|
|
|
ifc_exporter = export_ifc.IfcExporter(settings)
|
|
|
|
|
settings.logger.info("Starting export")
|
|
|
|
|
ifc_exporter.export()
|
|
|
|
|
settings.logger.info("Export finished in {:.2f} seconds".format(time.time() - start))
|
|
|
|
|
print("Export finished in {:.2f} seconds".format(time.time() - start))
|
|
|
|
|
scene = context.scene
|
|
|
|
|
if not scene.DocProperties.ifc_files:
|
|
|
|
|
new = scene.DocProperties.ifc_files.add()
|
|
|
|
|
new.name = output_file
|
2021-11-07 13:20:24 +11:00
|
|
|
if self.use_relative_path and bpy.data.is_saved:
|
|
|
|
|
output_file = os.path.relpath(output_file, bpy.path.abspath("//"))
|
|
|
|
|
if scene.BIMProperties.ifc_file != output_file:
|
2021-10-13 10:49:28 +11:00
|
|
|
scene.BIMProperties.ifc_file = output_file
|
|
|
|
|
if bpy.data.is_saved and bpy.data.is_dirty and bpy.data.filepath:
|
|
|
|
|
bpy.ops.wm.save_mainfile(filepath=bpy.data.filepath)
|
|
|
|
|
blenderbim.bim.handler.purge_module_data()
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ImportIFC(bpy.types.Operator):
|
|
|
|
|
bl_idname = "import_ifc.bim"
|
|
|
|
|
bl_label = "Import IFC"
|
|
|
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
bpy.ops.bim.load_project("INVOKE_DEFAULT")
|
|
|
|
|
return {"FINISHED"}
|