Files
IfcOpenShell/src/ifcblenderexport/blenderbim/bim/operator.py
T

4259 lines
168 KiB
Python
Raw Normal View History

import os
import bpy
import uuid
import time
import json
import logging
import webbrowser
import subprocess
2019-12-11 18:44:46 +11:00
import ifcopenshell
2020-08-08 17:58:45 +10:00
import ifcopenshell.util.selector
import ifcopenshell.util.geolocation
2020-04-28 14:52:04 +10:00
import tempfile
from . import export_ifc
from . import import_ifc
from . import qto
from . import cut_ifc
from . import svgwriter
from . import sheeter
from . import scheduler
from . import schema
2020-03-24 14:44:03 +11:00
from . import bcf
from . import ifc
2020-05-03 15:41:05 +10:00
from . import annotation
from . import helper
from bpy_extras.io_utils import ImportHelper
2019-10-10 18:29:22 +11:00
from itertools import cycle
from mathutils import Vector, Matrix, Euler, geometry
from math import radians, atan, tan, cos, sin, atan2, pi
from pathlib import Path
from bpy.app.handlers import persistent
colour_list = [
2020-11-01 20:08:48 +07:00
(0.651, 0.81, 0.892, 1),
(0.121, 0.471, 0.706, 1),
(0.699, 0.876, 0.54, 1),
(0.199, 0.629, 0.174, 1),
(0.983, 0.605, 0.602, 1),
(0.89, 0.101, 0.112, 1),
(0.989, 0.751, 0.427, 1),
(0.986, 0.497, 0.1, 1),
(0.792, 0.699, 0.839, 1),
(0.414, 0.239, 0.603, 1),
(0.993, 0.999, 0.6, 1),
(0.693, 0.349, 0.157, 1),
]
@persistent
def depsgraph_update_pre_handler(scene):
set_active_camera_resolution(scene)
def set_active_camera_resolution(scene):
2020-11-01 20:08:48 +07:00
if not scene.camera or "/" not in scene.camera.name or not scene.DocProperties.drawings:
return
2020-11-01 20:08:48 +07:00
if (
scene.render.resolution_x != scene.camera.data.BIMCameraProperties.raster_x
or scene.render.resolution_y != scene.camera.data.BIMCameraProperties.raster_y
):
scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x
scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y
2020-09-03 23:02:41 +10:00
current_drawing = scene.DocProperties.drawings[scene.DocProperties.current_drawing_index]
if scene.camera != current_drawing.camera:
2020-11-01 20:08:48 +07:00
scene.DocProperties.current_drawing_index = scene.DocProperties.drawings.find(scene.camera.name.split("/")[1])
2020-09-03 23:02:41 +10:00
bpy.ops.bim.activate_view(drawing_index=scene.DocProperties.current_drawing_index)
def open_with_user_command(user_command, path):
if user_command:
commands = eval(user_command)
for command in commands:
subprocess.run(command)
else:
2020-11-01 20:08:48 +07:00
webbrowser.open("file://" + path)
class ExportIFC(bpy.types.Operator):
bl_idname = "export_ifc.bim"
bl_label = "Export IFC"
filename_ext = ".ifc"
2020-11-01 20:08:48 +07:00
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
if not self.filepath:
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".ifc")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
def execute(self, context):
start = time.time()
2020-11-01 20:08:48 +07:00
logger = logging.getLogger("ExportIFC")
logging.basicConfig(
2020-11-01 20:08:48 +07:00
filename=context.scene.BIMProperties.data_dir + "process.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:
2020-11-01 20:08:48 +07:00
output_file = bpy.path.ensure_ext(self.filepath, ".ifc")
2020-04-08 15:43:45 +02:00
ifc_export_settings = export_ifc.IfcExportSettings.factory(context, output_file, logger)
qto_calculator = qto.QtoCalculator()
ifc_parser = export_ifc.IfcParser(ifc_export_settings, qto_calculator)
ifc_exporter = export_ifc.IfcExporter(ifc_export_settings, ifc_parser)
2020-11-01 20:08:48 +07:00
ifc_export_settings.logger.info("Starting export")
2020-04-09 02:46:38 +02:00
ifc_exporter.export(context.selected_objects)
2020-11-01 20:08:48 +07:00
ifc_export_settings.logger.info("Export finished in {:.2f} seconds".format(time.time() - start))
if not bpy.context.scene.DocProperties.ifc_files:
new = bpy.context.scene.DocProperties.ifc_files.add()
new.name = output_file
if not bpy.context.scene.BIMProperties.ifc_file:
bpy.context.scene.BIMProperties.ifc_file = output_file
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class ImportIFC(bpy.types.Operator, ImportHelper):
bl_idname = "import_ifc.bim"
bl_label = "Import IFC"
filename_ext = ".ifc"
2020-11-01 20:08:48 +07:00
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
def execute(self, context):
start = time.time()
2020-11-01 20:08:48 +07:00
logger = logging.getLogger("ImportIFC")
logging.basicConfig(
2020-11-01 20:08:48 +07:00
filename=bpy.context.scene.BIMProperties.data_dir + "process.log", filemode="a", level=logging.DEBUG
)
2020-04-28 14:52:04 +10:00
ifc_import_settings = import_ifc.IfcImportSettings.factory(context, self.filepath, logger)
2020-11-01 20:08:48 +07:00
ifc_import_settings.logger.info("Starting import")
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
ifc_importer.execute()
2020-11-01 20:08:48 +07:00
ifc_import_settings.logger.info("Import finished in {:.2f} seconds".format(time.time() - start))
print("Import finished in {:.2f} seconds".format(time.time() - start))
return {"FINISHED"}
class SelectGlobalId(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_global_id"
bl_label = "Select GlobalId"
def execute(self, context):
for obj in bpy.context.visible_objects:
2020-11-01 20:08:48 +07:00
index = obj.BIMObjectProperties.attributes.find("GlobalId")
if (
index != -1
and obj.BIMObjectProperties.attributes[index].string_value == bpy.context.scene.BIMProperties.global_id
):
obj.select_set(True)
break
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectAttribute(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_attribute"
bl_label = "Select Attribute"
def execute(self, context):
import re
2020-11-01 20:08:48 +07:00
search_value = bpy.context.scene.BIMProperties.search_attribute_value
for object in bpy.context.visible_objects:
index = object.BIMObjectProperties.attributes.find(bpy.context.scene.BIMProperties.search_attribute_name)
if index == -1:
continue
value = object.BIMObjectProperties.attributes[index].string_value
2020-11-01 20:08:48 +07:00
if (
bpy.context.scene.BIMProperties.search_regex
and bpy.context.scene.BIMProperties.search_ignorecase
and re.search(search_value, value, flags=re.IGNORECASE)
):
object.select_set(True)
2020-11-01 20:08:48 +07:00
elif bpy.context.scene.BIMProperties.search_regex and re.search(search_value, value):
object.select_set(True)
2020-11-01 20:08:48 +07:00
elif bpy.context.scene.BIMProperties.search_ignorecase and value.lower() == search_value.lower():
object.select_set(True)
elif value == search_value:
object.select_set(True)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectPset(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_pset"
bl_label = "Select Pset"
def execute(self, context):
import re
2020-11-01 20:08:48 +07:00
search_pset_name = bpy.context.scene.BIMProperties.search_pset_name
search_prop_name = bpy.context.scene.BIMProperties.search_prop_name
search_value = bpy.context.scene.BIMProperties.search_pset_value
for object in bpy.context.visible_objects:
2020-04-24 20:54:24 +10:00
pset_index = object.BIMObjectProperties.psets.find(search_pset_name)
if pset_index == -1:
continue
2020-04-24 20:54:24 +10:00
prop_index = object.BIMObjectProperties.psets[pset_index].properties.find(search_prop_name)
if prop_index == -1:
continue
2020-04-24 20:54:24 +10:00
value = object.BIMObjectProperties.psets[pset_index].properties[prop_index].string_value
2020-11-01 20:08:48 +07:00
if (
bpy.context.scene.BIMProperties.search_regex
and bpy.context.scene.BIMProperties.search_ignorecase
and re.search(search_value, value, flags=re.IGNORECASE)
):
object.select_set(True)
2020-11-01 20:08:48 +07:00
elif bpy.context.scene.BIMProperties.search_regex and re.search(search_value, value):
object.select_set(True)
2020-11-01 20:08:48 +07:00
elif bpy.context.scene.BIMProperties.search_ignorecase and value.lower() == search_value.lower():
object.select_set(True)
elif value == search_value:
object.select_set(True)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AssignClass(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.assign_class"
bl_label = "Assign IFC Class"
object_name: bpy.props.StringProperty()
def execute(self, context):
if self.object_name:
objects = [bpy.data.objects.get(self.object_name)]
else:
objects = bpy.context.selected_objects
for obj in objects:
existing_class = None
2020-11-01 20:08:48 +07:00
if "/" in obj.name and obj.name[0:3] == "Ifc":
existing_class = obj.name.split("/")[0]
if existing_class:
2020-11-01 20:08:48 +07:00
obj.name = "{}/{}".format(bpy.context.scene.BIMProperties.ifc_class, obj.name.split("/")[1])
else:
2020-11-01 20:08:48 +07:00
obj.name = "{}/{}".format(bpy.context.scene.BIMProperties.ifc_class, obj.name)
predefined_type_index = obj.BIMObjectProperties.attributes.find("PredefinedType")
2019-10-16 23:17:48 +11:00
if predefined_type_index >= 0:
obj.BIMObjectProperties.attributes.remove(predefined_type_index)
2020-11-01 20:08:48 +07:00
object_type_index = obj.BIMObjectProperties.attributes.find("ObjectType")
2019-10-16 23:17:48 +11:00
if object_type_index >= 0:
obj.BIMObjectProperties.attributes.remove(object_type_index)
if bpy.context.scene.BIMProperties.ifc_predefined_type:
predefined_type = obj.BIMObjectProperties.attributes.add()
2020-11-01 20:08:48 +07:00
predefined_type.name = "PredefinedType"
predefined_type.string_value = (
bpy.context.scene.BIMProperties.ifc_predefined_type
) # TODO: make it an enum
if bpy.context.scene.BIMProperties.ifc_predefined_type == "USERDEFINED":
object_type = obj.BIMObjectProperties.attributes.add()
2020-11-01 20:08:48 +07:00
object_type.name = "ObjectType"
2019-10-16 23:17:48 +11:00
object_type.string_value = bpy.context.scene.BIMProperties.ifc_userdefined_type
2020-11-01 20:08:48 +07:00
if bpy.context.scene.BIMProperties.ifc_product == "IfcElementType":
for project in [c for c in bpy.context.view_layer.layer_collection.children if "IfcProject" in c.name]:
if not [c for c in project.children if "Types" in c.name]:
types = bpy.data.collections.new("Types")
project.collection.children.link(types)
2020-11-01 20:08:48 +07:00
for collection in [c for c in project.children if "Types" in c.name]:
for user_collection in obj.users_collection:
user_collection.objects.unlink(obj)
collection.collection.objects.link(obj)
break
break
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class UnassignClass(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.unassign_class"
bl_label = "Unassign IFC Class"
object_name: bpy.props.StringProperty()
def execute(self, context):
if self.object_name:
objects = [bpy.data.objects.get(self.object_name)]
else:
objects = bpy.context.selected_objects
for obj in objects:
existing_class = None
2020-11-01 20:08:48 +07:00
if "/" in obj.name and obj.name[0:3] == "Ifc":
obj.name = "/".join(obj.name.split("/")[1:])
return {"FINISHED"}
class SelectClass(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_class"
bl_label = "Select IFC Class"
def execute(self, context):
for object in bpy.context.visible_objects:
2020-11-01 20:08:48 +07:00
if (
"/" in object.name
and object.name[0:3] == "Ifc"
and object.name.split("/")[0] == bpy.context.scene.BIMProperties.ifc_class
):
object.select_set(True)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectType(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_type"
bl_label = "Select IFC Type"
def execute(self, context):
for object in bpy.context.visible_objects:
2020-11-01 20:08:48 +07:00
if (
"/" in object.name
and object.name[0:3] == "Ifc"
and object.name.split("/")[0] == bpy.context.scene.BIMProperties.ifc_class
and "PredefinedType" in object.BIMObjectProperties.attributes
and object.BIMObjectProperties.attributes["PredefinedType"].string_value
== bpy.context.scene.BIMProperties.ifc_predefined_type
):
if bpy.context.scene.BIMProperties.ifc_predefined_type != "USERDEFINED":
object.select_set(True)
2020-11-01 20:08:48 +07:00
elif (
"ObjectType" in object.BIMObjectProperties.attributes
and object.BIMObjectProperties.attributes["ObjectType"].string_value
== bpy.context.scene.BIMProperties.ifc_userdefined_type
):
object.select_set(True)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-10-10 18:29:22 +11:00
class ColourByClass(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.colour_by_class"
bl_label = "Colour by Class"
2019-10-10 18:29:22 +11:00
def execute(self, context):
colours = cycle(colour_list)
ifc_classes = {}
for obj in bpy.context.visible_objects:
2020-11-01 20:08:48 +07:00
if "/" not in obj.name:
2019-10-10 18:29:22 +11:00
continue
2020-11-01 20:08:48 +07:00
ifc_class = obj.name.split("/")[0]
2019-10-10 18:29:22 +11:00
if ifc_class not in ifc_classes:
ifc_classes[ifc_class] = next(colours)
obj.color = ifc_classes[ifc_class]
2020-11-01 20:08:48 +07:00
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
return {"FINISHED"}
2019-10-10 18:29:22 +11:00
class ColourByAttribute(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.colour_by_attribute"
bl_label = "Colour by Attribute"
def execute(self, context):
colours = cycle(colour_list)
values = {}
attribute_name = bpy.context.scene.BIMProperties.search_attribute_name
for obj in bpy.context.visible_objects:
index = obj.BIMObjectProperties.attributes.find(attribute_name)
if index == -1:
continue
value = obj.BIMObjectProperties.attributes[index].string_value
if value not in values:
values[value] = next(colours)
obj.color = values[value]
2020-11-01 20:08:48 +07:00
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
return {"FINISHED"}
class ColourByPset(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.colour_by_pset"
bl_label = "Colour by Pset"
def execute(self, context):
colours = cycle(colour_list)
values = {}
search_pset_name = bpy.context.scene.BIMProperties.search_pset_name
search_prop_name = bpy.context.scene.BIMProperties.search_prop_name
for obj in bpy.context.visible_objects:
2020-04-24 20:54:24 +10:00
pset_index = obj.BIMObjectProperties.psets.find(search_pset_name)
if pset_index == -1:
continue
2020-04-24 20:54:24 +10:00
prop_index = obj.BIMObjectProperties.psets[pset_index].properties.find(search_prop_name)
if prop_index == -1:
continue
2020-04-24 20:54:24 +10:00
value = obj.BIMObjectProperties.psets[pset_index].properties[prop_index].string_value
if value not in values:
values[value] = next(colours)
obj.color = values[value]
2020-11-01 20:08:48 +07:00
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
return {"FINISHED"}
2019-10-10 18:29:22 +11:00
class ResetObjectColours(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.reset_object_colours"
bl_label = "Reset Colours"
2019-10-10 18:29:22 +11:00
def execute(self, context):
for object in bpy.context.selected_objects:
object.color = (1, 1, 1, 1)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-10-10 18:29:22 +11:00
2020-11-01 20:08:48 +07:00
class QAHelper:
@classmethod
def append_to_scenario(cls, lines):
filename = os.path.join(
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.features_dir, bpy.context.scene.BIMProperties.features_file + ".feature"
)
if os.path.exists(filename + "~"):
os.remove(filename + "~")
os.rename(filename, filename + "~")
with open(filename, "w") as destination:
with open(filename + "~", "r") as source:
is_in_scenario = False
for source_line in source:
2020-11-01 20:08:48 +07:00
if (
"Scenario: " in source_line
and bpy.context.scene.BIMProperties.scenario == source_line.strip()[len("Scenario: ") :]
):
is_in_scenario = True
elif is_in_scenario:
for line in lines:
2020-11-01 20:08:48 +07:00
destination.write(line + "\n")
is_in_scenario = False
destination.write(source_line)
2020-11-01 20:08:48 +07:00
os.remove(filename + "~")
2019-10-10 18:29:22 +11:00
class ApproveClass(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.approve_class"
bl_label = "Approve Class"
2019-10-10 18:29:22 +11:00
def execute(self, context):
lines = []
for object in bpy.context.selected_objects:
2020-11-01 20:08:48 +07:00
index = object.BIMObjectProperties.attributes.find("GlobalId")
if index != -1:
2020-11-01 20:08:48 +07:00
lines.append(
" * The element {} is an {}".format(
object.BIMObjectProperties.attributes[index].string_value, object.name.split("/")[0]
)
)
QAHelper.append_to_scenario(lines)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-10-10 18:29:22 +11:00
2019-10-10 18:29:22 +11:00
class RejectClass(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.reject_class"
bl_label = "Reject Class"
2019-10-10 18:29:22 +11:00
def execute(self, context):
lines = []
for object in bpy.context.selected_objects:
2020-11-01 20:08:48 +07:00
lines.append(
" * The element {} is an {}".format(
object.BIMObjectProperties.attributes[
object.BIMObjectProperties.attributes.find("GlobalId")
].string_value,
bpy.context.scene.BIMProperties.audit_ifc_class,
)
)
QAHelper.append_to_scenario(lines)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-10-10 18:29:22 +11:00
2019-10-10 18:29:22 +11:00
class RejectElement(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.reject_element"
bl_label = "Reject Element"
2019-10-10 18:29:22 +11:00
def execute(self, context):
lines = []
for object in bpy.context.selected_objects:
2020-11-01 20:08:48 +07:00
lines.append(
" * The element {} should not exist because {}".format(
object.BIMObjectProperties.attributes[
object.BIMObjectProperties.attributes.find("GlobalId")
].string_value,
bpy.context.scene.BIMProperties.qa_reject_element_reason,
)
)
QAHelper.append_to_scenario(lines)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-10-10 18:29:22 +11:00
class GetBcfTopics(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.get_bcf_topics"
bl_label = "Get BCF Topics"
def execute(self, context):
import bcfplugin
2020-11-01 20:08:48 +07:00
2020-03-24 17:53:58 +11:00
bcfplugin.openProject(bpy.context.scene.BCFProperties.bcf_file)
2020-03-24 14:44:03 +11:00
bcf.BcfStore.topics = bcfplugin.getTopics()
2020-03-24 17:53:58 +11:00
while len(bpy.context.scene.BCFProperties.topics) > 0:
bpy.context.scene.BCFProperties.topics.remove(0)
2020-03-24 14:44:03 +11:00
for topic in bcf.BcfStore.topics:
2020-03-24 17:53:58 +11:00
new = bpy.context.scene.BCFProperties.topics.add()
2020-03-21 21:44:02 +11:00
new.name = topic[0]
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-03-25 17:55:52 +11:00
class ViewBcfTopic(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.view_bcf_topic"
bl_label = "Get BCF Topic"
2020-03-25 17:55:52 +11:00
topic_guid: bpy.props.StringProperty()
def execute(self, context):
for index, topic in enumerate(bcf.BcfStore.topics):
if str(topic[1].xmlId) == self.topic_guid:
bpy.context.scene.BCFProperties.active_topic_index = index
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-03-25 17:55:52 +11:00
class ActivateBcfViewpoint(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.activate_bcf_viewpoint"
bl_label = "Activate BCF Viewpoint"
def execute(self, context):
import bcfplugin
2020-03-25 17:55:52 +11:00
topics = bcf.BcfStore.topics
if not topics:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-03-24 17:53:58 +11:00
topic = topics[bpy.context.scene.BCFProperties.active_topic_index][1]
viewpoints = bcf.BcfStore.viewpoints
if not viewpoints:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
viewpoint_reference = viewpoints[int(bpy.context.scene.BCFProperties.viewpoints)][1]
viewpoint = viewpoint_reference.viewpoint
2020-11-01 20:08:48 +07:00
obj = bpy.data.objects.get("Viewpoint")
if not obj:
2020-11-01 20:08:48 +07:00
obj = bpy.data.objects.new("Viewpoint", bpy.data.cameras.new("Viewpoint"))
bpy.context.scene.collection.objects.link(obj)
bpy.context.scene.camera = obj
cam_width = bpy.context.scene.render.resolution_x
cam_height = bpy.context.scene.render.resolution_y
cam_aspect = cam_width / cam_height
if viewpoint_reference.snapshot:
obj.data.show_background_images = True
while len(obj.data.background_images) > 0:
obj.data.background_images.remove(obj.data.background_images[0])
background = obj.data.background_images.new()
2020-11-01 20:08:48 +07:00
background.image = bpy.data.images.load(
os.path.join(bcfplugin.util.getBcfDir(), str(topic.xmlId), viewpoint_reference.snapshot.uri)
)
src_width = background.image.size[0]
src_height = background.image.size[1]
src_aspect = src_width / src_height
if src_aspect > cam_aspect:
2020-11-01 20:08:48 +07:00
background.frame_method = "FIT"
else:
2020-11-01 20:08:48 +07:00
background.frame_method = "CROP"
background.display_depth = "FRONT"
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].region_3d.view_perspective = "CAMERA"
if viewpoint.oCamera:
camera = viewpoint.oCamera
2020-11-01 20:08:48 +07:00
obj.data.type = "ORTHO"
obj.data.ortho_scale = viewpoint.oCamera.viewWorldScale
elif viewpoint.pCamera:
camera = viewpoint.pCamera
2020-11-01 20:08:48 +07:00
obj.data.type = "PERSP"
if cam_aspect >= 1:
obj.data.angle = radians(camera.fieldOfView)
else:
# https://blender.stackexchange.com/questions/23431/how-to-set-camera-horizontal-and-vertical-fov
2020-11-01 20:08:48 +07:00
obj.data.angle = 2 * atan((0.5 * cam_height) / (0.5 * cam_width / tan(radians(camera.fieldOfView) / 2)))
self.set_viewpoint_components(viewpoint)
2020-11-01 20:08:48 +07:00
gp = bpy.data.grease_pencils.get("BCF")
2020-04-27 13:35:39 +10:00
if gp:
bpy.data.grease_pencils.remove(gp)
if viewpoint.lines:
self.draw_lines(viewpoint)
2020-04-27 14:30:33 +10:00
self.delete_clipping_planes()
if viewpoint.clippingPlanes:
self.create_clipping_planes(viewpoint)
2020-04-28 10:46:08 +10:00
self.delete_bitmaps()
if viewpoint.bitmaps:
self.create_bitmaps(viewpoint)
z_axis = Vector((-camera.direction.x, -camera.direction.y, -camera.direction.z)).normalized()
y_axis = Vector((camera.upVector.x, camera.upVector.y, camera.upVector.z)).normalized()
x_axis = y_axis.cross(z_axis).normalized()
rotation = Matrix((x_axis, y_axis, z_axis))
rotation.invert()
location = Vector((camera.viewPoint.x, camera.viewPoint.y, camera.viewPoint.z))
obj.matrix_world = rotation.to_4x4()
obj.location = location
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def set_viewpoint_components(self, viewpoint):
selected_global_ids = [s.ifcId for s in viewpoint.components.selection]
exception_global_ids = [v.ifcId for v in viewpoint.components.visibilityExceptions]
global_id_colours = {}
for colouring in viewpoint.components.colouring:
for component in colouring.components:
global_id_colours.setdefault(component.ifcId, colouring.colour)
for obj in bpy.data.objects:
2020-11-01 20:08:48 +07:00
global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
if not global_id:
continue
global_id = global_id.string_value
is_visible = viewpoint.components.visibilityDefault
if global_id in exception_global_ids:
is_visible = not is_visible
if not is_visible:
obj.hide_set(True)
continue
2020-11-01 20:08:48 +07:00
if "IfcSpace" in obj.name:
is_visible = viewpoint.components.viewSetuphints.spacesVisible
2020-11-01 20:08:48 +07:00
elif "IfcOpeningElement" in obj.name:
is_visible = viewpoint.components.viewSetuphints.openingsVisible
obj.hide_set(not is_visible)
if not is_visible:
continue
obj.select_set(global_id in selected_global_ids)
if global_id in global_id_colours:
obj.color = self.hex_to_rgb(global_id_colours[global_id])
2020-04-27 13:35:39 +10:00
def draw_lines(self, viewpoint):
2020-11-01 20:08:48 +07:00
gp = bpy.data.grease_pencils.new("BCF")
2020-04-27 13:35:39 +10:00
scene = bpy.context.scene
scene.grease_pencil = gp
scene.frame_set(1)
2020-11-01 20:08:48 +07:00
layer = gp.layers.new("BCF Annotation", set_active=True)
2020-04-27 13:35:39 +10:00
layer.thickness = 3
layer.color = (1, 0, 0)
frame = layer.frames.new(1)
stroke = frame.strokes.new()
2020-11-01 20:08:48 +07:00
stroke.display_mode = "3DSPACE"
stroke.points.add(len(viewpoint.lines) * 2)
2020-04-27 13:35:39 +10:00
coords = []
for l in viewpoint.lines:
coords.extend([l.start.x, l.start.y, l.start.z, l.end.x, l.end.y, l.end.z])
2020-11-01 20:08:48 +07:00
stroke.points.foreach_set("co", coords)
2020-04-27 13:35:39 +10:00
2020-04-27 14:30:33 +10:00
def create_clipping_planes(self, viewpoint):
n = 0
for plane in viewpoint.clippingPlanes:
bpy.ops.bim.add_section_plane()
if n == 0:
2020-11-01 20:08:48 +07:00
obj = bpy.data.objects["Section"]
2020-04-27 14:30:33 +10:00
else:
2020-11-01 20:08:48 +07:00
obj = bpy.data.objects["Section.{:03d}".format(n)]
2020-04-27 14:30:33 +10:00
obj.location = (plane.location.x, plane.location.y, plane.location.z)
2020-11-01 20:08:48 +07:00
obj.rotation_mode = "QUATERNION"
obj.rotation_quaternion = Vector((plane.direction.x, plane.direction.y, plane.direction.z)).to_track_quat(
"Z", "Y"
)
2020-04-27 14:30:33 +10:00
n += 1
def delete_clipping_planes(self):
2020-11-01 20:08:48 +07:00
collection = bpy.data.collections.get("Sections")
2020-04-27 14:30:33 +10:00
if not collection:
return
for section in collection.objects:
bpy.context.view_layer.objects.active = section
bpy.ops.bim.remove_section_plane()
2020-04-28 10:46:08 +10:00
def delete_bitmaps(self):
2020-11-01 20:08:48 +07:00
collection = bpy.data.collections.get("Bitmaps")
2020-04-28 10:46:08 +10:00
if not collection:
2020-11-01 20:08:48 +07:00
collection = bpy.data.collections.new("Bitmaps")
2020-04-28 10:46:08 +10:00
bpy.context.scene.collection.children.link(collection)
for bitmap in collection.objects:
bpy.data.objects.remove(bitmap)
def create_bitmaps(self, viewpoint):
import bcfplugin
2020-11-01 20:08:48 +07:00
2020-04-28 10:46:08 +10:00
topics = bcf.BcfStore.topics
topic = topics[bpy.context.scene.BCFProperties.active_topic_index][1]
2020-11-01 20:08:48 +07:00
collection = bpy.data.collections.get("Bitmaps")
2020-04-28 10:46:08 +10:00
if not collection:
2020-11-01 20:08:48 +07:00
collection = bpy.data.collections.new("Bitmaps")
2020-04-28 10:46:08 +10:00
for bitmap in viewpoint.bitmaps:
2020-11-01 20:08:48 +07:00
obj = bpy.data.objects.new("Bitmap", None)
obj.empty_display_type = "IMAGE"
image = bpy.data.images.load(os.path.join(bcfplugin.util.getBcfDir(), str(topic.xmlId), bitmap.reference))
2020-04-28 10:46:08 +10:00
src_width = image.size[0]
src_height = image.size[1]
if src_height > src_width:
obj.empty_display_size = bitmap.height
else:
obj.empty_display_size = bitmap.height * (src_width / src_height)
obj.data = image
y = Vector((bitmap.upVector.x, bitmap.upVector.y, bitmap.upVector.z))
z = Vector((bitmap.normal.x, bitmap.normal.y, bitmap.normal.z))
x = y.cross(z)
2020-11-01 20:08:48 +07:00
obj.matrix_world = Matrix(
[[x[0], y[0], z[0], 0], [x[1], y[1], z[1], 0], [x[2], y[2], z[2], 0], [0, 0, 0, 1]]
)
2020-04-28 10:46:08 +10:00
obj.location = (bitmap.location.x, bitmap.location.y, bitmap.location.z)
collection.objects.link(obj)
def hex_to_rgb(self, value):
2020-11-01 20:08:48 +07:00
value = value.lstrip("#")
lv = len(value)
2020-11-01 20:08:48 +07:00
t = tuple(int(value[i : i + lv // 3], 16) for i in range(0, lv, lv // 3))
return [t[0] / 255.0, t[1] / 255.0, t[2] / 255.0, 1]
2020-03-31 12:35:17 +11:00
class OpenBcfFileReference(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.open_bcf_file_reference"
bl_label = "Open BCF File Reference"
2020-03-31 12:35:17 +11:00
data: bpy.props.StringProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if "/" not in self.data:
2020-03-31 12:35:17 +11:00
webbrowser.open(bpy.context.scene.BCFProperties.topic_files[int(self.data)].reference)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-03-31 12:35:17 +11:00
import bcfplugin
2020-11-01 20:08:48 +07:00
topic_guid, index = self.data.split("/")
path = os.path.join(bcfplugin.util.getBcfDir(), topic_guid)
2020-03-31 12:35:17 +11:00
# bpy.context.scene.BCFProperties.topic_files[int(index)].reference)
# TODO - maybe allow immediate importing?
webbrowser.open(path)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-03-31 12:35:17 +11:00
class OpenBcfReferenceLink(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.open_bcf_reference_link"
bl_label = "Open BCF Reference Link"
index: bpy.props.IntProperty()
def execute(self, context):
webbrowser.open(bpy.context.scene.BCFProperties.topic_links[self.index].name)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-03-25 16:20:24 +11:00
class OpenBcfBimSnippetSchema(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.open_bcf_bim_snippet_schema"
bl_label = "Open BCF BIM Snippet Schema"
2020-03-25 16:20:24 +11:00
def execute(self, context):
webbrowser.open(bpy.context.scene.BCFProperties.topic_snippet_schema)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-03-25 16:20:24 +11:00
class OpenBcfBimSnippetReference(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.open_bcf_bim_snippet_reference"
bl_label = "Open BCF BIM Snippet Reference"
2020-03-25 16:20:24 +11:00
topic_guid: bpy.props.StringProperty()
def execute(self, context):
import bcfplugin
2020-11-01 20:08:48 +07:00
2020-03-25 16:20:24 +11:00
if bpy.context.scene.BCFProperties.topic_snippet_is_external:
webbrowser.open(bpy.context.scene.BCFProperties.topic_snippet_reference)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
webbrowser.open(
"file://"
+ os.path.join(
bcfplugin.util.getBcfDir(), self.topic_guid, bpy.context.scene.BCFProperties.topic_snippet_reference
)
)
return {"FINISHED"}
2020-03-25 16:20:24 +11:00
2020-03-25 17:12:02 +11:00
class OpenBcfDocumentReference(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.open_bcf_document_reference"
bl_label = "Open BCF Document Reference"
2020-03-25 17:12:02 +11:00
data: bpy.props.StringProperty()
def execute(self, context):
import bcfplugin
2020-11-01 20:08:48 +07:00
topic_guid, index = self.data.split("/")
2020-03-25 17:12:02 +11:00
doc = bpy.context.scene.BCFProperties.topic_document_references[int(index)]
uri = doc.name
if doc.is_external:
webbrowser.open(uri)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
webbrowser.open("file://" + os.path.join(bcfplugin.util.getBcfDir(), topic_guid, uri))
return {"FINISHED"}
2020-03-25 17:12:02 +11:00
2019-10-10 18:29:22 +11:00
class SelectAudited(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_audited"
bl_label = "Select Audited"
2019-10-10 18:29:22 +11:00
def execute(self, context):
2019-11-18 11:30:08 +11:00
audited_global_ids = []
2020-11-01 20:08:48 +07:00
for filename in Path(bpy.context.scene.BIMProperties.features_dir).glob("*.feature"):
with open(filename, "r") as feature_file:
lines = feature_file.readlines()
for line in lines:
words = line.strip().split()
for word in words:
if self.is_a_global_id(word):
audited_global_ids.append(word)
2019-11-18 11:30:08 +11:00
for object in bpy.context.visible_objects:
2020-11-01 20:08:48 +07:00
index = object.BIMObjectProperties.attributes.find("GlobalId")
if index != -1 and object.BIMObjectProperties.attributes[index].string_value in audited_global_ids:
2019-11-18 11:30:08 +11:00
object.select_set(True)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-10-10 18:29:22 +11:00
def is_a_global_id(self, word):
2020-11-01 20:08:48 +07:00
return word[0] in ["0", "1", "2", "3"] and len(word) == 22
class QuickProjectSetup(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.quick_project_setup"
bl_label = "Quick Project Setup"
def execute(self, context):
2020-11-01 20:08:48 +07:00
project = bpy.data.collections.new("IfcProject/My Project")
site = bpy.data.collections.new("IfcSite/My Site")
building = bpy.data.collections.new("IfcBuilding/My Building")
building_storey = bpy.data.collections.new("IfcBuildingStorey/Ground Floor")
2020-11-01 20:08:48 +07:00
project_obj = bpy.data.objects.new("IfcProject/My Project", None)
site_obj = bpy.data.objects.new("IfcSite/My Site", None)
building_obj = bpy.data.objects.new("IfcBuilding/My Building", None)
building_storey_obj = bpy.data.objects.new("IfcBuildingStorey/Ground Floor", None)
bpy.context.scene.collection.children.link(project)
project.children.link(site)
site.children.link(building)
building.children.link(building_storey)
project.objects.link(project_obj)
site.objects.link(site_obj)
building.objects.link(building_obj)
building_storey.objects.link(building_storey_obj)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddQto(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_qto"
bl_label = "Add Qto"
def execute(self, context):
name = bpy.context.active_object.BIMObjectProperties.qto_name
if name not in schema.ifc.qtos:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
qto = bpy.context.active_object.BIMObjectProperties.qtos.add()
qto.name = name
2020-11-01 20:08:48 +07:00
for prop_name in schema.ifc.qtos[name]["HasPropertyTemplates"].keys():
prop = qto.properties.add()
prop.name = prop_name
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-24 20:54:24 +10:00
class AddPset(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_pset"
bl_label = "Add Pset"
def execute(self, context):
2020-04-24 20:54:24 +10:00
pset_name = bpy.context.active_object.BIMObjectProperties.pset_name
if pset_name not in schema.ifc.psets:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-24 20:54:24 +10:00
pset = bpy.context.active_object.BIMObjectProperties.psets.add()
pset.name = pset_name
2020-11-01 20:08:48 +07:00
for prop_name in schema.ifc.psets[pset_name]["HasPropertyTemplates"].keys():
prop = pset.properties.add()
prop.name = prop_name
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-24 20:54:24 +10:00
class RemovePset(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_pset"
bl_label = "Remove Pset"
pset_index: bpy.props.IntProperty()
def execute(self, context):
2020-04-24 20:54:24 +10:00
bpy.context.active_object.BIMObjectProperties.psets.remove(self.pset_index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-03-19 16:48:50 +11:00
class RemoveQto(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_qto"
bl_label = "Remove Qto"
2020-03-19 16:48:50 +11:00
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.BIMObjectProperties.qtos.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-03-19 16:48:50 +11:00
class AddMaterialPset(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_material_pset"
bl_label = "Add Material Pset"
def execute(self, context):
pset = bpy.context.active_object.active_material.BIMMaterialProperties.psets.add()
pset.name = bpy.context.active_object.active_material.BIMMaterialProperties.available_material_psets
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveMaterialPset(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_material_pset"
bl_label = "Remove Pset"
pset_index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.active_material.BIMMaterialProperties.psets.remove(self.pset_index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddConstraint(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_constraint"
bl_label = "Add Constraint"
def execute(self, context):
constraint = bpy.context.scene.BIMProperties.constraints.add()
2020-11-01 20:08:48 +07:00
constraint.name = "New Constraint"
return {"FINISHED"}
class RemoveConstraint(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_constraint"
bl_label = "Remove Constraint"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.BIMProperties.constraints.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AssignConstraint(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.assign_constraint"
bl_label = "Assign Constraint"
def execute(self, context):
2020-11-01 20:08:48 +07:00
identification = bpy.context.scene.BIMProperties.constraints[
bpy.context.scene.BIMProperties.active_constraint_index
].name
for obj in bpy.context.selected_objects:
if obj.BIMObjectProperties.constraints.get(identification):
continue
constraint = obj.BIMObjectProperties.constraints.add()
constraint.name = identification
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class UnassignConstraint(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.unassign_constraint"
bl_label = "Unassign Constraint"
def execute(self, context):
2020-11-01 20:08:48 +07:00
identification = bpy.context.scene.BIMProperties.constraints[
bpy.context.scene.BIMProperties.active_constraint_index
].name
for obj in bpy.context.selected_objects:
index = obj.BIMObjectProperties.constraints.find(identification)
if index >= 0:
obj.BIMObjectProperties.constraints.remove(index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveObjectConstraint(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_object_constraint"
bl_label = "Remove Object Constraint"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.BIMObjectProperties.constraints.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddPerson(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_person"
bl_label = "Add Person"
def execute(self, context):
new = bpy.context.scene.BIMProperties.people.add()
2020-11-01 20:08:48 +07:00
new.name = "New Person"
return {"FINISHED"}
class RemovePerson(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_person"
bl_label = "Remove Person"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.BIMProperties.people.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddPersonAddress(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_person_address"
bl_label = "Add Person Address"
def execute(self, context):
2020-11-01 20:08:48 +07:00
new = bpy.context.scene.BIMProperties.people[
bpy.context.scene.BIMProperties.active_person_index
].addresses.add()
new.name = "IfcPostalAddress"
return {"FINISHED"}
class RemovePersonAddress(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_person_address"
bl_label = "Remove Person Address"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.people[bpy.context.scene.BIMProperties.active_person_index].addresses.remove(
self.index
)
return {"FINISHED"}
class AddPersonRole(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_person_role"
bl_label = "Add Person Role"
def execute(self, context):
new = bpy.context.scene.BIMProperties.people[bpy.context.scene.BIMProperties.active_person_index].roles.add()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemovePersonRole(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_person_role"
bl_label = "Remove Person Role"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.people[bpy.context.scene.BIMProperties.active_person_index].roles.remove(
self.index
)
return {"FINISHED"}
class AddOrganisation(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_organisation"
bl_label = "Add Organisation"
def execute(self, context):
new = bpy.context.scene.BIMProperties.organisations.add()
2020-11-01 20:08:48 +07:00
new.name = "New Organisation"
return {"FINISHED"}
class RemoveOrganisation(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_organisation"
bl_label = "Remove Organisation"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.BIMProperties.organisations.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddOrganisationAddress(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_organisation_address"
bl_label = "Add Organisation Address"
def execute(self, context):
2020-11-01 20:08:48 +07:00
new = bpy.context.scene.BIMProperties.organisations[
bpy.context.scene.BIMProperties.active_organisation_index
].addresses.add()
new.name = "IfcPostalAddress"
return {"FINISHED"}
class RemoveOrganisationAddress(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_organisation_address"
bl_label = "Remove Organisation Address"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.organisations[
bpy.context.scene.BIMProperties.active_organisation_index
].addresses.remove(self.index)
return {"FINISHED"}
class AddOrganisationRole(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_organisation_role"
bl_label = "Add Organisation Role"
def execute(self, context):
2020-11-01 20:08:48 +07:00
new = bpy.context.scene.BIMProperties.organisations[
bpy.context.scene.BIMProperties.active_organisation_index
].roles.add()
return {"FINISHED"}
class RemoveOrganisationRole(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_organisation_role"
bl_label = "Remove Organisation Role"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.organisations[
bpy.context.scene.BIMProperties.active_organisation_index
].roles.remove(self.index)
return {"FINISHED"}
class AddDocumentInformation(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_document_information"
bl_label = "Add Document Information"
def execute(self, context):
info = bpy.context.scene.BIMProperties.document_information.add()
2020-11-01 20:08:48 +07:00
info.name = "New Document ID"
return {"FINISHED"}
class RemoveDocumentInformation(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_document_information"
bl_label = "Remove Document Information"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.BIMProperties.document_information.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AssignDocumentInformation(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.assign_document_information"
bl_label = "Assign Document Information"
index: bpy.props.IntProperty()
def execute(self, context):
reference = bpy.context.scene.BIMProperties.document_references[self.index]
index = bpy.context.scene.BIMProperties.active_document_information_index
info = bpy.context.scene.BIMProperties.document_information
if index < len(info):
reference.referenced_document = info[index].name
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddDocumentReference(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_document_reference"
bl_label = "Add Document Reference"
def execute(self, context):
document = bpy.context.scene.BIMProperties.document_references.add()
2020-11-01 20:08:48 +07:00
document.name = "New Document Reference ID"
return {"FINISHED"}
class RemoveDocumentReference(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_document_reference"
bl_label = "Remove Document Reference"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.BIMProperties.document_references.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveObjectDocumentReference(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_object_document_reference"
bl_label = "Remove Object Document Reference"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.BIMObjectProperties.document_references.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AssignDocumentReference(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.assign_document_reference"
bl_label = "Assign Document Reference"
def execute(self, context):
2020-11-01 20:08:48 +07:00
identification = bpy.context.scene.BIMProperties.document_references[
bpy.context.scene.BIMProperties.active_document_reference_index
].name
for obj in bpy.context.selected_objects:
if obj.BIMObjectProperties.document_references.get(identification):
continue
reference = obj.BIMObjectProperties.document_references.add()
reference.name = identification
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class UnassignDocumentReference(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.unassign_document_reference"
bl_label = "Unassign Document Reference"
def execute(self, context):
2020-11-01 20:08:48 +07:00
identification = bpy.context.scene.BIMProperties.document_references[
bpy.context.scene.BIMProperties.active_document_reference_index
].name
for obj in bpy.context.selected_objects:
index = obj.BIMObjectProperties.document_references.find(identification)
if index >= 0:
obj.BIMObjectProperties.document_references.remove(index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveObjectDocumentReference(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_object_document_reference"
bl_label = "Remove Object Document Reference"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.BIMObjectProperties.document_references.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-10-16 07:23:03 +11:00
2019-10-16 23:17:48 +11:00
class GenerateGlobalId(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.generate_global_id"
bl_label = "Regenerate GlobalId"
2019-10-16 23:17:48 +11:00
def execute(self, context):
2020-11-01 20:08:48 +07:00
index = bpy.context.active_object.BIMObjectProperties.attributes.find("GlobalId")
if index >= 0:
global_id = bpy.context.active_object.BIMObjectProperties.attributes[index]
else:
global_id = bpy.context.active_object.BIMObjectProperties.attributes.add()
2020-11-01 20:08:48 +07:00
global_id.name = "GlobalId"
global_id.data_type = "string"
2019-10-16 23:17:48 +11:00
global_id.string_value = ifcopenshell.guid.new()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-10-16 23:17:48 +11:00
class AddAttribute(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_attribute"
bl_label = "Add Attribute"
2019-10-16 23:17:48 +11:00
def execute(self, context):
if not bpy.context.active_object.BIMObjectProperties.applicable_attributes:
return {"FINISHED"}
name = bpy.context.active_object.BIMObjectProperties.applicable_attributes
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(bpy.context.scene.BIMProperties.export_schema)
for obj in bpy.context.selected_objects:
if "/" not in obj.name or obj.BIMObjectProperties.attributes.find(name) != -1:
continue
entity = schema.declaration_by_name(obj.name.split("/")[0])
if name not in [a.name() for a in entity.all_attributes()]:
continue
attribute = obj.BIMObjectProperties.attributes.add()
attribute.name = name
2020-11-01 20:08:48 +07:00
if attribute.name == "GlobalId":
attribute.string_value = ifcopenshell.guid.new()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-10-16 23:17:48 +11:00
2020-01-14 15:38:22 +11:00
class AddMaterialAttribute(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_material_attribute"
bl_label = "Add Material Attribute"
2020-01-14 15:38:22 +11:00
def execute(self, context):
if bpy.context.active_object.active_material.BIMMaterialProperties.applicable_attributes:
attribute = bpy.context.active_object.active_material.BIMMaterialProperties.attributes.add()
attribute.name = bpy.context.active_object.active_material.BIMMaterialProperties.applicable_attributes
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-01-14 15:38:22 +11:00
2019-10-16 23:17:48 +11:00
class RemoveAttribute(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_attribute"
bl_label = "Remove Attribute"
2019-10-16 23:17:48 +11:00
attribute_index: bpy.props.IntProperty()
def execute(self, context):
name = bpy.context.active_object.BIMObjectProperties.attributes[self.attribute_index].name
for obj in bpy.context.selected_objects:
if "/" not in obj.name:
continue
index = obj.BIMObjectProperties.attributes.find(name)
if index != -1:
obj.BIMObjectProperties.attributes.remove(index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-10-16 23:17:48 +11:00
2020-01-14 15:38:22 +11:00
class RemoveMaterialAttribute(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_material_attribute"
bl_label = "Remove Material Attribute"
2020-01-14 15:38:22 +11:00
attribute_index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.active_material.BIMMaterialProperties.attributes.remove(self.attribute_index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-01-14 15:38:22 +11:00
class AddSweptSolid(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_swept_solid"
bl_label = "Add Swept Solid"
def execute(self, context):
swept_solids = bpy.context.active_object.data.BIMMeshProperties.swept_solids
swept_solid = swept_solids.add()
2020-11-01 20:08:48 +07:00
swept_solid.name = "Swept Solid {}".format(len(swept_solids))
return {"FINISHED"}
class RemoveSweptSolid(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_swept_solid"
bl_label = "Remove Swept Solid"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.data.BIMMeshProperties.swept_solids.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AssignSweptSolidOuterCurve(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.assign_swept_solid_outer_curve"
bl_label = "Assign Outer Curve"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if bpy.context.mode != "EDIT_MESH":
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
vertices = [v.index for v in bpy.context.active_object.data.vertices if v.select == True]
bpy.context.active_object.data.BIMMeshProperties.swept_solids[self.index].outer_curve = json.dumps(vertices)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectSweptSolidOuterCurve(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_swept_solid_outer_curve"
bl_label = "Select Outer Curve"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if bpy.context.mode != "EDIT_MESH":
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
outer_curve = bpy.context.active_object.data.BIMMeshProperties.swept_solids[self.index].outer_curve
if not outer_curve:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
indices = json.loads(outer_curve)
for index in indices:
bpy.context.active_object.data.vertices[index].select = True
2020-11-01 20:08:48 +07:00
bpy.ops.object.mode_set(mode="EDIT")
return {"FINISHED"}
class AddSweptSolidInnerCurve(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_swept_solid_inner_curve"
bl_label = "Add Inner Curve"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if bpy.context.mode != "EDIT_MESH":
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
vertices = [v.index for v in bpy.context.active_object.data.vertices if v.select == True]
swept_solid = bpy.context.active_object.data.BIMMeshProperties.swept_solids[self.index]
if swept_solid.inner_curves:
curves = json.loads(swept_solid.inner_curves)
else:
curves = []
curves.append(vertices)
swept_solid.inner_curves = json.dumps(curves)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectSweptSolidInnerCurves(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_swept_solid_inner_curves"
bl_label = "Select Inner Curves"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if bpy.context.mode != "EDIT_MESH":
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
inner_curves = bpy.context.active_object.data.BIMMeshProperties.swept_solids[self.index].inner_curves
if not inner_curves:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
curves = json.loads(inner_curves)
for curve in curves:
for index in curve:
bpy.context.active_object.data.vertices[index].select = True
2020-11-01 20:08:48 +07:00
bpy.ops.object.mode_set(mode="EDIT")
return {"FINISHED"}
class AssignSweptSolidExtrusion(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.assign_swept_solid_extrusion"
bl_label = "Assign Extrusion"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if bpy.context.mode != "EDIT_MESH":
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
vertices = [v.index for v in bpy.context.active_object.data.vertices if v.select == True]
bpy.context.active_object.data.BIMMeshProperties.swept_solids[self.index].extrusion = json.dumps(vertices)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectSweptSolidExtrusion(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_swept_solid_extrusion"
bl_label = "Select Extrusion"
index: bpy.props.IntProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if bpy.context.mode != "EDIT_MESH":
return {"FINISHED"}
bpy.ops.object.mode_set(mode="OBJECT")
extrusion = bpy.context.active_object.data.BIMMeshProperties.swept_solids[self.index].extrusion
if not extrusion:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
indices = json.loads(extrusion)
for index in indices:
bpy.context.active_object.data.vertices[index].select = True
2020-11-01 20:08:48 +07:00
bpy.ops.object.mode_set(mode="EDIT")
return {"FINISHED"}
class SelectExternalMaterialDir(bpy.types.Operator):
bl_idname = "bim.select_external_material_dir"
bl_label = "Select Material File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.active_object.active_material.BIMMaterialProperties.location = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class SelectCobieIfcFile(bpy.types.Operator):
bl_idname = "bim.select_cobie_ifc_file"
bl_label = "Select COBie IFC File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.cobie_ifc_file = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class SelectCobieJsonFile(bpy.types.Operator):
bl_idname = "bim.select_cobie_json_file"
bl_label = "Select COBie JSON File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.cobie_json_file = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class ExecuteIfcCobie(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.execute_ifc_cobie"
bl_label = "Execute IFCCOBie"
file_format: bpy.props.StringProperty()
def execute(self, context):
from cobie import IfcCobieParser
2020-11-01 20:08:48 +07:00
output_dir = os.path.dirname(bpy.context.scene.BIMProperties.cobie_ifc_file)
2020-11-01 20:08:48 +07:00
output = os.path.join(output_dir, "output")
logger = logging.getLogger("IFCtoCOBie")
fh = logging.FileHandler(os.path.join(output_dir, "cobie.log"))
fh.setLevel(logging.DEBUG)
2020-11-01 20:08:48 +07:00
fh.setFormatter(logging.Formatter("%(asctime)s : %(levelname)s : %(message)s"))
logger = logging.getLogger("IFCtoCOBie")
logger.addHandler(fh)
selector = ifcopenshell.util.selector.Selector()
if bpy.context.scene.BIMProperties.cobie_json_file:
2020-11-01 20:08:48 +07:00
with open(bpy.context.scene.BIMProperties.cobie_json_file, "r") as f:
custom_data = json.load(f)
else:
custom_data = {}
parser = IfcCobieParser(logger, selector)
parser.parse(
bpy.context.scene.BIMProperties.cobie_ifc_file,
bpy.context.scene.BIMProperties.cobie_types,
bpy.context.scene.BIMProperties.cobie_components,
2020-11-01 20:08:48 +07:00
custom_data,
)
if self.file_format == "xlsx":
from cobie import CobieXlsWriter
2020-11-01 20:08:48 +07:00
writer = CobieXlsWriter(parser, output)
writer.write()
2020-11-01 20:08:48 +07:00
webbrowser.open("file://" + output + "." + self.file_format)
elif self.file_format == "ods":
from cobie import CobieOdsWriter
2020-11-01 20:08:48 +07:00
writer = CobieOdsWriter(parser, output)
writer.write()
2020-11-01 20:08:48 +07:00
webbrowser.open("file://" + output + "." + self.file_format)
else:
from cobie import CobieCsvWriter
2020-11-01 20:08:48 +07:00
writer = CobieCsvWriter(parser, output_dir)
writer.write()
2020-11-01 20:08:48 +07:00
webbrowser.open("file://" + output_dir)
webbrowser.open("file://" + output_dir + "/cobie.log")
return {"FINISHED"}
2020-08-05 17:18:10 +10:00
class ExecuteIfcPatch(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.execute_ifc_patch"
bl_label = "Execute IFCPatch"
2020-08-05 17:18:10 +10:00
file_format: bpy.props.StringProperty()
def execute(self, context):
import ifcpatch
2020-11-01 20:08:48 +07:00
ifcpatch.execute(
{
"input": bpy.context.scene.BIMProperties.ifc_patch_input,
"output": bpy.context.scene.BIMProperties.ifc_patch_output,
"recipe": bpy.context.scene.BIMProperties.ifc_patch_recipes,
"arguments": json.loads("[" + bpy.context.scene.BIMProperties.ifc_patch_args + "]"),
"log": bpy.context.scene.BIMProperties.data_dir + "process.log",
}
)
return {"FINISHED"}
2020-08-05 17:18:10 +10:00
class SelectDiffJsonFile(bpy.types.Operator):
bl_idname = "bim.select_diff_json_file"
bl_label = "Select Diff JSON File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.diff_json_file = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
2020-01-07 14:27:34 +11:00
2020-08-04 19:00:34 +10:00
class VisualiseDiff(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.visualise_diff"
bl_label = "Visualise Diff"
2020-08-04 19:00:34 +10:00
def execute(self, context):
2020-11-01 20:08:48 +07:00
with open(bpy.context.scene.BIMProperties.diff_json_file, "r") as file:
2020-08-04 19:00:34 +10:00
diff = json.load(file)
for obj in bpy.context.visible_objects:
2020-11-01 20:08:48 +07:00
obj.color = (1.0, 1.0, 1.0, 0.2)
global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
2020-08-04 19:00:34 +10:00
if not global_id:
continue
2020-11-01 20:08:48 +07:00
if global_id.string_value in diff["deleted"]:
obj.color = (1.0, 0.0, 0.0, 0.2)
elif global_id.string_value in diff["added"]:
obj.color = (0.0, 1.0, 0.0, 0.2)
elif global_id.string_value in diff["changed"]:
obj.color = (0.0, 0.0, 1.0, 0.2)
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
return {"FINISHED"}
2020-08-04 19:00:34 +10:00
2020-01-07 14:27:34 +11:00
class SelectDiffOldFile(bpy.types.Operator):
bl_idname = "bim.select_diff_old_file"
bl_label = "Select Diff Old File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.diff_old_file = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-01-07 14:27:34 +11:00
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
2020-01-07 14:27:34 +11:00
class SelectDiffNewFile(bpy.types.Operator):
bl_idname = "bim.select_diff_new_file"
bl_label = "Select Diff New File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.diff_new_file = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-01-07 14:27:34 +11:00
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
2020-01-07 14:27:34 +11:00
class ExecuteIfcDiff(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.execute_ifc_diff"
bl_label = "Execute IFC Diff"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
2020-11-01 20:08:48 +07:00
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
2020-01-07 14:27:34 +11:00
def execute(self, context):
import ifcdiff
2020-11-01 20:08:48 +07:00
2020-01-07 14:27:34 +11:00
ifc_diff = ifcdiff.IfcDiff(
bpy.context.scene.BIMProperties.diff_old_file,
bpy.context.scene.BIMProperties.diff_new_file,
self.filepath,
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.diff_relationships.split(),
2020-01-07 14:27:34 +11:00
)
ifc_diff.diff()
ifc_diff.export()
bpy.context.scene.BIMProperties.diff_json_file = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-01-07 14:27:34 +11:00
class ExportClashSets(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.export_clash_sets"
bl_label = "Export Clash Sets"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
2020-11-01 20:08:48 +07:00
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
def execute(self, context):
2020-11-01 20:08:48 +07:00
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
clash_sets = []
for clash_set in bpy.context.scene.BIMProperties.clash_sets:
self.a = []
self.b = []
2020-11-01 20:08:48 +07:00
for ab in ["a", "b"]:
for data in getattr(clash_set, ab):
2020-11-01 20:08:48 +07:00
clash_source = {"file": data.name}
if data.selector:
2020-11-01 20:08:48 +07:00
clash_source["selector"] = data.selector
clash_source["mode"] = data.mode
getattr(self, ab).append(clash_source)
2020-11-01 20:08:48 +07:00
clash_sets.append({"name": clash_set.name, "tolerance": clash_set.tolerance, "a": self.a, "b": self.b})
with open(self.filepath, "w") as destination:
destination.write(json.dumps(clash_sets, indent=4))
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class ImportClashSets(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.import_clash_sets"
bl_label = "Import Clash Sets"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
2020-11-01 20:08:48 +07:00
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
def execute(self, context):
with open(self.filepath) as f:
clash_sets = json.load(f)
for clash_set in clash_sets:
new = bpy.context.scene.BIMProperties.clash_sets.add()
2020-11-01 20:08:48 +07:00
new.name = clash_set["name"]
new.tolerance = clash_set["tolerance"]
for clash_source in clash_set["a"]:
new_source = new.a.add()
2020-11-01 20:08:48 +07:00
new_source.name = clash_source["file"]
if "selector" in clash_source:
new_source.selector = clash_source["selector"]
new_source.mode = clash_source["mode"]
if clash_set["b"]:
for clash_source in clash_set["b"]:
new_source = new.b.add()
2020-11-01 20:08:48 +07:00
new_source.name = clash_source["file"]
if "selector" in clash_source:
new_source.selector = clash_source["selector"]
new_source.mode = clash_source["mode"]
return {"FINISHED"}
class AddClashSet(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_clash_set"
bl_label = "Add Clash Set"
def execute(self, context):
new = bpy.context.scene.BIMProperties.clash_sets.add()
2020-11-01 20:08:48 +07:00
new.name = "New Clash Set"
new.tolerance = 0.01
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveClashSet(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_clash_set"
bl_label = "Remove Clash Set"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.BIMProperties.clash_sets.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddClashSource(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_clash_source"
bl_label = "Add Clash Source"
group: bpy.props.StringProperty()
def execute(self, context):
clash_set = bpy.context.scene.BIMProperties.clash_sets[bpy.context.scene.BIMProperties.active_clash_set_index]
source = getattr(clash_set, self.group).add()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveClashSource(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_clash_source"
bl_label = "Remove Clash Source"
index: bpy.props.IntProperty()
group: bpy.props.StringProperty()
def execute(self, context):
clash_set = bpy.context.scene.BIMProperties.clash_sets[bpy.context.scene.BIMProperties.active_clash_set_index]
getattr(clash_set, self.group).remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectClashSource(bpy.types.Operator):
bl_idname = "bim.select_clash_source"
bl_label = "Select Clash Source"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
index: bpy.props.IntProperty()
group: bpy.props.StringProperty()
def execute(self, context):
clash_set = bpy.context.scene.BIMProperties.clash_sets[bpy.context.scene.BIMProperties.active_clash_set_index]
getattr(clash_set, self.group)[self.index].name = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class ExecuteIfcClash(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.execute_ifc_clash"
bl_label = "Execute IFC Clash"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
2020-11-01 20:08:48 +07:00
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
def execute(self, context):
import ifcclash
2020-11-01 20:08:48 +07:00
settings = ifcclash.IfcClashSettings()
2020-11-01 20:08:48 +07:00
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
settings.output = self.filepath
2020-11-01 20:08:48 +07:00
settings.logger = logging.getLogger("Clash")
settings.logger.setLevel(logging.DEBUG)
ifc_clasher = ifcclash.IfcClasher(settings)
ifc_clasher.clash_sets = []
for clash_set in bpy.context.scene.BIMProperties.clash_sets:
self.a = []
self.b = []
2020-11-01 20:08:48 +07:00
for ab in ["a", "b"]:
for data in getattr(clash_set, ab):
2020-11-01 20:08:48 +07:00
clash_source = {"file": data.name}
if data.selector:
2020-11-01 20:08:48 +07:00
clash_source["selector"] = data.selector
clash_source["mode"] = data.mode
getattr(self, ab).append(clash_source)
2020-11-01 20:08:48 +07:00
ifc_clasher.clash_sets.append(
{"name": clash_set.name, "tolerance": clash_set.tolerance, "a": self.a, "b": self.b}
)
ifc_clasher.clash()
ifc_clasher.export()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-05-25 19:07:09 +10:00
class SelectIfcClashResults(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_ifc_clash_results"
bl_label = "Select IFC Clash Results"
filename_ext = ".json"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
2020-05-25 19:07:09 +10:00
def invoke(self, context, event):
2020-11-01 20:08:48 +07:00
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
2020-05-25 19:07:09 +10:00
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
2020-05-25 19:07:09 +10:00
def execute(self, context):
2020-11-01 20:08:48 +07:00
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
2020-05-25 19:07:09 +10:00
with open(self.filepath) as f:
clash_sets = json.load(f)
clash_set_name = bpy.context.scene.BIMProperties.clash_sets[
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.active_clash_set_index
].name
2020-05-25 19:07:09 +10:00
global_ids = []
for clash_set in clash_sets:
2020-11-01 20:08:48 +07:00
if clash_set["name"] != clash_set_name:
2020-05-25 19:07:09 +10:00
continue
2020-11-01 20:08:48 +07:00
for clash in clash_set["clashes"].values():
global_ids.extend([clash["a_global_id"], clash["b_global_id"]])
2020-05-25 19:07:09 +10:00
for obj in bpy.context.visible_objects:
2020-11-01 20:08:48 +07:00
global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
2020-05-25 19:07:09 +10:00
if global_id and global_id.string_value in global_ids:
obj.select_set(True)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-05-25 19:07:09 +10:00
class SelectBcfFile(bpy.types.Operator):
bl_idname = "bim.select_bcf_file"
bl_label = "Select BCF File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
2020-03-24 17:53:58 +11:00
bpy.context.scene.BCFProperties.bcf_file = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class SelectFeaturesDir(bpy.types.Operator):
bl_idname = "bim.select_features_dir"
bl_label = "Select Features Directory"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.features_dir = (
os.path.dirname(os.path.abspath(self.filepath)) if "." in self.filepath else self.filepath
)
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class SelectIfcFile(bpy.types.Operator):
bl_idname = "bim.select_ifc_file"
bl_label = "Select IFC File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.ifc_file = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class ValidateIfcFile(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.validate_ifc_file"
bl_label = "Validate IFC File"
def execute(self, context):
import ifcopenshell.validate
2020-11-01 20:08:48 +07:00
logger = logging.getLogger("validate")
logger.setLevel(logging.DEBUG)
ifcopenshell.validate.validate(ifc.IfcStore.get_file(), logger)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectDataDir(bpy.types.Operator):
bl_idname = "bim.select_data_dir"
bl_label = "Select Data Directory"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.data_dir = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class SelectSchemaDir(bpy.types.Operator):
bl_idname = "bim.select_schema_dir"
bl_label = "Select Schema Directory"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.schema_dir = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
2019-11-01 16:32:11 +11:00
class CreateAggregate(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.create_aggregate"
bl_label = "Create Aggregate"
2019-11-01 16:32:11 +11:00
def execute(self, context):
spatial_container = None
for obj in bpy.context.selected_objects:
if obj.instance_collection:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-11-01 16:32:11 +11:00
for collection in obj.users_collection:
2020-11-01 20:08:48 +07:00
if "IfcRelAggregates" in collection.name:
return {"FINISHED"}
elif collection.name[0:3] == "Ifc":
2019-11-01 16:32:11 +11:00
spatial_container = collection
if not spatial_container:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-11-01 16:32:11 +11:00
2020-11-01 20:08:48 +07:00
aggregate = bpy.data.collections.new(
"IfcRelAggregates/{}".format(bpy.context.scene.BIMProperties.aggregate_class)
)
for project in [c for c in bpy.context.view_layer.layer_collection.children if "IfcProject" in c.name]:
if not [c for c in project.children if "Aggregates" in c.name]:
aggregates = bpy.data.collections.new("Aggregates")
project.collection.children.link(aggregates)
2020-11-01 20:08:48 +07:00
for aggregate_collection in [c for c in project.children if "Aggregates" in c.name]:
aggregate_collection.collection.children.link(aggregate)
aggregate_collection.children[aggregate.name].hide_viewport = True
break
break
2019-11-01 16:32:11 +11:00
for obj in bpy.context.selected_objects:
for collection in obj.users_collection:
collection.objects.unlink(obj)
aggregate.objects.link(obj)
2020-11-01 20:08:48 +07:00
instance = bpy.data.objects.new(
"{}/{}".format(
bpy.context.scene.BIMProperties.aggregate_class, bpy.context.scene.BIMProperties.aggregate_name
),
None,
)
instance.instance_type = "COLLECTION"
2019-11-01 16:32:11 +11:00
instance.instance_collection = aggregate
spatial_container.objects.link(instance)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-11-01 16:32:11 +11:00
class EditAggregate(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.edit_aggregate"
bl_label = "Edit Aggregate"
2019-11-01 16:32:11 +11:00
def execute(self, context):
obj = bpy.context.active_object
2020-11-01 20:08:48 +07:00
if obj.instance_type != "COLLECTION" or "IfcRelAggregates" not in obj.instance_collection.name:
return {"FINISHED"}
2019-11-01 16:32:11 +11:00
bpy.context.view_layer.objects[obj.name].hide_viewport = True
2020-11-01 20:08:48 +07:00
for project in [c for c in bpy.context.view_layer.layer_collection.children if "IfcProject" in c.name]:
for aggregate_collection in [c for c in project.children if "Aggregates" in c.name]:
for aggregate in [c for c in aggregate_collection.children if c.name == obj.instance_collection.name]:
aggregate.hide_viewport = False
break
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-11-01 16:32:11 +11:00
class SaveAggregate(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.save_aggregate"
bl_label = "Save Aggregate"
2019-11-01 16:32:11 +11:00
def execute(self, context):
obj = bpy.context.active_object
aggregate = None
names = [c.name for c in obj.users_collection]
2020-11-01 20:08:48 +07:00
for project in [c for c in bpy.context.view_layer.layer_collection.children if "IfcProject" in c.name]:
for aggregate_collection in [c for c in project.children if "Aggregates" in c.name]:
for collection in [c for c in aggregate_collection.children if c.name in names]:
collection.hide_viewport = True
aggregate = collection.collection
break
2019-11-01 16:32:11 +11:00
if not aggregate:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-11-01 16:32:11 +11:00
for obj in bpy.context.view_layer.objects:
if obj.instance_collection == aggregate:
obj.hide_viewport = False
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-11-01 17:44:10 +11:00
2020-01-08 15:58:39 +11:00
class ExplodeAggregate(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.explode_aggregate"
bl_label = "Explode Aggregate"
2020-01-08 15:58:39 +11:00
def execute(self, context):
obj = bpy.context.active_object
2020-11-01 20:08:48 +07:00
if obj.instance_type != "COLLECTION" or "IfcRelAggregates" not in obj.instance_collection.name:
return {"FINISHED"}
2020-01-08 15:58:39 +11:00
aggregate_collection = bpy.data.collections.get(obj.instance_collection.name)
spatial_collection = obj.users_collection[0]
for part in aggregate_collection.objects:
spatial_collection.objects.link(part)
aggregate_collection.objects.unlink(part)
bpy.data.objects.remove(obj, do_unlink=True)
bpy.data.collections.remove(aggregate_collection, do_unlink=True)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-01-08 15:58:39 +11:00
class LoadClassification(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.load_classification"
bl_label = "Load Classification"
is_file: bpy.props.BoolProperty()
classification_index: bpy.props.IntProperty()
def execute(self, context):
from . import prop
2020-11-01 20:08:48 +07:00
if self.is_file:
prop.ClassificationView.raw_data = schema.ifc.load_classification(
2020-11-01 20:08:48 +07:00
context.scene.BIMProperties.classification
)
else:
prop.ClassificationView.raw_data = schema.ifc.load_classification(
2020-11-01 20:08:48 +07:00
context.scene.BIMProperties.classifications[self.classification_index].name, self.classification_index
)
context.scene.BIMProperties.classification_references.root = ""
return {"FINISHED"}
class AddClassification(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_classification"
bl_label = "Add Classification"
def execute(self, context):
if context.scene.BIMProperties.classification not in schema.ifc.classifications:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
data = schema.ifc.classifications[context.scene.BIMProperties.classification]
classification = context.scene.BIMProperties.classifications.add()
data_map = {
2020-11-01 20:08:48 +07:00
"name": "Name",
"source": "Source",
"edition": "Edition",
"edition_date": "EditionDate",
"description": "Description",
"location": "Location",
"reference_tokens": "ReferenceTokens",
}
for key, value in data_map.items():
if hasattr(data, value) and getattr(data, value):
setattr(classification, key, str(getattr(data, value)))
classification.data = schema.ifc.classification_files[context.scene.BIMProperties.classification].to_string()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveClassification(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_classification"
bl_label = "Remove Classification"
classification_index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.BIMProperties.classifications.remove(self.classification_index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-11-01 17:44:10 +11:00
class AssignClassification(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.assign_classification"
bl_label = "Assign Classification"
2019-11-01 17:44:10 +11:00
def execute(self, context):
for obj in bpy.context.selected_objects:
classification = obj.BIMObjectProperties.classifications.add()
refs = bpy.context.scene.BIMProperties.classification_references
2020-11-01 20:08:48 +07:00
data = refs.root["children"][refs.children[refs.active_index].name]
if data["identification"]:
classification.name = data["identification"]
if data["name"]:
classification.human_name = data["name"]
for key in ["location", "description"]:
if data[key]:
setattr(classification, key, data[key])
classification.referenced_source = bpy.context.scene.BIMProperties.active_classification_name
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2019-11-01 17:44:10 +11:00
2019-11-01 17:44:10 +11:00
class UnassignClassification(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.unassign_classification"
bl_label = "Unassign Classification"
2019-11-01 17:44:10 +11:00
def execute(self, context):
refs = bpy.context.scene.BIMProperties.classification_references
key = refs.children[refs.active_index].name
2019-11-01 17:44:10 +11:00
for obj in bpy.context.selected_objects:
index = obj.BIMObjectProperties.classifications.find(key)
2019-11-01 17:44:10 +11:00
if index != -1:
obj.BIMObjectProperties.classifications.remove(index)
2019-11-01 17:44:10 +11:00
2020-11-01 20:08:48 +07:00
obj.BIMObjectProperties.classification = ""
return {"FINISHED"}
2019-11-01 17:44:10 +11:00
class RemoveClassificationReference(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_classification_reference"
bl_label = "Remove Classification Reference"
2019-11-01 17:44:10 +11:00
classification_index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.BIMObjectProperties.classifications.remove(self.classification_index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class FetchExternalMaterial(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.fetch_external_material"
bl_label = "Fetch External Material"
def execute(self, context):
location = bpy.context.active_object.active_material.BIMMaterialProperties.location
2020-11-01 20:08:48 +07:00
if location[-6:] != ".mpass":
return {"FINISHED"}
if not os.path.isabs(location):
2020-11-01 20:08:48 +07:00
location = os.path.join(os.path.join(bpy.context.scene.BIMProperties.data_dir, location))
with open(location) as f:
self.material_pass = json.load(f)
2020-11-01 20:08:48 +07:00
if bpy.context.scene.render.engine == "BLENDER_EEVEE" and "eevee" in self.material_pass:
self.fetch_eevee_or_cycles("eevee")
elif bpy.context.scene.render.engine == "CYCLES" and "cycles" in self.material_pass:
self.fetch_eevee_or_cycles("cycles")
return {"FINISHED"}
def fetch_eevee_or_cycles(self, name):
identification = bpy.context.active_object.active_material.BIMMaterialProperties.identification
2020-11-01 20:08:48 +07:00
uri = self.material_pass[name]["uri"]
if not os.path.isabs(uri):
2020-11-01 20:08:48 +07:00
uri = os.path.join(os.path.join(bpy.context.scene.BIMProperties.data_dir, uri))
bpy.ops.wm.link(filename=identification, directory=os.path.join(uri, "Material"))
for material in bpy.data.materials:
2020-11-01 20:08:48 +07:00
if material.name == identification and material.library:
bpy.context.active_object.material_slots[0].material = material
return
class FetchLibraryInformation(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.fetch_library_information"
bl_label = "Fetch Library Information"
def execute(self, context):
# TODO
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class FetchObjectPassport(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.fetch_object_passport"
bl_label = "Fetch Object Passport"
def execute(self, context):
for reference in bpy.context.active_object.BIMObjectProperties.document_references:
reference = bpy.context.scene.BIMProperties.document_references[reference.name]
2020-11-01 20:08:48 +07:00
if reference.location[-6:] == ".blend":
self.fetch_blender(reference)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def fetch_blender(self, reference):
2020-11-01 20:08:48 +07:00
bpy.ops.wm.link(filename=reference.name, directory=os.path.join(reference.location, "Mesh"))
bpy.context.active_object.data = bpy.data.meshes[reference.name]
class AddSubcontext(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_subcontext"
bl_label = "Add Subcontext"
2019-12-07 17:02:59 +11:00
context: bpy.props.StringProperty()
def execute(self, context):
2019-12-07 17:02:59 +11:00
props = bpy.context.scene.BIMProperties
2020-11-01 20:08:48 +07:00
subcontext = getattr(bpy.context.scene.BIMProperties, "{}_subcontexts".format(self.context)).add()
subcontext.name = bpy.context.scene.BIMProperties.available_subcontexts
subcontext.target_view = bpy.context.scene.BIMProperties.available_target_views
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveSubcontext(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_subcontext"
bl_label = "Remove Context"
indexes: bpy.props.StringProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
context, subcontext_index = self.indexes.split("-")
subcontext_index = int(subcontext_index)
2020-11-01 20:08:48 +07:00
getattr(bpy.context.scene.BIMProperties, "{}_subcontexts".format(context)).remove(subcontext_index)
return {"FINISHED"}
class OpenView(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.open_view"
bl_label = "Open View"
view: bpy.props.StringProperty()
def execute(self, context):
open_with_user_command(
2020-11-01 20:08:48 +07:00
bpy.context.preferences.addons["blenderbim"].preferences.svg_command,
os.path.join(bpy.context.scene.BIMProperties.data_dir, "diagrams", self.view + ".svg"),
)
return {"FINISHED"}
class CutSection(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.cut_section"
bl_label = "Cut Section"
def execute(self, context):
camera = bpy.context.scene.camera
2020-11-01 20:08:48 +07:00
if not (camera.type == "CAMERA" and camera.data.type == "ORTHO"):
return {"FINISHED"}
bpy.ops.bim.activate_view(
drawing_index=bpy.context.scene.DocProperties.drawings.find(camera.name.split("/")[1])
)
drawing_style = bpy.context.scene.DocProperties.drawing_styles[
camera.data.BIMCameraProperties.active_drawing_style_index
]
self.diagram_name = camera.name.split("/")[1]
bpy.context.scene.render.filepath = os.path.join(
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.data_dir, "diagrams", "{}.png".format(self.diagram_name)
)
self.create_raster(camera, drawing_style)
location = camera.location
render = bpy.context.scene.render
if self.is_landscape():
width = camera.data.ortho_scale
height = width / render.resolution_x * render.resolution_y
else:
height = camera.data.ortho_scale
width = height / render.resolution_y * render.resolution_x
depth = camera.data.clip_end
projection = camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
x_axis = camera.matrix_world.to_quaternion() @ Vector((1, 0, 0))
y_axis = camera.matrix_world.to_quaternion() @ Vector((0, -1, 0))
top_left_corner = location - (width / 2 * x_axis) - (height / 2 * y_axis)
ifc_cutter = cut_ifc.IfcCutter()
import ifccsv
2020-11-01 20:08:48 +07:00
ifc_cutter.ifc_filenames = [i.name for i in bpy.context.scene.DocProperties.ifc_files]
ifc_cutter.data_dir = bpy.context.scene.BIMProperties.data_dir
ifc_cutter.vector_style = drawing_style.vector_style
ifc_cutter.diagram_name = self.diagram_name
ifc_cutter.background_image = bpy.context.scene.render.filepath
2020-11-01 20:08:48 +07:00
if camera.data.BIMCameraProperties.cut_objects == "CUSTOM":
ifc_cutter.cut_objects = camera.data.BIMCameraProperties.cut_objects_custom
else:
ifc_cutter.cut_objects = camera.data.BIMCameraProperties.cut_objects
ifc_cutter.leader_obj = None
ifc_cutter.stair_obj = None
ifc_cutter.dimension_objs = []
2020-05-11 10:00:54 +10:00
ifc_cutter.break_obj = None
ifc_cutter.equal_objs = []
ifc_cutter.hidden_objs = []
ifc_cutter.solid_objs = []
ifc_cutter.plan_level_obj = None
ifc_cutter.section_level_obj = None
ifc_cutter.grid_objs = []
ifc_cutter.text_objs = []
ifc_cutter.misc_objs = []
ifc_cutter.attributes = [a.name for a in drawing_style.attributes]
for obj in camera.users_collection[0].objects:
2020-11-01 20:08:48 +07:00
if "IfcGrid" in obj.name:
ifc_cutter.grid_objs.append(obj)
2020-11-01 20:08:48 +07:00
elif "IfcGroup" in obj.name and obj.type == "CAMERA":
ifc_cutter.camera_obj = obj
2020-11-01 20:08:48 +07:00
if "IfcAnnotation/" not in obj.name:
continue
2020-11-01 20:08:48 +07:00
if "Leader" in obj.name:
ifc_cutter.leader_obj = (obj, obj.data)
2020-11-01 20:08:48 +07:00
elif "Stair" in obj.name:
ifc_cutter.stair_obj = obj
2020-11-01 20:08:48 +07:00
elif "Equal" in obj.name:
ifc_cutter.equal_objs.append(obj)
2020-11-01 20:08:48 +07:00
elif "Dimension" in obj.name:
ifc_cutter.dimension_objs.append(obj)
2020-11-01 20:08:48 +07:00
elif "Break" in obj.name:
2020-05-11 10:00:54 +10:00
ifc_cutter.break_obj = obj
2020-11-01 20:08:48 +07:00
elif "Hidden" in obj.name:
ifc_cutter.hidden_objs.append((obj, obj.data))
2020-11-01 20:08:48 +07:00
elif "Solid" in obj.name:
ifc_cutter.solid_objs.append((obj, obj.data))
2020-11-01 20:08:48 +07:00
elif "Plan Level" in obj.name:
ifc_cutter.plan_level_obj = obj
2020-11-01 20:08:48 +07:00
elif "Section Level" in obj.name:
ifc_cutter.section_level_obj = obj
2020-11-01 20:08:48 +07:00
elif obj.type == "FONT":
ifc_cutter.text_objs.append(obj)
else:
ifc_cutter.misc_objs.append(obj)
ifc_cutter.section_box = {
2020-11-01 20:08:48 +07:00
"projection": tuple(projection),
"x_axis": tuple(x_axis),
"y_axis": tuple(y_axis),
"top_left_corner": tuple(top_left_corner),
"x": width,
"y": height,
"z": depth,
"shape": None,
"face": None,
}
2020-11-01 20:08:48 +07:00
ifc_cutter.cut_pickle_file = os.path.join(ifc_cutter.data_dir, "{}-cut.pickle".format(self.diagram_name))
ifc_cutter.text_pickle_file = os.path.join(ifc_cutter.data_dir, "{}-text.pickle".format(self.diagram_name))
ifc_cutter.metadata_pickle_file = os.path.join(
ifc_cutter.data_dir, "{}-metadata.pickle".format(self.diagram_name)
)
2019-12-12 18:16:02 +11:00
ifc_cutter.should_recut = bpy.context.scene.DocProperties.should_recut
ifc_cutter.should_recut_selected = bpy.context.scene.DocProperties.should_recut_selected
selected_global_ids = []
for obj in bpy.context.selected_objects:
2020-11-01 20:08:48 +07:00
if "Ifc" not in obj.name:
continue
for attribute in obj.BIMObjectProperties.attributes:
2020-11-01 20:08:48 +07:00
if attribute.name == "GlobalId":
selected_global_ids.append(attribute.string_value)
break
ifc_cutter.selected_global_ids = selected_global_ids
ifc_cutter.should_extract = bpy.context.scene.DocProperties.should_extract
svg_writer = svgwriter.SvgWriter(ifc_cutter)
2020-11-01 20:08:48 +07:00
if camera.data.BIMCameraProperties.diagram_scale == "CUSTOM":
human_scale, fraction = camera.data.BIMCameraProperties.custom_diagram_scale.split("|")
else:
2020-11-01 20:08:48 +07:00
human_scale, fraction = camera.data.BIMCameraProperties.diagram_scale.split("|")
numerator, denominator = fraction.split("/")
if camera.data.BIMCameraProperties.is_nts:
2020-11-01 20:08:48 +07:00
svg_writer.human_scale = "NTS"
else:
svg_writer.human_scale = human_scale
2019-12-12 12:51:47 +11:00
svg_writer.scale = float(numerator) / float(denominator)
ifc_cutter.cut()
svg_writer.write()
bpy.ops.bim.open_view(view=self.diagram_name)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def create_raster(self, camera, drawing_style):
2020-11-01 20:08:48 +07:00
if drawing_style.render_type == "NONE":
return
2020-11-01 20:08:48 +07:00
if drawing_style.render_type == "DEFAULT":
return bpy.ops.render.render(write_still=True)
previous_visibility = {}
for obj in camera.users_collection[0].objects:
previous_visibility[obj.name] = obj.hide_get()
obj.hide_set(True)
for obj in bpy.context.visible_objects:
2020-11-01 20:08:48 +07:00
if (
not obj.data
or isinstance(obj.data, bpy.types.Camera)
or "IfcGrid/" in obj.name
or "IfcGridAxis/" in obj.name
or "IfcOpeningElement/" in obj.name
or self.does_obj_have_target_view_representation(obj, camera)
):
previous_visibility[obj.name] = obj.hide_get()
obj.hide_set(True)
space = self.get_view_3d()
previous_shading = space.shading.type
previous_format = bpy.context.scene.render.image_settings.file_format
2020-11-01 20:08:48 +07:00
space.shading.type = "RENDERED"
bpy.context.scene.render.image_settings.file_format = "PNG"
bpy.ops.render.opengl(write_still=True)
space.shading.type = previous_shading
bpy.context.scene.render.image_settings.file_format = previous_format
for name, value in previous_visibility.items():
bpy.data.objects[name].hide_set(value)
def does_obj_have_target_view_representation(self, obj, camera):
2020-11-01 20:08:48 +07:00
return camera.data.BIMCameraProperties.target_view in [
c.target_view for c in obj.BIMObjectProperties.representation_contexts
]
def is_landscape(self):
return bpy.context.scene.render.resolution_x > bpy.context.scene.render.resolution_y
def get_view_3d(self):
for area in bpy.context.screen.areas:
2020-11-01 20:08:48 +07:00
if area.type != "VIEW_3D":
continue
for space in area.spaces:
2020-11-01 20:08:48 +07:00
if space.type != "VIEW_3D":
continue
return space
class AddSheet(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_sheet"
bl_label = "Add Sheet"
def execute(self, context):
new = bpy.context.scene.DocProperties.sheets.add()
2020-11-01 20:08:48 +07:00
new.name = "{} - SHEET".format(len(bpy.context.scene.DocProperties.sheets))
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
sheet_builder.create(new.name, bpy.context.scene.DocProperties.titleblock)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class OpenSheet(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.open_sheet"
bl_label = "Open Sheet"
def execute(self, context):
props = bpy.context.scene.DocProperties
open_with_user_command(
2020-11-01 20:08:48 +07:00
bpy.context.preferences.addons["blenderbim"].preferences.svg_command,
os.path.join(
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.data_dir, "sheets", props.sheets[props.active_sheet_index].name + ".svg"
),
)
return {"FINISHED"}
class AddDrawingToSheet(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_drawing_to_sheet"
bl_label = "Add Drawing To Sheet"
def execute(self, context):
props = bpy.context.scene.DocProperties
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
try:
sheet_builder.add_drawing(
2020-11-01 20:08:48 +07:00
props.drawings[props.active_drawing_index].name, props.sheets[props.active_sheet_index].name
)
except:
2020-11-01 20:08:48 +07:00
self.report({"ERROR"}, "Drawings need to be created before being added to a sheet")
return {"FINISHED"}
class CreateSheets(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.create_sheets"
bl_label = "Create Sheets"
def execute(self, context):
props = bpy.context.scene.DocProperties
name = props.sheets[props.active_sheet_index].name
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
sheet_builder.build(name)
2020-11-01 20:08:48 +07:00
svg2pdf_command = bpy.context.preferences.addons["blenderbim"].preferences.svg2pdf_command
svg2dxf_command = bpy.context.preferences.addons["blenderbim"].preferences.svg2dxf_command
if svg2pdf_command:
2020-11-01 20:08:48 +07:00
path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name)
svg = os.path.join(path, name + ".svg")
pdf = os.path.join(path, name + ".pdf")
# With great power comes great responsibility. Example:
# [['inkscape', svg, '-o', pdf]]
commands = eval(svg2pdf_command)
for command in commands:
subprocess.run(command)
if svg2dxf_command:
2020-11-01 20:08:48 +07:00
path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name)
svg = os.path.join(path, name + ".svg")
eps = os.path.join(path, name + ".eps")
dxf = os.path.join(path, name + ".dxf")
base = os.path.join(path, name)
# With great power comes great responsibility. Example:
# [['inkscape', svg, '-o', eps], ['pstoedit', '-dt', '-f', 'dxf:-polyaslines -mm', eps, dxf, '-psarg', '-dNOSAFER']]
commands = eval(svg2dxf_command)
for command in commands:
subprocess.run(command)
if svg2pdf_command:
2020-11-01 20:08:48 +07:00
open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.pdf_command, pdf)
else:
open_with_user_command(
2020-11-01 20:08:48 +07:00
bpy.context.preferences.addons["blenderbim"].preferences.svg_command,
os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", name, name + ".svg"),
)
return {"FINISHED"}
2020-01-06 14:30:44 +11:00
class ActivateView(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.activate_view"
bl_label = "Activate View"
2020-09-03 23:02:41 +10:00
drawing_index: bpy.props.IntProperty()
2020-01-06 14:30:44 +11:00
def execute(self, context):
2020-09-03 23:02:41 +10:00
camera = bpy.context.scene.DocProperties.drawings[self.drawing_index].camera
2020-01-06 14:30:44 +11:00
if not camera:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-01-06 14:30:44 +11:00
bpy.context.scene.camera = camera
2020-11-01 20:08:48 +07:00
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].region_3d.view_perspective = "CAMERA"
views_collection = bpy.data.collections.get("Views")
2020-01-06 14:30:44 +11:00
for collection in views_collection.children:
# We assume the project collection is at the top level
for project_collection in bpy.context.view_layer.layer_collection.children:
# We assume a convention that the 'Views' collection is directly
# in the project collection
2020-11-01 20:08:48 +07:00
if (
"Views" in project_collection.children
and collection.name in project_collection.children["Views"].children
):
project_collection.children["Views"].children[collection.name].hide_viewport = True
bpy.data.collections.get(collection.name).hide_render = True
2020-11-01 20:08:48 +07:00
bpy.context.view_layer.layer_collection.children["Views"].children[
camera.users_collection[0].name
].hide_viewport = False
2020-01-06 14:30:44 +11:00
bpy.data.collections.get(camera.users_collection[0].name).hide_render = False
bpy.ops.bim.activate_drawing_style()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SwitchContext(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.switch_context"
bl_label = "Switch Context"
has_target_context: bpy.props.BoolProperty()
context_name: bpy.props.StringProperty()
subcontext_name: bpy.props.StringProperty()
target_view_name: bpy.props.StringProperty()
# Warning: This is an incredibly experimental operator. It effectively does
# a mini-import. A better approach will make this obsolete in the future.
def execute(self, context):
self.obj = bpy.context.active_object
2020-11-01 20:08:48 +07:00
if "/" not in self.obj.data.name:
self.obj.data.name = ifcopenshell.guid.compress(str(uuid.uuid4()).replace("-", ""))
self.obj.data.name = "Model/Body/MODEL_VIEW/" + self.obj.data.name
representation_context = self.obj.BIMObjectProperties.representation_contexts.add()
2020-11-01 20:08:48 +07:00
representation_context.context = "Model"
representation_context.name = "Body"
representation_context.target_view = "MODEL_VIEW"
self.context = bpy.context.scene.BIMProperties.available_contexts
self.subcontext = bpy.context.scene.BIMProperties.available_subcontexts
self.target_view = bpy.context.scene.BIMProperties.available_target_views
if self.has_target_context:
self.context = self.context_name
self.subcontext = self.subcontext_name
self.target_view = self.target_view_name
existing_mesh = self.obj.data
existing_mesh.use_fake_user = True
2020-11-01 20:08:48 +07:00
mesh = bpy.data.meshes.get(
"{}/{}/{}/{}".format(self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3])
)
if not mesh:
try:
2020-11-01 20:08:48 +07:00
global_id = self.obj.BIMObjectProperties.attributes.get("GlobalId").string_value
mesh = self.pull_mesh_from_ifc(global_id)
except:
mesh = self.obj.data.copy()
2020-11-01 20:08:48 +07:00
mesh.name = "{}/{}/{}/{}".format(
self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3]
)
has_context = False
for context in self.obj.BIMObjectProperties.representation_contexts:
2020-11-01 20:08:48 +07:00
if (
context.context == self.context
and context.name == self.subcontext
and context.target_view == self.target_view
):
has_context = True
break
if not has_context:
representation_context = self.obj.BIMObjectProperties.representation_contexts.add()
representation_context.context = self.context
representation_context.name = self.subcontext
representation_context.target_view = self.target_view
mesh.use_fake_user = True
self.obj.data = mesh
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def pull_mesh_from_ifc(self, global_id):
self.file = ifc.IfcStore.get_file()
2020-11-01 20:08:48 +07:00
logger = logging.getLogger("ImportIFC")
ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, ifc.IfcStore.path, logger)
element = self.file.by_id(global_id)
settings = ifcopenshell.geom.settings()
settings.set(settings.INCLUDE_CURVES, True)
2020-11-01 20:08:48 +07:00
if element.is_a("IfcProduct"):
representations = element.Representation.Representations
else:
representations = element.RepresentationMaps
for rep in element.Representation.Representations:
2020-11-01 20:08:48 +07:00
if (
rep.ContextOfItems.is_a("IfcGeometricRepresentationSubContext")
and rep.ContextOfItems.ContextType == self.context
and rep.ContextOfItems.ContextIdentifier == self.subcontext
and rep.ContextOfItems.TargetView == self.target_view
):
break
2020-11-01 20:08:48 +07:00
elif (
rep.ContextOfItems.is_a("IfcGeometricRepresentationContext")
and rep.ContextOfItems.ContextType == self.context
and rep.ContextOfItems.ContextIdentifier == self.subcontext
):
break
2020-11-01 20:08:48 +07:00
if not element.is_a("IfcProduct"):
rep = rep.MappedRepresentation
shape = ifcopenshell.geom.create_shape(settings, rep)
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
ifc_importer.file = self.file
mesh = ifc_importer.create_mesh(element, shape)
2020-11-01 20:08:48 +07:00
mesh.name = "{}/{}/{}/{}".format(
self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3]
)
self.obj.data = mesh
material_creator = import_ifc.MaterialCreator(ifc_import_settings)
material_creator.create(element, self.obj, mesh)
return mesh
class RemoveContext(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_context"
bl_label = "Remove Context"
index: bpy.props.IntProperty()
def execute(self, context):
obj = bpy.context.active_object
data = obj.BIMObjectProperties.representation_contexts[self.index]
2020-11-01 20:08:48 +07:00
if "/" not in obj.data.name:
obj.data.name = "Model/Body/MODEL_VIEW/" + obj.data.name
2020-11-01 20:08:48 +07:00
mesh = bpy.data.meshes.get(
"{}/{}/{}/{}".format(data.context, data.name, data.target_view, obj.data.name.split("/")[3])
)
if mesh:
if obj.data == mesh:
2020-11-01 20:08:48 +07:00
void_name = "Void/Void/Void/" + obj.data.name.split("/")[3]
void_mesh = bpy.data.meshes.get(void_name)
if not void_mesh:
void_mesh = bpy.data.meshes.new(void_name)
obj.data = void_mesh
bpy.data.meshes.remove(mesh)
obj.BIMObjectProperties.representation_contexts.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class OpenUpstream(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.open_upstream"
bl_label = "Open Upstream Reference"
page: bpy.props.StringProperty()
def execute(self, context):
2020-11-01 20:08:48 +07:00
if self.page == "home":
webbrowser.open("https://blenderbim.org/")
elif self.page == "docs":
webbrowser.open("https://blenderbim.org/docs/")
elif self.page == "wiki":
webbrowser.open("https://wiki.osarch.org/index.php?title=Category:BlenderBIM_Add-on")
elif self.page == "community":
webbrowser.open("https://community.osarch.org/")
return {"FINISHED"}
class BIM_OT_CopyAttributesToSelection(bpy.types.Operator):
"""Copies attributes from the active object towards selected objects"""
2020-11-01 20:08:48 +07:00
bl_idname = "bim.copy_attributes_to_selection"
bl_label = "Copy Attributes To Selection"
2020-11-01 20:08:48 +07:00
prop_base = bpy.props.StringProperty() # data for properties to assign to
prop_name = bpy.props.StringProperty(description="Property name which to change")
2020-11-01 20:08:48 +07:00
sub_props = bpy.props.StringProperty() # properties which to copy (commasep). (empty = all)
collection_element = bpy.props.BoolProperty(description="If this is a collection element, copy the complete thing")
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
active_object = bpy.context.active_object
2020-11-01 20:08:48 +07:00
selected_objects = [
obj
for obj in bpy.context.visible_objects
if obj.type == active_object.type and obj in bpy.context.selected_objects and obj != active_object
]
if self.prop_base:
2020-11-01 20:08:48 +07:00
prop_base = eval("active_object." + self.prop_base)
else:
prop_base = active_object
if not self.collection_element:
self.copy_simple(prop_base, selected_objects)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
self.copy_collection(prop_base, selected_objects)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def copy_simple(self, prop_base, selected_objects):
prop = getattr(prop_base, self.prop_name)
for obj in selected_objects:
if self.prop_base:
2020-11-01 20:08:48 +07:00
new_prop_base = eval("obj." + self.prop_base)
else:
new_prop_base = obj
setattr(new_prop_base, self.prop_name, prop)
def copy_collection(self, prop_base, selected_objects):
prop = prop_base[self.prop_name]
for obj in selected_objects:
if self.prop_base:
2020-11-01 20:08:48 +07:00
new_prop_base = eval("obj." + self.prop_base)
else:
new_prop_base = obj
if self.prop_name in new_prop_base:
new_prop_base = new_prop_base[self.prop_name]
else:
new_prop_base = new_prop_base.add()
if self.sub_props:
2020-11-01 20:08:48 +07:00
for p in self.sub_props.replace(" ", "").split(","):
try:
setattr(new_prop_base, p, getattr(prop, p))
2020-11-01 20:08:48 +07:00
except:
pass
else:
for p in dir(prop):
try:
setattr(new_prop_base, p, getattr(prop, p))
2020-11-01 20:08:48 +07:00
except:
pass
class CopyPropertyToSelection(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.copy_property_to_selection"
bl_label = "Copy Property To Selection"
pset_name: bpy.props.StringProperty()
prop_name: bpy.props.StringProperty()
prop_value: bpy.props.StringProperty()
def execute(self, context):
self.applicable_psets_cache = {}
self.empty = ifcopenshell.file()
for obj in bpy.context.selected_objects:
2020-11-01 20:08:48 +07:00
if "/" not in obj.name:
continue
pset = obj.BIMObjectProperties.psets.get(self.pset_name)
if not pset:
2020-11-01 20:08:48 +07:00
applicable_psets = self.get_applicable_psets(obj.name.split("/")[0])
if self.pset_name not in applicable_psets:
continue
pset = obj.BIMObjectProperties.psets.add()
pset.name = self.pset_name
2020-11-01 20:08:48 +07:00
for template_prop_name in schema.ifc.psets[self.pset_name]["HasPropertyTemplates"].keys():
prop = pset.properties.add()
prop.name = template_prop_name
prop = pset.properties.get(self.prop_name)
if prop:
prop.string_value = self.prop_value
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
# TODO: move into util module. See bug #971
def get_applicable_psets(self, element_class):
if element_class not in self.applicable_psets_cache:
element = self.empty.create_entity(element_class)
applicable_psets = []
for ifc_class, pset_names in schema.ifc.applicable_psets.items():
if element.is_a(ifc_class):
applicable_psets.extend(pset_names)
self.applicable_psets_cache[element_class] = applicable_psets
return self.applicable_psets_cache[element_class]
class BIM_OT_ChangeClassificationLevel(bpy.types.Operator):
bl_idname = "bim.change_classification_level"
bl_label = "Change Classification Level"
# string representing the id-data (e.g. the scene).
path_sid: bpy.props.StringProperty()
# path from the id-data to the classification view object
path_lst: bpy.props.StringProperty()
# name of child entity to enter (empty = go up one level)
path_itm: bpy.props.StringProperty()
def invoke(self, context, event):
id_data = eval(self.path_sid)
lst = id_data.path_resolve(self.path_lst)
if self.path_itm:
lst.root = self.path_itm
else:
2020-11-01 20:08:48 +07:00
lst.root = ""
return {"FINISHED"}
2020-04-24 17:14:07 +10:00
class AddPropertySetTemplate(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_property_set_template"
bl_label = "Add Property Set Template"
2020-04-24 17:14:07 +10:00
def execute(self, context):
2020-11-01 20:08:48 +07:00
context.scene.BIMProperties.active_property_set_template.global_id = ""
context.scene.BIMProperties.active_property_set_template.name = "New_Pset"
context.scene.BIMProperties.active_property_set_template.description = ""
context.scene.BIMProperties.active_property_set_template.template_type = "PSET_TYPEDRIVENONLY"
context.scene.BIMProperties.active_property_set_template.applicable_entity = "IfcTypeObject"
while len(bpy.context.scene.BIMProperties.property_templates) > 0:
bpy.context.scene.BIMProperties.property_templates.remove(0)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-24 17:14:07 +10:00
class RemovePropertySetTemplate(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_property_set_template"
bl_label = "Remove Property Set Template"
2020-04-24 17:14:07 +10:00
def execute(self, context):
template = ifc.IfcStore.pset_template_file.by_guid(context.scene.BIMProperties.property_set_templates)
ifc.IfcStore.pset_template_file.remove(template)
ifc.IfcStore.pset_template_file.write(ifc.IfcStore.pset_template_path)
from . import prop
2020-11-01 20:08:48 +07:00
prop.refreshPropertySetTemplates(self, context)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-24 17:14:07 +10:00
class EditPropertySetTemplate(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.edit_property_set_template"
bl_label = "Edit Property Set Template"
2020-04-24 17:14:07 +10:00
def execute(self, context):
template = ifc.IfcStore.pset_template_file.by_guid(context.scene.BIMProperties.property_set_templates)
2020-04-24 17:14:07 +10:00
context.scene.BIMProperties.active_property_set_template.global_id = template.GlobalId
context.scene.BIMProperties.active_property_set_template.name = template.Name
context.scene.BIMProperties.active_property_set_template.description = template.Description
context.scene.BIMProperties.active_property_set_template.template_type = template.TemplateType
context.scene.BIMProperties.active_property_set_template.applicable_entity = template.ApplicableEntity
while len(bpy.context.scene.BIMProperties.property_templates) > 0:
bpy.context.scene.BIMProperties.property_templates.remove(0)
if template.HasPropertyTemplates:
for property_template in template.HasPropertyTemplates:
2020-11-01 20:08:48 +07:00
if not property_template.is_a("IfcSimplePropertyTemplate"):
continue
new = context.scene.BIMProperties.property_templates.add()
new.global_id = property_template.GlobalId
new.name = property_template.Name
new.description = property_template.Description
new.primary_measure_type = property_template.PrimaryMeasureType
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-24 17:14:07 +10:00
class SavePropertySetTemplate(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.save_property_set_template"
bl_label = "Save Property Set Template"
2020-04-24 17:14:07 +10:00
def execute(self, context):
blender_property_set_template = context.scene.BIMProperties.active_property_set_template
if blender_property_set_template.global_id:
template = ifc.IfcStore.pset_template_file.by_guid(blender_property_set_template.global_id)
else:
template = ifc.IfcStore.pset_template_file.createIfcPropertySetTemplate()
template.GlobalId = ifcopenshell.guid.new()
template.Name = blender_property_set_template.name
template.Description = blender_property_set_template.description
template.TemplateType = blender_property_set_template.template_type
template.ApplicableEntity = blender_property_set_template.applicable_entity
saved_global_ids = []
for blender_property_template in context.scene.BIMProperties.property_templates:
if blender_property_template.global_id:
property_template = ifc.IfcStore.pset_template_file.by_guid(blender_property_template.global_id)
else:
property_template = ifc.IfcStore.pset_template_file.createIfcSimplePropertyTemplate()
property_template.GlobalId = ifcopenshell.guid.new()
if template.HasPropertyTemplates:
has_property_templates = list(template.HasPropertyTemplates)
else:
has_property_templates = []
has_property_templates.append(property_template)
template.HasPropertyTemplates = has_property_templates
property_template.Name = blender_property_template.name
property_template.Description = blender_property_template.description
property_template.PrimaryMeasureType = blender_property_template.primary_measure_type
2020-11-01 20:08:48 +07:00
property_template.TemplateType = "P_SINGLEVALUE"
property_template.AccessState = "READWRITE"
saved_global_ids.append(property_template.GlobalId)
for element in template.HasPropertyTemplates:
if element.GlobalId not in saved_global_ids:
ifc.IfcStore.pset_template_file.remove(element)
ifc.IfcStore.pset_template_file.write(ifc.IfcStore.pset_template_path)
from . import prop
2020-11-01 20:08:48 +07:00
prop.refreshPropertySetTemplates(self, context)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-24 17:14:07 +10:00
class AddPropertyTemplate(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_property_template"
bl_label = "Add Property Template"
2020-04-24 17:14:07 +10:00
def execute(self, context):
context.scene.BIMProperties.property_templates.add()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemovePropertyTemplate(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_property_template"
bl_label = "Remove Property Template"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.BIMProperties.property_templates.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddSectionPlane(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_section_plane"
bl_label = "Add Temporary Section Cutaway"
def execute(self, context):
obj = self.create_section_obj()
if not self.has_section_override_node():
self.create_section_compare_node()
self.create_section_override_node(obj)
else:
self.append_obj_to_section_override_node(obj)
self.add_default_material_if_none_exists()
self.override_materials()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def create_section_obj(self):
2020-11-01 20:08:48 +07:00
section = bpy.data.objects.new("Section", None)
section.empty_display_type = "SINGLE_ARROW"
section.empty_display_size = 5
section.show_in_front = True
2020-11-01 20:08:48 +07:00
if bpy.context.active_object.select_get() and isinstance(bpy.context.active_object.data, bpy.types.Camera):
section.matrix_world = (
bpy.context.active_object.matrix_world @ Euler((radians(180.0), 0.0, 0.0), "XYZ").to_matrix().to_4x4()
)
else:
2020-11-01 20:08:48 +07:00
section.rotation_euler = Euler((radians(180.0), 0.0, 0.0), "XYZ")
section.location = bpy.context.scene.cursor.location
2020-11-01 20:08:48 +07:00
collection = bpy.data.collections.get("Sections")
if not collection:
2020-11-01 20:08:48 +07:00
collection = bpy.data.collections.new("Sections")
bpy.context.scene.collection.children.link(collection)
collection.objects.link(section)
return section
def has_section_override_node(self):
2020-11-01 20:08:48 +07:00
return bpy.data.node_groups.get("Section Override")
def create_section_compare_node(self):
2020-11-01 20:08:48 +07:00
group = bpy.data.node_groups.new("Section Compare", type="ShaderNodeTree")
group_input = group.nodes.new(type="NodeGroupInput")
group_output = group.nodes.new(type="NodeGroupOutput")
separate_xyz_a = group.nodes.new(type="ShaderNodeSeparateXYZ")
separate_xyz_b = group.nodes.new(type="ShaderNodeSeparateXYZ")
gt_a = group.nodes.new(type="ShaderNodeMath")
gt_a.operation = "GREATER_THAN"
gt_a.inputs[1].default_value = 0
2020-11-01 20:08:48 +07:00
gt_b = group.nodes.new(type="ShaderNodeMath")
gt_b.operation = "GREATER_THAN"
gt_b.inputs[1].default_value = 0
2020-11-01 20:08:48 +07:00
add = group.nodes.new(type="ShaderNodeMath")
compare = group.nodes.new(type="ShaderNodeMath")
compare.operation = "COMPARE"
compare.inputs[1].default_value = 2
2020-11-01 20:08:48 +07:00
group.links.new(group_input.outputs[""], separate_xyz_a.inputs[0])
group.links.new(group_input.outputs[""], separate_xyz_b.inputs[0])
group.links.new(separate_xyz_a.outputs[2], gt_a.inputs[0])
group.links.new(separate_xyz_b.outputs[2], gt_b.inputs[0])
group.links.new(gt_a.outputs[0], add.inputs[0])
group.links.new(gt_b.outputs[0], add.inputs[1])
group.links.new(add.outputs[0], compare.inputs[0])
2020-11-01 20:08:48 +07:00
group.links.new(compare.outputs[0], group_output.inputs[""])
def create_section_override_node(self, obj):
2020-11-01 20:08:48 +07:00
group = bpy.data.node_groups.new("Section Override", type="ShaderNodeTree")
2020-11-01 20:08:48 +07:00
group_input = group.nodes.new(type="NodeGroupInput")
group_output = group.nodes.new(type="NodeGroupOutput")
2020-11-01 20:08:48 +07:00
backfacing = group.nodes.new(type="ShaderNodeNewGeometry")
backfacing_mix = group.nodes.new(type="ShaderNodeMixShader")
emission = group.nodes.new(type="ShaderNodeEmission")
emission.inputs[0].default_value = list(bpy.context.scene.BIMProperties.section_plane_colour) + [1]
2020-11-01 20:08:48 +07:00
group.links.new(backfacing.outputs["Backfacing"], backfacing_mix.inputs[0])
group.links.new(group_input.outputs[""], backfacing_mix.inputs[1])
group.links.new(emission.outputs["Emission"], backfacing_mix.inputs[2])
2020-11-01 20:08:48 +07:00
transparent = group.nodes.new(type="ShaderNodeBsdfTransparent")
section_mix = group.nodes.new(type="ShaderNodeMixShader")
section_mix.name = "Section Mix"
2020-11-01 20:08:48 +07:00
group.links.new(transparent.outputs["BSDF"], section_mix.inputs[1])
group.links.new(backfacing_mix.outputs["Shader"], section_mix.inputs[2])
2020-11-01 20:08:48 +07:00
group.links.new(section_mix.outputs["Shader"], group_output.inputs[""])
2020-11-01 20:08:48 +07:00
cut_obj = group.nodes.new(type="ShaderNodeTexCoord")
cut_obj.object = obj
2020-11-01 20:08:48 +07:00
section_compare = group.nodes.new(type="ShaderNodeGroup")
section_compare.node_tree = bpy.data.node_groups.get("Section Compare")
section_compare.name = "Last Section Compare"
value = group.nodes.new(type="ShaderNodeValue")
value.name = "Mock Section"
group.links.new(cut_obj.outputs["Object"], section_compare.inputs[0])
group.links.new(value.outputs[0], section_compare.inputs[1])
group.links.new(section_compare.outputs[0], section_mix.inputs[0])
def append_obj_to_section_override_node(self, obj):
2020-11-01 20:08:48 +07:00
group = bpy.data.node_groups.get("Section Override")
cut_obj = group.nodes.new(type="ShaderNodeTexCoord")
cut_obj.object = obj
2020-11-01 20:08:48 +07:00
section_compare = group.nodes.new(type="ShaderNodeGroup")
section_compare.node_tree = bpy.data.node_groups.get("Section Compare")
2020-11-01 20:08:48 +07:00
last_compare = group.nodes.get("Last Section Compare")
last_compare.name = "Section Compare"
mock_section = group.nodes.get("Mock Section")
section_mix = group.nodes.get("Section Mix")
group.links.new(last_compare.outputs[0], section_compare.inputs[0])
group.links.new(mock_section.outputs[0], section_compare.inputs[1])
2020-11-01 20:08:48 +07:00
group.links.new(cut_obj.outputs["Object"], last_compare.inputs[1])
group.links.new(section_compare.outputs[0], section_mix.inputs[0])
2020-11-01 20:08:48 +07:00
section_compare.name = "Last Section Compare"
def add_default_material_if_none_exists(self):
2020-11-01 20:08:48 +07:00
material = bpy.data.materials.get("Section Override")
if not material:
2020-11-01 20:08:48 +07:00
material = bpy.data.materials.new("Section Override")
material.use_nodes = True
if bpy.context.scene.BIMProperties.should_section_selected_objects:
objects = list(bpy.context.selected_objects)
else:
objects = list(bpy.context.visible_objects)
for obj in objects:
aggregate = obj.instance_collection
2020-11-01 20:08:48 +07:00
if aggregate and "IfcRelAggregates/" in aggregate.name:
for part in aggregate.objects:
objects.append(part)
2020-11-01 20:08:48 +07:00
if not (obj.data and hasattr(obj.data, "materials") and obj.data.materials and obj.data.materials[0]):
if obj.data and hasattr(obj.data, "materials"):
2020-04-25 19:09:34 +10:00
if len(obj.material_slots):
obj.material_slots[0].material = material
else:
obj.data.materials.append(material)
def override_materials(self):
2020-11-01 20:08:48 +07:00
override = bpy.data.node_groups.get("Section Override")
for material in bpy.data.materials:
material.use_nodes = True
2020-11-01 20:08:48 +07:00
if material.node_tree.nodes.get("Section Override"):
continue
2020-11-01 20:08:48 +07:00
material.blend_method = "HASHED"
material.shadow_method = "HASHED"
material_output = self.get_node(material.node_tree.nodes, "OUTPUT_MATERIAL")
if not material_output:
continue
from_socket = material_output.inputs[0].links[0].from_socket
2020-11-01 20:08:48 +07:00
section_override = material.node_tree.nodes.new(type="ShaderNodeGroup")
section_override.name = "Section Override"
section_override.node_tree = override
material.node_tree.links.new(from_socket, section_override.inputs[0])
material.node_tree.links.new(section_override.outputs[0], material_output.inputs[0])
def get_node(self, nodes, node_type):
for node in nodes:
if node.type == node_type:
return node
2020-04-25 19:09:34 +10:00
class RemoveSectionPlane(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_section_plane"
bl_label = "Remove Temporary Section Cutaway"
2020-04-25 19:09:34 +10:00
def execute(self, context):
name = bpy.context.active_object.name
2020-11-01 20:08:48 +07:00
section_override = bpy.data.node_groups.get("Section Override")
2020-04-25 19:09:34 +10:00
if not section_override:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-25 19:09:34 +10:00
for node in section_override.nodes:
2020-11-01 20:08:48 +07:00
if node.type != "TEX_COORD" or node.object.name != name:
2020-04-25 19:09:34 +10:00
continue
2020-11-01 20:08:48 +07:00
section_compare = node.outputs["Object"].links[0].to_node
2020-04-25 19:09:34 +10:00
# If the tex coord links to section_compare.inputs[1], it is called 'Input_3'
2020-11-01 20:08:48 +07:00
if node.outputs["Object"].links[0].to_socket.identifier == "Input_3":
2020-04-25 19:09:34 +10:00
section_override.links.new(
2020-11-01 20:08:48 +07:00
section_compare.inputs[0].links[0].from_socket, section_compare.outputs[0].links[0].to_socket
)
else: # If it links to section_compare.inputs[0]
if section_compare.inputs[1].links[0].from_node.name == "Mock Section":
2020-04-25 19:09:34 +10:00
# Then it is the very last section. Purge everything.
self.purge_all_section_data()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-25 19:09:34 +10:00
section_override.links.new(
2020-11-01 20:08:48 +07:00
section_compare.inputs[1].links[0].from_socket, section_compare.outputs[0].links[0].to_socket
)
2020-04-25 19:09:34 +10:00
section_override.nodes.remove(section_compare)
section_override.nodes.remove(node)
2020-11-01 20:08:48 +07:00
old_last_compare = section_override.nodes.get("Last Section Compare")
old_last_compare.name = "Section Compare"
section_mix = section_override.nodes.get("Section Mix")
new_last_compare = section_mix.inputs[0].links[0].from_node
2020-11-01 20:08:48 +07:00
new_last_compare.name = "Last Section Compare"
2020-04-25 19:09:34 +10:00
bpy.ops.object.delete({"selected_objects": [bpy.context.active_object]})
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-25 19:09:34 +10:00
def purge_all_section_data(self):
2020-11-01 20:08:48 +07:00
bpy.data.materials.remove(bpy.data.materials.get("Section Override"))
2020-04-25 19:09:34 +10:00
for material in bpy.data.materials:
if not material.node_tree:
continue
2020-11-01 20:08:48 +07:00
override = material.node_tree.nodes.get("Section Override")
if not override:
continue
2020-04-25 19:09:34 +10:00
material.node_tree.links.new(
2020-11-01 20:08:48 +07:00
override.inputs[0].links[0].from_socket, override.outputs[0].links[0].to_socket
)
2020-04-25 19:09:34 +10:00
material.node_tree.nodes.remove(override)
2020-11-01 20:08:48 +07:00
bpy.data.node_groups.remove(bpy.data.node_groups.get("Section Override"))
bpy.data.node_groups.remove(bpy.data.node_groups.get("Section Compare"))
2020-04-25 19:09:34 +10:00
bpy.ops.object.delete({"selected_objects": [bpy.context.active_object]})
class AddCsvAttribute(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_csv_attribute"
bl_label = "Add CSV Attribute"
def execute(self, context):
attribute = bpy.context.scene.BIMProperties.csv_attributes.add()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveCsvAttribute(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_csv_attribute"
bl_label = "Remove CSV Attribute"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.BIMProperties.csv_attributes.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class ExportIfcCsv(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.export_ifccsv"
bl_label = "Export IFC to CSV"
filename_ext = ".csv"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
2020-11-01 20:08:48 +07:00
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".csv")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
def execute(self, context):
import ifccsv
2020-11-01 20:08:48 +07:00
self.filepath = bpy.path.ensure_ext(self.filepath, ".csv")
2020-08-08 17:58:45 +10:00
ifc_file = ifcopenshell.open(bpy.context.scene.BIMProperties.ifc_file)
selector = ifcopenshell.util.selector.Selector()
results = selector.parse(ifc_file, bpy.context.scene.BIMProperties.ifc_selector)
ifc_csv = ifccsv.IfcCsv()
ifc_csv.output = self.filepath
ifc_csv.attributes = [a.name for a in bpy.context.scene.BIMProperties.csv_attributes]
2020-08-08 17:58:45 +10:00
ifc_csv.selector = selector
ifc_csv.export(ifc_file, results)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class ImportIfcCsv(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.import_ifccsv"
bl_label = "Import CSV to IFC"
filename_ext = ".csv"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def invoke(self, context, event):
2020-11-01 20:08:48 +07:00
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".csv")
WindowManager = context.window_manager
WindowManager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
def execute(self, context):
import ifccsv
2020-11-01 20:08:48 +07:00
ifc_csv = ifccsv.IfcCsv()
ifc_csv.output = self.filepath
ifc_csv.Import(bpy.context.scene.BIMProperties.ifc_file)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class EyedropIfcCsv(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.eyedrop_ifccsv"
bl_label = "Query Selected Items"
def execute(self, context):
global_ids = []
for obj in bpy.context.selected_objects:
2020-11-01 20:08:48 +07:00
if hasattr(obj, "BIMObjectProperties") and obj.BIMObjectProperties.attributes.get("GlobalId"):
global_ids.append("#" + obj.BIMObjectProperties.attributes.get("GlobalId").string_value)
bpy.context.scene.BIMProperties.ifc_selector = "|".join(global_ids)
return {"FINISHED"}
2020-04-28 14:52:04 +10:00
class ReloadIfcFile(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.reload_ifc_file"
bl_label = "Reload IFC File"
2020-04-28 14:52:04 +10:00
def execute(self, context):
self.diff_ifc()
self.reimport_ifc(context)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-28 14:52:04 +10:00
def diff_ifc(self):
import ifcdiff
2020-11-01 20:08:48 +07:00
temp_file = tempfile.NamedTemporaryFile(delete=False)
2020-04-28 14:52:04 +10:00
temp_file.close()
ifc_diff = ifcdiff.IfcDiff(
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.ifc_cache, bpy.context.scene.BIMProperties.ifc_file, temp_file.name
2020-04-28 14:52:04 +10:00
)
ifc_diff.diff()
ifc_diff.export()
bpy.context.scene.BIMProperties.diff_json_file = temp_file.name
def reimport_ifc(self, context):
2020-11-01 20:08:48 +07:00
logger = logging.getLogger("ImportIFC")
2020-04-28 14:52:04 +10:00
logging.basicConfig(
2020-11-01 20:08:48 +07:00
filename=bpy.context.scene.BIMProperties.data_dir + "process.log", filemode="a", level=logging.DEBUG
)
2020-04-28 14:52:04 +10:00
ifc_import_settings = import_ifc.IfcImportSettings.factory(
2020-11-01 20:08:48 +07:00
context, bpy.context.scene.BIMProperties.ifc_file, logger
)
2020-04-28 14:52:04 +10:00
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
ifc_importer.execute()
2020-04-29 20:26:36 +10:00
class SelectSimilarType(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_similar_type"
bl_label = "Select Similar Type"
2020-04-29 20:26:36 +10:00
def execute(self, context):
if context.active_object.BIMObjectProperties.relating_type:
relating_type = context.active_object.BIMObjectProperties.relating_type
2020-11-01 20:08:48 +07:00
elif "Type/" in context.active_object.name:
2020-04-29 20:26:36 +10:00
relating_type = context.active_object
else:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-04-29 20:26:36 +10:00
for obj in bpy.context.visible_objects:
if obj.BIMObjectProperties.relating_type == relating_type:
obj.select_set(True)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddIfcFile(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_ifc_file"
bl_label = "Add IFC File"
def execute(self, context):
bpy.context.scene.DocProperties.ifc_files.add()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveIfcFile(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_ifc_file"
bl_label = "Remove IFC File"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.DocProperties.ifc_files.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectDocIfcFile(bpy.types.Operator):
bl_idname = "bim.select_doc_ifc_file"
bl_label = "Select Documentation IFC File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.DocProperties.ifc_files[self.index].name = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
2020-05-03 15:41:05 +10:00
class AddAnnotation(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_annotation"
bl_label = "Add Annotation"
2020-05-03 15:41:05 +10:00
obj_name = bpy.props.StringProperty()
data_type = bpy.props.StringProperty()
def execute(self, context):
if not bpy.context.scene.camera:
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
if self.data_type == "text":
if bpy.context.selected_objects:
for selected_object in bpy.context.selected_objects:
obj = annotation.Annotator.add_text(related_element=selected_object)
else:
obj = annotation.Annotator.add_text()
2020-05-03 15:41:05 +10:00
else:
obj = annotation.Annotator.get_annotation_obj(self.obj_name, self.data_type)
obj = annotation.Annotator.add_line_to_annotation(obj)
2020-11-01 20:08:48 +07:00
bpy.ops.object.select_all(action="DESELECT")
2020-05-03 15:41:05 +10:00
bpy.context.view_layer.objects.active = obj
2020-11-01 20:08:48 +07:00
bpy.ops.object.mode_set(mode="EDIT")
return {"FINISHED"}
class GenerateReferences(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.generate_references"
bl_label = "Generate References"
def execute(self, context):
self.camera = bpy.context.scene.camera
self.filter_potential_references()
2020-11-01 20:08:48 +07:00
if self.camera.data.BIMCameraProperties.target_view == "PLAN_VIEW":
self.generate_grids()
2020-11-01 20:08:48 +07:00
if self.camera.data.BIMCameraProperties.target_view == "ELEVATION_VIEW":
self.generate_grids()
self.generate_levels()
2020-11-01 20:08:48 +07:00
if self.camera.data.BIMCameraProperties.target_view == "SECTION_VIEW":
self.generate_grids()
self.generate_levels()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def filter_potential_references(self):
2020-11-01 20:08:48 +07:00
for name in ["grids", "levels"]:
setattr(self, name, [])
for obj in bpy.data.objects:
2020-11-01 20:08:48 +07:00
if "IfcGridAxis/" in obj.name:
self.grids.append(obj)
2020-11-01 20:08:48 +07:00
if "IfcBuildingStorey/" in obj.name:
self.levels.append(obj)
def generate_grids(self):
# TODO
pass
def generate_levels(self):
if self.camera.data.BIMCameraProperties.raster_x > self.camera.data.BIMCameraProperties.raster_y:
width = self.camera.data.ortho_scale
2020-11-01 20:08:48 +07:00
height = (
width / self.camera.data.BIMCameraProperties.raster_x * self.camera.data.BIMCameraProperties.raster_y
)
else:
height = self.camera.data.ortho_scale
2020-11-01 20:08:48 +07:00
width = (
height / self.camera.data.BIMCameraProperties.raster_y * self.camera.data.BIMCameraProperties.raster_x
)
level_obj = annotation.Annotator.get_annotation_obj("Section Level", "curve")
width_in_mm = width * 1000
2020-11-01 20:08:48 +07:00
if self.camera.data.BIMCameraProperties.diagram_scale == "CUSTOM":
human_scale, fraction = self.camera.data.BIMCameraProperties.custom_diagram_scale.split("|")
else:
2020-11-01 20:08:48 +07:00
human_scale, fraction = self.camera.data.BIMCameraProperties.diagram_scale.split("|")
numerator, denominator = fraction.split("/")
scale = float(numerator) / float(denominator)
real_world_width_in_mm = width_in_mm * scale
offset_in_mm = 20
offset_percentage = offset_in_mm / real_world_width_in_mm
for obj in self.levels:
projection = self.project_point_onto_camera(obj.location)
2020-11-01 20:08:48 +07:00
co1 = self.camera.matrix_world @ Vector((width / 2 - (offset_percentage * width), projection[1], -1))
co2 = self.camera.matrix_world @ Vector((-(width / 2), projection[1], -1))
annotation.Annotator.add_line_to_annotation(level_obj, co1, co2)
def project_point_onto_camera(self, point):
projection = self.camera.matrix_world.to_quaternion() @ Vector((0, 0, -1))
return self.camera.matrix_world.inverted() @ geometry.intersect_line_plane(
2020-11-01 20:08:48 +07:00
point.xyz, point.xyz - projection, self.camera.location, projection
)
class ResizeText(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.resize_text"
bl_label = "Resize Text"
def execute(self, context):
for obj in bpy.context.scene.camera.users_collection[0].objects:
if isinstance(obj.data, bpy.types.TextCurve):
annotation.Annotator.resize_text(obj)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddVariable(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_variable"
bl_label = "Add Variable"
def execute(self, context):
bpy.context.active_object.data.BIMTextProperties.variables.add()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveVariable(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_variable"
bl_label = "Remove Variable"
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.active_object.data.BIMTextProperties.variables.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class PropagateTextData(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.propagate_text_data"
bl_label = "Propagate Text Data"
def execute(self, context):
source = bpy.context.active_object
for obj in bpy.context.selected_objects:
if obj == source:
continue
obj.data.body = source.data.body
obj.data.align_x = source.data.align_x
obj.data.align_y = source.data.align_y
obj.data.BIMTextProperties.font_size = source.data.BIMTextProperties.font_size
obj.data.BIMTextProperties.symbol = source.data.BIMTextProperties.symbol
while len(obj.data.BIMTextProperties.variables) > 0:
obj.data.BIMTextProperties.variables.remove(0)
for variable in source.data.BIMTextProperties.variables:
new_variable = obj.data.BIMTextProperties.variables.add()
new_variable.name = variable.name
new_variable.prop_key = variable.prop_key
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class PushRepresentation(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.push_representation"
bl_label = "Push Representation"
# Warning: This is an incredibly experimental operator.
def execute(self, context):
2020-05-09 12:47:26 +10:00
self.file = ifc.IfcStore.get_file()
2020-11-01 20:08:48 +07:00
logger = logging.getLogger("ExportIFC")
output_file = "tmp.ifc"
ifc_export_settings = export_ifc.IfcExportSettings.factory(context, output_file, logger)
qto_calculator = qto.QtoCalculator()
ifc_parser = export_ifc.IfcParser(ifc_export_settings, qto_calculator)
ifc_parser.parse([bpy.context.active_object])
self.ifc_exporter = export_ifc.IfcExporter(ifc_export_settings, ifc_parser)
2020-05-09 12:47:26 +10:00
self.ifc_exporter.file = ifcopenshell.file(schema=self.file.schema)
self.ifc_exporter.create_origin()
self.ifc_exporter.create_rep_context()
self.ifc_exporter.create_representations()
2020-11-01 20:08:48 +07:00
self.context, self.subcontext, self.target_view, self.mesh_name = bpy.context.active_object.data.name.split("/")
rep_context = self.get_geometric_representation_context()
for key, rep in self.ifc_exporter.ifc_parser.representations.items():
if key != bpy.context.active_object.data.name:
continue
if rep_context:
self.ifc_exporter.file.add(rep_context)
2020-11-01 20:08:48 +07:00
rep["ifc"].MappedRepresentation.ContextOfItems = rep_context
self.push_representation(rep["ifc"])
break
2020-11-01 20:08:48 +07:00
self.file.write(bpy.context.scene.BIMProperties.ifc_file[0:-4] + "-patch.ifc")
return {"FINISHED"}
def get_geometric_representation_context(self):
2020-11-01 20:08:48 +07:00
for element in self.file.by_type("IfcGeometricRepresentationSubContext"):
if self.is_current_context(element):
return element
def push_representation(self, new_representation):
2020-11-01 20:08:48 +07:00
element = self.file.by_guid(
bpy.context.active_object.BIMObjectProperties.attributes.get("GlobalId").string_value
)
old_shape = None
new_shape = self.file.add(new_representation.MappedRepresentation)
2020-11-01 20:08:48 +07:00
if element.is_a("IfcProduct"):
representations = element.Representation.Representations
else:
representations = [rm.MappedRepresentation for rm in element.RepresentationMaps]
for representation in representations:
if self.is_current_context(representation.ContextOfItems):
old_shape = self.resolve_mapped_representation(representation)
break
if old_shape:
self.swap_old_representation(old_shape, new_shape)
else:
self.add_new_representation(element, new_shape)
def resolve_mapped_representation(self, representation):
2020-11-01 20:08:48 +07:00
if representation.RepresentationType == "MappedRepresentation":
if representation.Items:
return representation.Items[0].MappingSource.MappedRepresentation
return representation
def swap_old_representation(self, old, new):
inverse_elements = self.file.get_inverse(old)
for element in inverse_elements:
for i, attribute in enumerate(element):
2020-11-01 20:08:48 +07:00
if (isinstance(attribute, list) or isinstance(attribute, tuple)) and old in attribute:
items = list(attribute)
for j, item in enumerate(items):
if item == old:
del items[j]
items.append(new)
element[i] = items
elif attribute == old:
element[i] = new
def add_new_representation(self, element, new):
2020-11-01 20:08:48 +07:00
if element.is_a("IfcProduct"):
self.add_new_representation_to_product(element, new)
return
if element.RepresentationMaps:
representation_maps = list(element.RepresentationMaps)
representation_maps.append(self.file.createIfcRepresentationMap(self.ifc_exporter.origin, new))
element.RepresentationMaps = representation_maps
else:
2020-11-01 20:08:48 +07:00
element.RepresentationMaps = self.file.createIfcRepresentationMap(self.ifc_exporter.origin, new)
2020-11-01 20:08:48 +07:00
if hasattr(element, "Types"):
related_objects = element.Types[0].RelatedObjects
2020-11-01 20:08:48 +07:00
elif hasattr(element, "ObjectTypeOf"): # IFC2X3
related_objects = element.ObjectTypeOf[0].RelatedObjects
for related_object in related_objects:
self.add_new_representation_to_product(related_object, new)
def add_new_representation_to_product(self, element, new):
representations = list(element.Representation.Representations)
representations.append(new)
element.Representation.Representations = representations
def is_current_context(self, element):
2020-11-01 20:08:48 +07:00
return (
element.ContextType == self.context
and element.ContextIdentifier == self.subcontext
and element.TargetView == self.target_view
2020-11-01 20:08:48 +07:00
)
class ConvertLocalToGlobal(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.convert_local_to_global"
bl_label = "Convert Local To Global"
def execute(self, context):
x, y, z = bpy.context.scene.cursor.location
if bpy.context.scene.MapConversion.scale:
scale = float(bpy.context.scene.MapConversion.scale)
else:
2020-11-01 20:08:48 +07:00
scale = 1.0
rotation = atan2(
float(bpy.context.scene.MapConversion.x_axis_ordinate),
2020-11-01 20:08:48 +07:00
float(bpy.context.scene.MapConversion.x_axis_abscissa),
)
a = scale * cos(rotation)
b = scale * sin(rotation)
eastings = (a * x) - (b * y) + float(bpy.context.scene.MapConversion.eastings)
northings = (b * x) + (a * y) + float(bpy.context.scene.MapConversion.northings)
height = z + float(bpy.context.scene.MapConversion.orthogonal_height)
bpy.context.scene.cursor.location = (eastings, northings, height)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class GuessQuantity(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.guess_quantity"
bl_label = "Guess Quantity"
qto_index: bpy.props.IntProperty()
prop_index: bpy.props.IntProperty()
def execute(self, context):
self.qto_calculator = qto.QtoCalculator()
source_qto = bpy.context.active_object.BIMObjectProperties.qtos[self.qto_index]
props = source_qto.properties
prop = props[self.prop_index]
for obj in bpy.context.selected_objects:
dest_qto = obj.BIMObjectProperties.qtos.get(source_qto.name)
if not dest_qto:
if source_qto.name not in obj.BIMObjectProperties.qto_name:
continue
dest_qto = self.add_qto(obj, source_qto.name)
prop = dest_qto.properties.get(prop.name)
self.guess_quantity(obj, prop, props)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def guess_quantity(self, obj, prop, props):
2020-11-01 20:08:48 +07:00
quantity = self.qto_calculator.guess_quantity(prop.name, [p.name for p in props], obj)
if "area" in prop.name.lower():
if bpy.context.scene.BIMProperties.area_unit:
prefix, name = self.get_prefix_name(bpy.context.scene.BIMProperties.area_unit)
2020-11-01 20:08:48 +07:00
quantity = helper.SIUnitHelper.convert(quantity, None, "SQUARE_METRE", prefix, name)
elif "volume" in prop.name.lower():
if bpy.context.scene.BIMProperties.volume_unit:
prefix, name = self.get_prefix_name(bpy.context.scene.BIMProperties.volume_unit)
2020-11-01 20:08:48 +07:00
quantity = helper.SIUnitHelper.convert(quantity, None, "CUBIC_METRE", prefix, name)
else:
prefix, name = self.get_blender_prefix_name()
2020-11-01 20:08:48 +07:00
quantity = helper.SIUnitHelper.convert(quantity, None, "METRE", prefix, name)
prop.string_value = str(round(quantity, 3))
def add_qto(self, obj, name):
if name not in schema.ifc.qtos:
return
qto = obj.BIMObjectProperties.qtos.add()
qto.name = name
2020-11-01 20:08:48 +07:00
for prop_name in schema.ifc.qtos[name]["HasPropertyTemplates"].keys():
prop = qto.properties.add()
prop.name = prop_name
return qto
def get_prefix_name(self, value):
2020-11-01 20:08:48 +07:00
if "/" in value:
return value.split("/")
return None, value
def get_blender_prefix_name(self):
2020-11-01 20:08:48 +07:00
if bpy.context.scene.unit_settings.system == "IMPERIAL":
if bpy.context.scene.unit_settings.length_unit == "INCHES":
return None, "inch"
elif bpy.context.scene.unit_settings.length_unit == "FEET":
return None, "foot"
elif bpy.context.scene.unit_settings.system == "METRIC":
if bpy.context.scene.unit_settings.length_unit == "METERS":
return None, "METRE"
return bpy.context.scene.unit_settings.length_unit[0 : -len("METERS")], "METRE"
class ExecuteBIMTester(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.execute_bim_tester"
bl_label = "Execute BIMTester"
def execute(self, context):
import bimtester
2020-11-01 20:08:48 +07:00
filename = os.path.join(
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.features_dir, bpy.context.scene.BIMProperties.features_file + ".feature"
)
cwd = os.getcwd()
os.chdir(bpy.context.scene.BIMProperties.features_dir)
2020-11-01 20:08:48 +07:00
bimtester.run_tests({"feature": filename, "advanced_arguments": None, "console": False})
bimtester.generate_report()
2020-11-01 20:08:48 +07:00
webbrowser.open(
"file://"
+ os.path.join(
bpy.context.scene.BIMProperties.features_dir,
"report",
bpy.context.scene.BIMProperties.features_file + ".feature.html",
)
)
os.chdir(cwd)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class BIMTesterPurge(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.bim_tester_purge"
bl_label = "Purge Tests"
def execute(self, context):
import bimtester
2020-11-01 20:08:48 +07:00
filename = os.path.join(
2020-11-01 20:08:48 +07:00
bpy.context.scene.BIMProperties.features_dir, bpy.context.scene.BIMProperties.features_file + ".feature"
)
cwd = os.getcwd()
os.chdir(bpy.context.scene.BIMProperties.features_dir)
bimtester.TestPurger().purge()
os.chdir(cwd)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-08-05 17:18:10 +10:00
class SelectIfcPatchInput(bpy.types.Operator):
bl_idname = "bim.select_ifc_patch_input"
bl_label = "Select IFC Patch Input"
2020-11-01 20:08:48 +07:00
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
2020-08-05 17:18:10 +10:00
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.ifc_patch_input = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-08-05 17:18:10 +10:00
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
2020-08-05 17:18:10 +10:00
class SelectIfcPatchOutput(bpy.types.Operator):
bl_idname = "bim.select_ifc_patch_output"
bl_label = "Select IFC Patch Output"
2020-11-01 20:08:48 +07:00
filename_ext = ".ifc"
2020-08-05 17:18:10 +10:00
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
bpy.context.scene.BIMProperties.ifc_patch_output = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-08-05 17:18:10 +10:00
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class CalculateEdgeLengths(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.calculate_edge_lengths"
bl_label = "Calculate Edge Lengths"
def execute(self, context):
result = 0
for obj in bpy.context.selected_objects:
if not obj.data or not obj.data.edges:
continue
for edge in obj.data.edges:
if edge.select:
2020-11-01 20:08:48 +07:00
result += (obj.data.vertices[edge.vertices[1]].co - obj.data.vertices[edge.vertices[0]].co).length
bpy.context.scene.BIMProperties.qto_result = str(round(result, 3))
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class CalculateFaceAreas(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.calculate_face_areas"
bl_label = "Calculate Face Areas"
def execute(self, context):
result = 0
for obj in bpy.context.selected_objects:
if not obj.data or not obj.data.polygons:
continue
for polygon in obj.data.polygons:
if polygon.select:
result += polygon.area
bpy.context.scene.BIMProperties.qto_result = str(round(result, 3))
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class CalculateObjectVolumes(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.calculate_object_volumes"
bl_label = "Calculate Object Volumes"
def execute(self, context):
qto_calculator = qto.QtoCalculator()
result = 0
for obj in bpy.context.selected_objects:
if not obj.data:
continue
result += qto_calculator.get_volume(obj)
bpy.context.scene.BIMProperties.qto_result = str(round(result, 3))
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddOpening(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_opening"
bl_label = "Add Opening"
def execute(self, context):
2020-11-01 20:08:48 +07:00
if context.active_object.children and "IfcOpeningElement/" in context.active_object.children[0].name:
opening = context.active_object.children[0]
else:
opening = context.active_object
if context.selected_objects[0] != context.active_object:
obj = context.selected_objects[0]
else:
obj = context.selected_objects[1]
2020-11-01 20:08:48 +07:00
modifier = obj.modifiers.new("IfcOpeningElement", "BOOLEAN")
modifier.operation = "DIFFERENCE"
modifier.object = opening
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SetOverrideColour(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.set_override_colour"
bl_label = "Set Override Colour"
def execute(self, context):
result = 0
for obj in bpy.context.selected_objects:
obj.color = bpy.context.scene.BIMProperties.override_colour
2020-11-01 20:08:48 +07:00
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].shading.color_type = "OBJECT"
return {"FINISHED"}
2020-08-12 21:50:57 +10:00
class AddDrawingStyle(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_drawing_style"
bl_label = "Add Drawing Style"
2020-08-12 21:50:57 +10:00
def execute(self, context):
new = bpy.context.scene.DocProperties.drawing_styles.add()
2020-11-01 20:08:48 +07:00
new.name = "New Drawing Style"
return {"FINISHED"}
2020-08-12 21:50:57 +10:00
class RemoveDrawingStyle(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_drawing_style"
bl_label = "Remove Drawing Style"
2020-08-12 21:50:57 +10:00
index: bpy.props.IntProperty()
def execute(self, context):
bpy.context.scene.DocProperties.drawing_styles.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-08-12 21:50:57 +10:00
class SaveDrawingStyle(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.save_drawing_style"
bl_label = "Save Drawing Style"
index: bpy.props.StringProperty()
def execute(self, context):
space = self.get_view_3d()
style = {
2020-11-01 20:08:48 +07:00
"bpy.data.worlds[0].color": tuple(bpy.data.worlds[0].color),
"bpy.context.scene.render.engine": bpy.context.scene.render.engine,
"bpy.context.scene.render.film_transparent": bpy.context.scene.render.film_transparent,
"bpy.context.scene.display.shading.show_object_outline": bpy.context.scene.display.shading.show_object_outline,
"bpy.context.scene.display.shading.show_cavity": bpy.context.scene.display.shading.show_cavity,
"bpy.context.scene.display.shading.cavity_type": bpy.context.scene.display.shading.cavity_type,
"bpy.context.scene.display.shading.curvature_ridge_factor": bpy.context.scene.display.shading.curvature_ridge_factor,
"bpy.context.scene.display.shading.curvature_valley_factor": bpy.context.scene.display.shading.curvature_valley_factor,
"bpy.context.scene.view_settings.view_transform": bpy.context.scene.view_settings.view_transform,
"bpy.context.scene.display.shading.light": bpy.context.scene.display.shading.light,
"bpy.context.scene.display.shading.color_type": bpy.context.scene.display.shading.color_type,
"bpy.context.scene.display.shading.single_color": tuple(bpy.context.scene.display.shading.single_color),
"bpy.context.scene.display.shading.show_shadows": bpy.context.scene.display.shading.show_shadows,
"bpy.context.scene.display.shading.shadow_intensity": bpy.context.scene.display.shading.shadow_intensity,
"bpy.context.scene.display.light_direction": tuple(bpy.context.scene.display.light_direction),
"bpy.context.scene.view_settings.use_curve_mapping": bpy.context.scene.view_settings.use_curve_mapping,
"space.overlay.show_wireframes": space.overlay.show_wireframes,
"space.overlay.wireframe_threshold": space.overlay.wireframe_threshold,
"space.overlay.show_floor": space.overlay.show_floor,
"space.overlay.show_axis_x": space.overlay.show_axis_x,
"space.overlay.show_axis_y": space.overlay.show_axis_y,
"space.overlay.show_axis_z": space.overlay.show_axis_z,
"space.overlay.show_object_origins": space.overlay.show_object_origins,
"space.overlay.show_relationship_lines": space.overlay.show_relationship_lines,
}
if self.index:
index = int(self.index)
else:
index = bpy.context.active_object.data.BIMCameraProperties.active_drawing_style_index
bpy.context.scene.DocProperties.drawing_styles[index].raster_style = json.dumps(style)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-08-12 21:50:57 +10:00
def get_view_3d(self):
for area in bpy.context.screen.areas:
2020-11-01 20:08:48 +07:00
if area.type != "VIEW_3D":
continue
for space in area.spaces:
2020-11-01 20:08:48 +07:00
if space.type != "VIEW_3D":
continue
return space
2020-08-12 21:50:57 +10:00
class ActivateDrawingStyle(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.activate_drawing_style"
bl_label = "Activate Drawing Style"
2020-08-12 21:50:57 +10:00
def execute(self, context):
2020-11-01 20:08:48 +07:00
if context.scene.camera.data.BIMCameraProperties.active_drawing_style_index < len(
bpy.context.scene.DocProperties.drawing_styles
):
self.drawing_style = bpy.context.scene.DocProperties.drawing_styles[
context.scene.camera.data.BIMCameraProperties.active_drawing_style_index
]
self.set_raster_style()
self.set_query()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def set_raster_style(self):
space = self.get_view_3d()
style = json.loads(self.drawing_style.raster_style)
2020-11-01 20:08:48 +07:00
bpy.data.worlds[0].color = style["bpy.data.worlds[0].color"]
bpy.context.scene.render.engine = style["bpy.context.scene.render.engine"]
bpy.context.scene.render.film_transparent = style["bpy.context.scene.render.film_transparent"]
bpy.context.scene.display.shading.show_object_outline = style[
"bpy.context.scene.display.shading.show_object_outline"
]
bpy.context.scene.display.shading.show_cavity = style["bpy.context.scene.display.shading.show_cavity"]
bpy.context.scene.display.shading.cavity_type = style["bpy.context.scene.display.shading.cavity_type"]
bpy.context.scene.display.shading.curvature_ridge_factor = style[
"bpy.context.scene.display.shading.curvature_ridge_factor"
]
bpy.context.scene.display.shading.curvature_valley_factor = style[
"bpy.context.scene.display.shading.curvature_valley_factor"
]
bpy.context.scene.view_settings.view_transform = style["bpy.context.scene.view_settings.view_transform"]
bpy.context.scene.display.shading.light = style["bpy.context.scene.display.shading.light"]
bpy.context.scene.display.shading.color_type = style["bpy.context.scene.display.shading.color_type"]
bpy.context.scene.display.shading.single_color = style["bpy.context.scene.display.shading.single_color"]
bpy.context.scene.display.shading.show_shadows = style["bpy.context.scene.display.shading.show_shadows"]
bpy.context.scene.display.shading.shadow_intensity = style["bpy.context.scene.display.shading.shadow_intensity"]
bpy.context.scene.display.light_direction = style["bpy.context.scene.display.light_direction"]
bpy.context.scene.view_settings.use_curve_mapping = style["bpy.context.scene.view_settings.use_curve_mapping"]
space.overlay.show_wireframes = style["space.overlay.show_wireframes"]
space.overlay.wireframe_threshold = style["space.overlay.wireframe_threshold"]
space.overlay.show_floor = style["space.overlay.show_floor"]
space.overlay.show_axis_x = style["space.overlay.show_axis_x"]
space.overlay.show_axis_y = style["space.overlay.show_axis_y"]
space.overlay.show_axis_z = style["space.overlay.show_axis_z"]
space.overlay.show_object_origins = style["space.overlay.show_object_origins"]
space.overlay.show_relationship_lines = style["space.overlay.show_relationship_lines"]
space.shading.type = "RENDERED"
def set_query(self):
self.selector = ifcopenshell.util.selector.Selector()
self.include_global_ids = []
self.exclude_global_ids = []
for ifc_file in bpy.context.scene.DocProperties.ifc_files:
2020-09-05 14:15:37 +10:00
try:
ifc = ifcopenshell.open(ifc_file.name)
except:
continue
if self.drawing_style.include_query:
results = self.selector.parse(ifc, self.drawing_style.include_query)
self.include_global_ids.extend([e.GlobalId for e in results])
if self.drawing_style.exclude_query:
results = self.selector.parse(ifc, self.drawing_style.exclude_query)
self.exclude_global_ids.extend([e.GlobalId for e in results])
if self.drawing_style.include_query:
2020-11-01 20:08:48 +07:00
self.parse_filter_query("INCLUDE")
else:
for obj in bpy.context.scene.objects:
obj.hide_viewport = False
if self.drawing_style.exclude_query:
2020-11-01 20:08:48 +07:00
self.parse_filter_query("EXCLUDE")
def parse_filter_query(self, mode):
2020-11-01 20:08:48 +07:00
if mode == "INCLUDE":
objects = bpy.context.scene.objects
2020-11-01 20:08:48 +07:00
elif mode == "EXCLUDE":
objects = bpy.context.visible_objects
for obj in objects:
2020-11-01 20:08:48 +07:00
if mode == "INCLUDE":
obj.hide_viewport = False # Note: this breaks alt-H
global_id = obj.BIMObjectProperties.attributes.get("GlobalId")
if not global_id:
continue
global_id = global_id.string_value
2020-11-01 20:08:48 +07:00
if mode == "INCLUDE":
if global_id not in self.include_global_ids:
2020-11-01 20:08:48 +07:00
obj.hide_viewport = True # Note: this breaks alt-H
elif mode == "EXCLUDE":
if global_id in self.exclude_global_ids:
2020-11-01 20:08:48 +07:00
obj.hide_viewport = True # Note: this breaks alt-H
def get_view_3d(self):
for area in bpy.context.screen.areas:
2020-11-01 20:08:48 +07:00
if area.type != "VIEW_3D":
continue
for space in area.spaces:
2020-11-01 20:08:48 +07:00
if space.type != "VIEW_3D":
continue
return space
class AddDrawing(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_drawing"
bl_label = "Add Drawing"
def execute(self, context):
new = bpy.context.scene.DocProperties.drawings.add()
2020-11-01 20:08:48 +07:00
new.name = "DRAWING {}".format(len(bpy.context.scene.DocProperties.drawings))
if not bpy.data.collections.get("Views"):
bpy.context.scene.collection.children.link(bpy.data.collections.new("Views"))
views_collection = bpy.data.collections.get("Views")
view_collection = bpy.data.collections.new("IfcGroup/" + new.name)
views_collection.children.link(view_collection)
2020-11-01 20:08:48 +07:00
camera = bpy.data.objects.new("IfcGroup/" + new.name, bpy.data.cameras.new("IfcGroup/" + new.name))
camera.location = (0, 0, 1.7) # The view shall be 1.7m above the origin
camera.data.type = "ORTHO"
camera.data.ortho_scale = 50 # The default of 6m is too small
if bpy.context.scene.unit_settings.system == "IMPERIAL":
camera.data.BIMCameraProperties.diagram_scale = '1/8"=1\'-0"|1/96'
else:
2020-11-01 20:08:48 +07:00
camera.data.BIMCameraProperties.diagram_scale = "1:100|1/100"
bpy.context.scene.camera = camera
view_collection.objects.link(camera)
2020-11-01 20:08:48 +07:00
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].region_3d.view_perspective = "CAMERA"
new.camera = camera
bpy.ops.bim.activate_drawing_style()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveDrawing(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_drawing"
bl_label = "Remove Drawing"
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.DocProperties
camera = props.drawings[self.index].camera
collection = camera.users_collection[0]
for obj in collection.objects:
bpy.data.objects.remove(obj)
bpy.data.collections.remove(collection, do_unlink=True)
props.drawings.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class EditVectorStyle(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.edit_vector_style"
bl_label = "Edit Vector Style"
def execute(self, context):
camera = context.scene.camera
2020-11-01 20:08:48 +07:00
vector_style = context.scene.DocProperties.drawing_styles[
camera.data.BIMCameraProperties.active_drawing_style_index
].vector_style
bpy.data.texts.load(os.path.join(context.scene.BIMProperties.data_dir, "styles", vector_style + ".css"))
return {"FINISHED"}
class RemoveSheet(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_sheet"
bl_label = "Remove Sheet"
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.DocProperties
props.sheets.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddSchedule(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_schedule"
bl_label = "Add Schedule"
def execute(self, context):
new = bpy.context.scene.DocProperties.schedules.add()
2020-11-01 20:08:48 +07:00
new.name = "SCHEDULE {}".format(len(bpy.context.scene.DocProperties.schedules))
return {"FINISHED"}
class RemoveSchedule(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_schedule"
bl_label = "Remove Schedule"
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.DocProperties
props.schedules.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class SelectScheduleFile(bpy.types.Operator):
bl_idname = "bim.select_schedule_file"
bl_label = "Select Documentation IFC File"
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
2020-11-01 20:08:48 +07:00
filter_glob: bpy.props.StringProperty(default="*.ods", options={"HIDDEN"})
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.DocProperties
props.schedules[props.active_schedule_index].file = self.filepath
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
2020-11-01 20:08:48 +07:00
return {"RUNNING_MODAL"}
class BuildSchedule(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.build_schedule"
bl_label = "Build Schedule"
def execute(self, context):
props = bpy.context.scene.DocProperties
schedule = props.schedules[props.active_schedule_index]
schedule_creator = scheduler.Scheduler()
2020-11-01 20:08:48 +07:00
outfile = os.path.join(bpy.context.scene.BIMProperties.data_dir, "schedules", schedule.name + ".svg")
schedule_creator.schedule(schedule.file, outfile)
2020-11-01 20:08:48 +07:00
open_with_user_command(bpy.context.preferences.addons["blenderbim"].preferences.svg_command, outfile)
return {"FINISHED"}
2020-08-18 10:04:19 +10:00
class AddScheduleToSheet(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_schedule_to_sheet"
bl_label = "Add Schedule To Sheet"
2020-08-18 10:04:19 +10:00
def execute(self, context):
props = bpy.context.scene.DocProperties
sheet_builder = sheeter.SheetBuilder()
sheet_builder.data_dir = bpy.context.scene.BIMProperties.data_dir
sheet_builder.add_schedule(
2020-11-01 20:08:48 +07:00
props.schedules[props.active_schedule_index].name, props.sheets[props.active_sheet_index].name
)
return {"FINISHED"}
class SetViewportShadowFromSun(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.set_viewport_shadow_from_sun"
bl_label = "Set Viewport Shadow from Sun"
def execute(self, context):
# The vector used for the light direction is a bit funny
2020-11-01 20:08:48 +07:00
mat = Matrix(((-1.0, 0.0, 0.0, 0.0), (0.0, 0, 1.0, 0.0), (-0.0, -1.0, 0, 0.0), (0.0, 0.0, 0.0, 1.0)))
context.scene.display.light_direction = mat.inverted() @ (
context.active_object.matrix_world.to_quaternion() @ Vector((0, 0, -1))
)
return {"FINISHED"}
class SetNorthOffset(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.set_north_offset"
bl_label = "Set North Offset"
def execute(self, context):
2020-11-01 20:08:48 +07:00
context.scene.sun_pos_properties.north_offset = radians(
ifcopenshell.util.geolocation.xy2angle(
float(bpy.context.scene.MapConversion.x_axis_ordinate),
float(bpy.context.scene.MapConversion.x_axis_abscissa),
)
)
return {"FINISHED"}
class GetNorthOffset(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.get_north_offset"
bl_label = "Get North Offset"
def execute(self, context):
x_angle = -context.scene.sun_pos_properties.north_offset
bpy.context.scene.MapConversion.x_axis_abscissa = str(cos(x_angle))
bpy.context.scene.MapConversion.x_axis_ordinate = str(sin(x_angle))
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class AddDrawingStyleAttribute(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.add_drawing_style_attribute"
bl_label = "Add Drawing Style Attribute"
def execute(self, context):
props = bpy.context.scene.camera.data.BIMCameraProperties
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.add()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RemoveDrawingStyleAttribute(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.remove_drawing_style_attribute"
bl_label = "Remove Drawing Style Attribute"
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.camera.data.BIMCameraProperties
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.remove(self.index)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class CreateShapeFromStepId(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.create_shape_from_step_id"
bl_label = "Create Shape From STEP ID"
def execute(self, context):
2020-11-01 20:08:48 +07:00
logger = logging.getLogger("ImportIFC")
self.ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, ifc.IfcStore.path, logger)
self.file = ifc.IfcStore.get_file()
element = self.file.by_id(int(bpy.context.scene.BIMDebugProperties.step_id))
settings = ifcopenshell.geom.settings()
2020-11-01 20:08:48 +07:00
# settings.set(settings.INCLUDE_CURVES, True)
shape = ifcopenshell.geom.create_shape(settings, element)
ifc_importer = import_ifc.IfcImporter(self.ifc_import_settings)
ifc_importer.file = self.file
mesh = ifc_importer.create_mesh(element, shape)
2020-11-01 20:08:48 +07:00
obj = bpy.data.objects.new("Debug", mesh)
bpy.context.scene.collection.objects.link(obj)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
2020-09-07 11:28:35 +10:00
class SelectHighPolygonMeshes(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.select_high_polygon_meshes"
bl_label = "Select High Polygon Meshes"
2020-09-07 11:28:35 +10:00
def execute(self, context):
results = {}
for obj in bpy.data.objects:
2020-11-01 20:08:48 +07:00
if not isinstance(obj.data, bpy.types.Mesh) or len(obj.data.polygons) < int(
bpy.context.scene.BIMDebugProperties.number_of_polygons
):
2020-09-07 11:28:35 +10:00
continue
try:
obj.select_set(True)
except:
# If it is not in the view layer
pass
relating_type = obj.BIMObjectProperties.relating_type
if relating_type:
relating_type.select_set(True)
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class RefreshDrawingList(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.refresh_drawing_list"
bl_label = "Refresh Drawing List"
def execute(self, context):
while len(bpy.context.scene.DocProperties.drawings) > 0:
bpy.context.scene.DocProperties.drawings.remove(0)
2020-09-08 17:17:45 +10:00
for obj in bpy.context.scene.objects:
if not isinstance(obj.data, bpy.types.Camera):
continue
2020-11-01 20:08:48 +07:00
if "IfcGroup/" in obj.name and obj.users_collection[0].name == obj.name:
new = bpy.context.scene.DocProperties.drawings.add()
2020-11-01 20:08:48 +07:00
new.name = obj.name.split("/")[1]
new.camera = obj
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class GetRepresentationIfcParameters(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.get_representation_ifc_parameters"
bl_label = "Get Representation IFC Parameters"
def execute(self, context):
props = bpy.context.active_object.data.BIMMeshProperties
dummy = ifcopenshell.file.from_string(props.ifc_definition)
for element in dummy:
2020-11-01 20:08:48 +07:00
if not element.is_a("IfcRepresentationItem"):
continue
for i in range(0, len(element)):
2020-11-01 20:08:48 +07:00
if element.attribute_type(i) == "DOUBLE":
new = props.ifc_parameters.add()
2020-11-01 20:08:48 +07:00
new.name = "{}/{}".format(element.is_a(), element.attribute_name(i))
new.step_id = element.id()
new.type = element.attribute_type(i)
new.index = i
if element[i]:
new.value = element[i]
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
class UpdateIfcRepresentation(bpy.types.Operator):
2020-11-01 20:08:48 +07:00
bl_idname = "bim.update_ifc_representation"
bl_label = "Update IFC Representation"
index: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.active_object.data.BIMMeshProperties
parameter = props.ifc_parameters[self.index]
dummy = ifcopenshell.file.from_string(props.ifc_definition)
element = dummy.by_id(parameter.step_id)[parameter.index] = parameter.value
props.ifc_definition = dummy.to_string()
self.recreate_ifc_representation()
2020-11-01 20:08:48 +07:00
return {"FINISHED"}
def recreate_ifc_representation(self):
props = bpy.context.active_object.data.BIMMeshProperties
dummy = ifcopenshell.file.from_string(props.ifc_definition)
2020-11-01 20:08:48 +07:00
logger = logging.getLogger("ImportIFC")
self.ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, ifc.IfcStore.path, logger)
element = dummy.by_id(props.ifc_definition_id)
settings = ifcopenshell.geom.settings()
shape = ifcopenshell.geom.create_shape(settings, element)
ifc_importer = import_ifc.IfcImporter(self.ifc_import_settings)
ifc_importer.file = dummy
mesh = ifc_importer.create_mesh(element, shape)
bpy.context.active_object.data.user_remap(mesh)