mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-28 15:53:00 +00:00
919 lines
36 KiB
Python
919 lines
36 KiB
Python
import os
|
|
import bpy
|
|
import bcf
|
|
import bcf.bcfxml
|
|
import bcf.v2.data
|
|
from . import bcfstore
|
|
from blenderbim.bim.ifc import IfcStore
|
|
from math import radians, degrees, atan, tan, cos, sin
|
|
from mathutils import Vector, Matrix, Euler, geometry
|
|
|
|
class NewBcfProject(bpy.types.Operator):
|
|
bl_idname = "bim.new_bcf_project"
|
|
bl_label = "New BCF Project"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bpy.context.scene.BCFProperties.is_loaded = False
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
bcfxml.new_project()
|
|
bpy.ops.bim.load_bcf_project()
|
|
bpy.context.scene.BCFProperties.is_loaded = True
|
|
return {"FINISHED"}
|
|
|
|
|
|
class LoadBcfProject(bpy.types.Operator):
|
|
bl_idname = "bim.load_bcf_project"
|
|
bl_label = "Load BCF Project"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
filter_glob: bpy.props.StringProperty(default="*.bcf;*.bcfzip", options={"HIDDEN"})
|
|
|
|
def execute(self, context):
|
|
bpy.context.scene.BCFProperties.is_loaded = False
|
|
if self.filepath:
|
|
bcfstore.BcfStore.bcfxml = bcf.bcfxml.load(self.filepath)
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
bcfxml.get_project()
|
|
bpy.context.scene.BCFProperties.name = bcfxml.project.name
|
|
bpy.ops.bim.load_bcf_topics()
|
|
bpy.context.scene.BCFProperties.is_loaded = True
|
|
return {"FINISHED"}
|
|
|
|
def invoke(self, context, event):
|
|
context.window_manager.fileselect_add(self)
|
|
return {"RUNNING_MODAL"}
|
|
|
|
|
|
class LoadBcfTopics(bpy.types.Operator):
|
|
bl_idname = "bim.load_bcf_topics"
|
|
bl_label = "Load BCF Topics"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
bcfxml.get_topics()
|
|
while len(bpy.context.scene.BCFProperties.topics) > 0:
|
|
bpy.context.scene.BCFProperties.topics.remove(0)
|
|
index = 0
|
|
for topic_guid in bcfxml.topics.keys():
|
|
new = bpy.context.scene.BCFProperties.topics.add()
|
|
bpy.ops.bim.load_bcf_topic(topic_guid = topic_guid, topic_index = index)
|
|
index += 1
|
|
return {"FINISHED"}
|
|
|
|
|
|
class LoadBcfTopic(bpy.types.Operator):
|
|
bl_idname = "bim.load_bcf_topic"
|
|
bl_label = "Load BCF Topics"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
topic_guid: bpy.props.StringProperty()
|
|
topic_index: bpy.props.IntProperty()
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
topic = bcfxml.get_topic(self.topic_guid)
|
|
new = bpy.context.scene.BCFProperties.topics[self.topic_index]
|
|
data_map = {
|
|
"name": topic.guid,
|
|
"title": topic.title,
|
|
"type": topic.topic_type,
|
|
"status": topic.topic_status,
|
|
"priority": topic.priority,
|
|
"stage": topic.stage,
|
|
"creation_date": topic.creation_date,
|
|
"creation_author": topic.creation_author,
|
|
"modified_date": topic.modified_date,
|
|
"modified_author": topic.modified_author,
|
|
"assigned_to": topic.assigned_to,
|
|
"due_date": topic.due_date,
|
|
"description": topic.description
|
|
}
|
|
for key, value in data_map.items():
|
|
if value is not None:
|
|
setattr(new, key, str(value))
|
|
while len(new.reference_links) > 0:
|
|
new.reference_links.remove(0)
|
|
for reference_link in topic.reference_links:
|
|
new2 = new.reference_links.add()
|
|
new2.name = reference_link
|
|
while len(new.labels) > 0:
|
|
new.labels.remove(0)
|
|
for label in topic.labels:
|
|
new2 = new.labels.add()
|
|
new2.name = label
|
|
if topic.bim_snippet:
|
|
data_map = {
|
|
"type": topic.bim_snippet.snippet_type,
|
|
"is_external": topic.bim_snippet.is_external,
|
|
"reference": topic.bim_snippet.reference,
|
|
"schema": topic.bim_snippet.reference_schema
|
|
}
|
|
for key, value in data_map.items():
|
|
if value is not None:
|
|
setattr(new.bim_snippet, key, value)
|
|
while len(new.document_references) > 0:
|
|
new.document_references.remove(0)
|
|
for doc in topic.document_references:
|
|
new2 = new.document_references.add()
|
|
data_map = {
|
|
"reference": doc.referenced_document,
|
|
"description": doc.description,
|
|
"guid": doc.guid,
|
|
"is_external": doc.is_external
|
|
}
|
|
for key, value in data_map.items():
|
|
if value is not None:
|
|
setattr(new2, key, value)
|
|
while len(new.related_topics) > 0:
|
|
new.related_topics.remove(0)
|
|
for related_topic in topic.related_topics:
|
|
new2 = new.related_topics.add()
|
|
new2.name = related_topic.guid
|
|
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class LoadBcfComments(bpy.types.Operator):
|
|
bl_idname = "bim.load_bcf_comments"
|
|
bl_label = "Load BCF Comments"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
topic_guid: bpy.props.StringProperty()
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
bcfxml.get_comments(self.topic_guid)
|
|
blender_topic = bpy.context.scene.BCFProperties.topics.get(self.topic_guid)
|
|
while len(blender_topic.comments) > 0:
|
|
blender_topic.comments.remove(0)
|
|
for comment in bcfxml.topics[self.topic_guid].comments.values():
|
|
new = blender_topic.comments.add()
|
|
data_map = {
|
|
"name": comment.guid,
|
|
"comment": comment.comment,
|
|
"viewpoint": comment.viewpoint.guid if comment.viewpoint else None,
|
|
"date": comment.date,
|
|
"author": comment.author,
|
|
"modified_date": comment.modified_date,
|
|
"modified_author": comment.modified_author,
|
|
}
|
|
for key, value in data_map.items():
|
|
if value is not None:
|
|
setattr(new, key, str(value))
|
|
return {"FINISHED"}
|
|
|
|
|
|
class EditBcfProjectName(bpy.types.Operator):
|
|
bl_idname = "bim.edit_bcf_project_name"
|
|
bl_label = "Edit BCF Project Name"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
bcfxml.project.name = bpy.context.scene.BCFProperties.name
|
|
bcfxml.edit_project()
|
|
return {"FINISHED"}
|
|
|
|
|
|
class EditBcfAuthor(bpy.types.Operator):
|
|
bl_idname = "bim.edit_bcf_author"
|
|
bl_label = "Edit BCF Author"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
bcfxml.author = bpy.context.scene.BCFProperties.author
|
|
return {"FINISHED"}
|
|
|
|
|
|
class EditBcfTopicName(bpy.types.Operator):
|
|
bl_idname = "bim.edit_bcf_topic_name"
|
|
bl_label = "Edit BCF Topic Name"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
topic.title = blender_topic.title
|
|
bcfxml.edit_topic(topic)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class EditBcfTopic(bpy.types.Operator):
|
|
bl_idname = "bim.edit_bcf_topic"
|
|
bl_label = "Edit BCF Topic"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
topic.title = blender_topic.title or None
|
|
topic.priority = blender_topic.priority or None
|
|
topic.due_date = blender_topic.due_date or None
|
|
topic.assigned_to = blender_topic.assigned_to or None
|
|
topic.stage = blender_topic.stage or None
|
|
topic.description = blender_topic.description or None
|
|
topic.topic_status = blender_topic.status or None
|
|
topic.topic_type = blender_topic.type or None
|
|
|
|
bcfxml.edit_topic(topic)
|
|
props.active_topic_index = props.active_topic_index # Refreshes the BCF Topic
|
|
return {"FINISHED"}
|
|
|
|
|
|
class SaveBcfProject(bpy.types.Operator):
|
|
bl_idname = "bim.save_bcf_project"
|
|
bl_label = "Save BCF Project"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
bcfxml.save_project(self.filepath)
|
|
return {"FINISHED"}
|
|
|
|
def invoke(self, context, event):
|
|
context.window_manager.fileselect_add(self)
|
|
return {"RUNNING_MODAL"}
|
|
|
|
|
|
class AddBcfTopic(bpy.types.Operator):
|
|
bl_idname = "bim.add_bcf_topic"
|
|
bl_label = "Add BCF Topic"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
bcfxml.add_topic()
|
|
new = bpy.context.scene.BCFProperties.topics.add()
|
|
new.name = "New Topic"
|
|
bpy.ops.bim.load_bcf_topics()
|
|
return {"FINISHED"}
|
|
|
|
|
|
class AddBcfBimSnippet(bpy.types.Operator):
|
|
bl_idname = "bim.add_bcf_bim_snippet"
|
|
bl_label = "Add BCF BIM Snippet"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
bim_snippet = bcf.v2.data.BimSnippet()
|
|
bim_snippet.reference = blender_topic.bim_snippet_reference
|
|
bim_snippet.reference_schema = blender_topic.bim_snippet_schema
|
|
bim_snippet.snippet_type = blender_topic.bim_snippet_type
|
|
bcfxml.add_bim_snippet(topic, bim_snippet)
|
|
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class AddBcfRelatedTopic(bpy.types.Operator):
|
|
bl_idname = "bim.add_bcf_related_topic"
|
|
bl_label = "Add BCF Related Topic"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
related_topic = None
|
|
for topic in bcfxml.topics.values():
|
|
if topic.title == blender_topic.related_topic:
|
|
related_topic = bcf.v2.data.RelatedTopic()
|
|
related_topic.guid = topic.guid
|
|
break
|
|
if not related_topic:
|
|
return {"FINISHED"}
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
topic.related_topics.append(related_topic)
|
|
bcfxml.edit_topic(topic)
|
|
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class AddBcfHeaderFile(bpy.types.Operator):
|
|
bl_idname = "bim.add_bcf_header_file"
|
|
bl_label = "Add BCF Header File"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
header_file = bcf.v2.data.HeaderFile()
|
|
header_file.reference = blender_topic.file_reference
|
|
if not os.path.exists(header_file.reference):
|
|
header_file.filename = header_file.reference
|
|
if len(blender_topic.file_ifc_project) == 22:
|
|
header_file.ifc_project = blender_topic.file_ifc_project
|
|
if len(blender_topic.file_ifc_spatial_structure_element) == 22:
|
|
header_file.ifc_spatial_structure_element = blender_topic.file_ifc_spatial_structure_element
|
|
bcfxml.add_file(topic, header_file)
|
|
props.active_topic_index = props.active_topic_index # refreshes the BCF Topic
|
|
return {"FINISHED"}
|
|
|
|
|
|
class ViewBcfTopic(bpy.types.Operator):
|
|
bl_idname = "bim.view_bcf_topic"
|
|
bl_label = "Get BCF Topic"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
topic_guid: bpy.props.StringProperty()
|
|
|
|
def execute(self, context):
|
|
for index, topic in enumerate(bpy.context.scene.BCFProperties.topics):
|
|
if topic.guid.lower() == self.topic_guid.lower():
|
|
bpy.context.scene.BCFProperties.active_topic_index = index
|
|
return {"FINISHED"}
|
|
|
|
|
|
class AddBcfViewpoint(bpy.types.Operator):
|
|
bl_idname = "bim.add_bcf_viewpoint"
|
|
bl_label = "Add BCF Viewpoint"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
if not bpy.context.scene.camera:
|
|
return {"FINISHED"}
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
viewpoint = bcf.v2.data.Viewpoint()
|
|
|
|
if bpy.context.scene.camera.data.type == "ORTHO":
|
|
camera = bcf.v2.data.OrthogonalCamera()
|
|
camera.view_to_world_scale = bpy.context.scene.camera.data.ortho_scale
|
|
viewpoint.orthogonal_camera = camera
|
|
elif bpy.context.scene.camera.data.type == "PERSP":
|
|
camera = bcf.v2.data.PerspectiveCamera()
|
|
camera.field_of_view = degrees(bpy.context.scene.camera.data.angle)
|
|
viewpoint.perspective_camera = camera
|
|
camera.camera_view_point.x = bpy.context.scene.camera.location.x
|
|
camera.camera_view_point.y = bpy.context.scene.camera.location.y
|
|
camera.camera_view_point.z = bpy.context.scene.camera.location.z
|
|
direction = bpy.context.scene.camera.matrix_world.to_quaternion() @ Vector((0.0, 0.0, -1.0))
|
|
camera.camera_direction.x = direction.x
|
|
camera.camera_direction.y = direction.y
|
|
camera.camera_direction.z = direction.z
|
|
up = bpy.context.scene.camera.matrix_world.to_quaternion() @ Vector((0.0, 1.0, 0.0))
|
|
camera.camera_up_vector.x = up.x
|
|
camera.camera_up_vector.y = up.y
|
|
camera.camera_up_vector.z = up.z
|
|
|
|
old_file_format = bpy.context.scene.render.image_settings.file_format
|
|
bpy.context.scene.render.image_settings.file_format = "PNG"
|
|
old_filepath = bpy.context.scene.render.filepath
|
|
bpy.context.scene.render.filepath = os.path.join(bpy.context.scene.BIMProperties.data_dir, "snapshot.png")
|
|
bpy.ops.render.opengl(write_still=True)
|
|
viewpoint.snapshot = bpy.context.scene.render.filepath
|
|
|
|
bcfxml.add_viewpoint(topic, viewpoint)
|
|
bpy.context.scene.render.filepath = old_filepath
|
|
bpy.context.scene.render.image_settings.file_format = old_file_format
|
|
props.active_topic_index = props.active_topic_index # refreshes the BCF Topic
|
|
return {"FINISHED"}
|
|
|
|
|
|
class RemoveBcfViewpoint(bpy.types.Operator):
|
|
bl_idname = "bim.remove_bcf_viewpoint"
|
|
bl_label = "Remove BCF Viewpoint"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
viewpoint_guid = blender_topic.viewpoints
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
bcfxml.delete_viewpoint(viewpoint_guid, topic)
|
|
props.active_topic_index = props.active_topic_index # Refreshes the BCF Topic
|
|
return {"FINISHED"}
|
|
|
|
|
|
class RemoveBcfFile(bpy.types.Operator):
|
|
bl_idname = "bim.remove_bcf_file"
|
|
bl_label = "Remove BCF File"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
index: bpy.props.IntProperty()
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
bcfxml.delete_file(topic, self.index)
|
|
props.active_topic_index = props.active_topic_index # Refreshes the BCF Topic
|
|
return {"FINISHED"}
|
|
|
|
|
|
class AddBcfReferenceLink(bpy.types.Operator):
|
|
bl_idname = "bim.add_bcf_reference_link"
|
|
bl_label = "Add BCF Reference Link"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
if not blender_topic.reference_link:
|
|
return {"FINISHED"}
|
|
topic.reference_links.append(blender_topic.reference_link)
|
|
bcfxml.edit_topic(topic)
|
|
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
|
|
blender_topic.reference_link = ""
|
|
return {"FINISHED"}
|
|
|
|
|
|
class AddBcfDocumentReference(bpy.types.Operator):
|
|
bl_idname = "bim.add_bcf_document_reference"
|
|
bl_label = "Add BCF Document Reference"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
if not blender_topic.document_reference:
|
|
return {"FINISHED"}
|
|
document_reference = bcf.v2.data.DocumentReference()
|
|
document_reference.referenced_document = blender_topic.document_reference
|
|
document_reference.description = blender_topic.document_reference_description or None
|
|
bcfxml.add_document_reference(topic, document_reference)
|
|
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
|
|
blender_topic.document_reference = ""
|
|
blender_topic.document_reference_description = ""
|
|
return {"FINISHED"}
|
|
|
|
|
|
class AddBcfLabel(bpy.types.Operator):
|
|
bl_idname = "bim.add_bcf_label"
|
|
bl_label = "Add BCF Label"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
if not blender_topic.label:
|
|
return {"FINISHED"}
|
|
new = blender_topic.labels.add()
|
|
new.name = blender_topic.label
|
|
topic.labels.append(blender_topic.label)
|
|
bcfxml.edit_topic(topic)
|
|
blender_topic.label = ""
|
|
return {"FINISHED"}
|
|
|
|
|
|
class EditBcfReferenceLinks(bpy.types.Operator):
|
|
bl_idname = "bim.edit_bcf_reference_links"
|
|
bl_label = "Edit BCF Reference Link"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
for index, reference_link in enumerate(topic.reference_links):
|
|
if reference_link == blender_topic.reference_links[index].name:
|
|
continue
|
|
topic.reference_links[index] = blender_topic.reference_links[index].name
|
|
bcfxml.edit_topic(topic)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class EditBcfLabels(bpy.types.Operator):
|
|
bl_idname = "bim.edit_bcf_labels"
|
|
bl_label = "Edit BCF Labels"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
for index, label in enumerate(blender_topic.labels):
|
|
if label.name == topic.labels[index]:
|
|
continue
|
|
topic.labels[index] = blender_topic.labels[index].name
|
|
bcfxml.edit_topic(topic)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class RemoveBcfReferenceLink(bpy.types.Operator):
|
|
bl_idname = "bim.remove_bcf_reference_link"
|
|
bl_label = "Remove BCF Reference Link"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
index: bpy.props.IntProperty()
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
del topic.reference_links[self.index]
|
|
bcfxml.edit_topic(topic)
|
|
blender_topic.reference_links.remove(self.index)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class RemoveBcfLabel(bpy.types.Operator):
|
|
bl_idname = "bim.remove_bcf_label"
|
|
bl_label = "Remove BCF Label"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
index: bpy.props.IntProperty()
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
del topic.labels[self.index]
|
|
bcfxml.edit_topic(topic)
|
|
blender_topic.labels.remove(self.index)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class RemoveBcfBimSnippet(bpy.types.Operator):
|
|
bl_idname = "bim.remove_bcf_bim_snippet"
|
|
bl_label = "Remove BCF BIM Snippet"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
bcfxml.delete_bim_snippet(topic)
|
|
blender_topic.bim_snippet.schema = ""
|
|
blender_topic.bim_snippet.reference = ""
|
|
blender_topic.bim_snippet.type = ""
|
|
return {"FINISHED"}
|
|
|
|
|
|
class RemoveBcfDocumentReference(bpy.types.Operator):
|
|
bl_idname = "bim.remove_bcf_document_reference"
|
|
bl_label = "Remove BCF Document Reference"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
index: bpy.props.IntProperty()
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
bcfxml.delete_document_reference(topic, self.index)
|
|
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class RemoveBcfRelatedTopic(bpy.types.Operator):
|
|
bl_idname = "bim.remove_bcf_related_topic"
|
|
bl_label = "Remove BCF Related Topic"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
index: bpy.props.IntProperty()
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
del topic.related_topics[self.index]
|
|
bcfxml.edit_topic(topic)
|
|
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class RemoveBcfComment(bpy.types.Operator):
|
|
bl_idname = "bim.remove_bcf_comment"
|
|
bl_label = "Remove BCF Comment"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
comment_guid: bpy.props.StringProperty()
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
bcfxml.delete_comment(self.comment_guid, topic)
|
|
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class EditBcfComment(bpy.types.Operator):
|
|
bl_idname = "bim.edit_bcf_comment"
|
|
bl_label = "Edit BCF Comment"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
comment_guid: bpy.props.StringProperty()
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
blender_comment = blender_topic.comments.get(self.comment_guid)
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
comment = topic.comments[self.comment_guid]
|
|
comment.comment = blender_comment.comment
|
|
bcfxml.edit_comment(comment, topic)
|
|
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class AddBcfComment(bpy.types.Operator):
|
|
bl_idname = "bim.add_bcf_comment"
|
|
bl_label = "Add BCF Comment"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
comment_guid: bpy.props.StringProperty()
|
|
|
|
def execute(self, context):
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
if not blender_topic.comment:
|
|
return {"FINISHED"}
|
|
comment = bcf.v2.data.Comment()
|
|
comment.comment = blender_topic.comment
|
|
if blender_topic.has_related_viewpoint and blender_topic.viewpoints:
|
|
comment.viewpoint = bcf.v2.data.Viewpoint()
|
|
comment.viewpoint.guid = blender_topic.viewpoints
|
|
bcfxml.add_comment(topic, comment)
|
|
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class ActivateBcfViewpoint(bpy.types.Operator):
|
|
bl_idname = "bim.activate_bcf_viewpoint"
|
|
bl_label = "Activate BCF Viewpoint"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
|
|
def execute(self, context):
|
|
self.file = IfcStore.get_file()
|
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
|
props = bpy.context.scene.BCFProperties
|
|
blender_topic = props.topics[props.active_topic_index]
|
|
topic = bcfxml.topics[blender_topic.name]
|
|
if not topic.viewpoints:
|
|
return {"FINISHED"}
|
|
|
|
viewpoint_guid = blender_topic.viewpoints
|
|
viewpoint = topic.viewpoints[viewpoint_guid]
|
|
obj = bpy.data.objects.get("Viewpoint")
|
|
if not obj:
|
|
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.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()
|
|
background.image = bpy.data.images.load(
|
|
os.path.join(bcfxml.filepath, topic.guid, viewpoint.snapshot)
|
|
)
|
|
src_width = background.image.size[0]
|
|
src_height = background.image.size[1]
|
|
src_aspect = src_width / src_height
|
|
|
|
if src_aspect > cam_aspect:
|
|
background.frame_method = "FIT"
|
|
else:
|
|
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.orthogonal_camera:
|
|
camera = viewpoint.orthogonal_camera
|
|
obj.data.type = "ORTHO"
|
|
obj.data.ortho_scale = viewpoint.orthogonal_camera.view_to_world_scale
|
|
elif viewpoint.perspective_camera:
|
|
camera = viewpoint.perspective_camera
|
|
obj.data.type = "PERSP"
|
|
if cam_aspect >= 1:
|
|
obj.data.angle = radians(camera.field_of_view)
|
|
else:
|
|
# https://blender.stackexchange.com/questions/23431/how-to-set-camera-horizontal-and-vertical-fov
|
|
obj.data.angle = 2 * atan((0.5 * cam_height) / (0.5 * cam_width / tan(radians(camera.field_of_view) / 2)))
|
|
|
|
self.set_viewpoint_components(viewpoint)
|
|
|
|
gp = bpy.data.grease_pencils.get("BCF")
|
|
if gp:
|
|
bpy.data.grease_pencils.remove(gp)
|
|
if viewpoint.lines:
|
|
self.draw_lines(viewpoint)
|
|
|
|
self.delete_clipping_planes()
|
|
if viewpoint.clipping_planes:
|
|
self.create_clipping_planes(viewpoint)
|
|
|
|
self.delete_bitmaps()
|
|
if viewpoint.bitmaps:
|
|
self.create_bitmaps(bcfxml, viewpoint, topic)
|
|
|
|
z_axis = Vector((-camera.camera_direction.x, -camera.camera_direction.y, -camera.camera_direction.z)).normalized()
|
|
y_axis = Vector((camera.camera_up_vector.x, camera.camera_up_vector.y, camera.camera_up_vector.z)).normalized()
|
|
x_axis = y_axis.cross(z_axis).normalized()
|
|
rotation = Matrix((x_axis, y_axis, z_axis))
|
|
rotation.invert()
|
|
location = Vector((camera.camera_view_point.x, camera.camera_view_point.y, camera.camera_view_point.z))
|
|
obj.matrix_world = rotation.to_4x4()
|
|
obj.location = location
|
|
return {"FINISHED"}
|
|
|
|
def set_viewpoint_components(self, viewpoint):
|
|
if not viewpoint.components:
|
|
return
|
|
selected_global_ids = [s.ifc_guid for s in viewpoint.components.selection]
|
|
exception_global_ids = [v.ifc_guid for v in viewpoint.components.visibility.exceptions]
|
|
global_id_colours = {}
|
|
for coloring in viewpoint.components.coloring:
|
|
for component in coloring.components:
|
|
global_id_colours.setdefault(component.ifc_guid, coloring.color)
|
|
|
|
for obj in bpy.data.objects:
|
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
|
continue
|
|
global_id = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId
|
|
is_visible = viewpoint.components.visibility.default_visibility
|
|
if global_id in exception_global_ids:
|
|
is_visible = not is_visible
|
|
if not is_visible:
|
|
obj.hide_set(True)
|
|
continue
|
|
if "IfcSpace" in obj.name:
|
|
if viewpoint.components.view_setup_hints:
|
|
is_visible = viewpoint.components.view_setup_hints.spaces_visible
|
|
else:
|
|
is_visible = False
|
|
elif "IfcOpeningElement" in obj.name:
|
|
if viewpoint.components.view_setup_hints:
|
|
is_visible = viewpoint.components.view_setup_hints.openings_visible
|
|
else:
|
|
is_visible = False
|
|
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])
|
|
|
|
def draw_lines(self, viewpoint):
|
|
gp = bpy.data.grease_pencils.new("BCF")
|
|
scene = bpy.context.scene
|
|
scene.grease_pencil = gp
|
|
scene.frame_set(1)
|
|
layer = gp.layers.new("BCF Annotation", set_active=True)
|
|
layer.thickness = 3
|
|
layer.color = (1, 0, 0)
|
|
frame = layer.frames.new(1)
|
|
stroke = frame.strokes.new()
|
|
stroke.display_mode = "3DSPACE"
|
|
stroke.points.add(len(viewpoint.lines) * 2)
|
|
coords = []
|
|
for l in viewpoint.lines:
|
|
coords.extend([l.start_point.x, l.start_point.y, l.start_point.z, l.end_point.x, l.end_point.y, l.end_point.z])
|
|
stroke.points.foreach_set("co", coords)
|
|
|
|
def create_clipping_planes(self, viewpoint):
|
|
n = 0
|
|
for plane in viewpoint.clipping_planes:
|
|
bpy.ops.bim.add_section_plane()
|
|
if n == 0:
|
|
obj = bpy.data.objects["Section"]
|
|
else:
|
|
obj = bpy.data.objects["Section.{:03d}".format(n)]
|
|
obj.location = (plane.location.x, plane.location.y, plane.location.z)
|
|
obj.rotation_mode = "QUATERNION"
|
|
obj.rotation_quaternion = Vector((plane.direction.x, plane.direction.y, plane.direction.z)).to_track_quat(
|
|
"Z", "Y"
|
|
)
|
|
n += 1
|
|
|
|
def delete_clipping_planes(self):
|
|
collection = bpy.data.collections.get("Sections")
|
|
if not collection:
|
|
return
|
|
for section in collection.objects:
|
|
bpy.context.view_layer.objects.active = section
|
|
bpy.ops.bim.remove_section_plane()
|
|
|
|
def delete_bitmaps(self):
|
|
collection = bpy.data.collections.get("Bitmaps")
|
|
if not collection:
|
|
collection = bpy.data.collections.new("Bitmaps")
|
|
bpy.context.scene.collection.children.link(collection)
|
|
for bitmap in collection.objects:
|
|
bpy.data.objects.remove(bitmap)
|
|
|
|
def create_bitmaps(self, bcfxml, viewpoint, topic):
|
|
collection = bpy.data.collections.get("Bitmaps")
|
|
if not collection:
|
|
collection = bpy.data.collections.new("Bitmaps")
|
|
for bitmap in viewpoint.bitmaps:
|
|
obj = bpy.data.objects.new("Bitmap", None)
|
|
obj.empty_display_type = "IMAGE"
|
|
image = bpy.data.images.load(os.path.join(bcfxml.filepath, topic.guid, bitmap.reference))
|
|
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.up.x, bitmap.up.y, bitmap.up.z))
|
|
z = Vector((bitmap.normal.x, bitmap.normal.y, bitmap.normal.z))
|
|
x = y.cross(z)
|
|
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]]
|
|
)
|
|
obj.location = (bitmap.location.x, bitmap.location.y, bitmap.location.z)
|
|
collection.objects.link(obj)
|
|
|
|
def hex_to_rgb(self, value):
|
|
value = value.lstrip("#")
|
|
lv = len(value)
|
|
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]
|
|
|
|
class OpenBcfReferenceLink(bpy.types.Operator):
|
|
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)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class SelectBcfHeaderFile(bpy.types.Operator):
|
|
bl_idname = "bim.select_bcf_header_file"
|
|
bl_label = "Select BCF Header File"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
def execute(self, context):
|
|
if self.filepath:
|
|
props = bpy.context.scene.BCFProperties
|
|
topic = props.topics[props.active_topic_index]
|
|
topic.file_reference = self.filepath
|
|
return {"FINISHED"}
|
|
|
|
def invoke(self, context, event):
|
|
context.window_manager.fileselect_add(self)
|
|
return {"RUNNING_MODAL"}
|
|
|
|
|
|
class SelectBcfBimSnippetReference(bpy.types.Operator):
|
|
bl_idname = "bim.select_bcf_bim_snippet_reference"
|
|
bl_label = "Select BCF BIM Snippet Reference"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
def execute(self, context):
|
|
if self.filepath:
|
|
props = bpy.context.scene.BCFProperties
|
|
topic = props.topics[props.active_topic_index]
|
|
topic.bim_snippet_reference = self.filepath
|
|
return {"FINISHED"}
|
|
|
|
def invoke(self, context, event):
|
|
context.window_manager.fileselect_add(self)
|
|
return {"RUNNING_MODAL"}
|
|
|
|
|
|
class SelectBcfDocumentReference(bpy.types.Operator):
|
|
bl_idname = "bim.select_bcf_document_reference"
|
|
bl_label = "Select BCF Document Reference"
|
|
bl_options = {"REGISTER", "UNDO"}
|
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
|
|
|
def execute(self, context):
|
|
if self.filepath:
|
|
props = bpy.context.scene.BCFProperties
|
|
topic = props.topics[props.active_topic_index]
|
|
topic.document_reference = self.filepath
|
|
return {"FINISHED"}
|
|
|
|
def invoke(self, context, event):
|
|
context.window_manager.fileselect_add(self)
|
|
return {"RUNNING_MODAL"}
|