Make BCF project UI (more) foolproof (#1662)

* Various code fixes and optimizations

* Prevent running operators if conditions not met
This commit is contained in:
Gorgious56
2021-08-16 23:52:55 +02:00
committed by GitHub
parent 1b4d0bf577
commit 06b43e7f80
@@ -7,6 +7,7 @@ import numpy as np
import ifcopenshell import ifcopenshell
import ifcopenshell.util.unit import ifcopenshell.util.unit
from . import bcfstore from . import bcfstore
from blenderbim.bim.module.bcf.prop import getBcfViewpoints
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from math import radians, degrees, atan, tan, cos, sin from math import radians, degrees, atan, tan, cos, sin
from mathutils import Vector, Matrix, Euler, geometry from mathutils import Vector, Matrix, Euler, geometry
@@ -56,13 +57,10 @@ class LoadBcfTopics(bpy.types.Operator):
def execute(self, context): def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.get_topics() bcfxml.get_topics()
while len(context.scene.BCFProperties.topics) > 0: context.scene.BCFProperties.topics.clear()
context.scene.BCFProperties.topics.remove(0) for index, topic_guid in enumerate(bcfxml.topics.keys()):
index = 0
for topic_guid in bcfxml.topics.keys():
new = context.scene.BCFProperties.topics.add() new = context.scene.BCFProperties.topics.add()
bpy.ops.bim.load_bcf_topic(topic_guid = topic_guid, topic_index = index) bpy.ops.bim.load_bcf_topic(topic_guid = topic_guid, topic_index = index)
index += 1
return {"FINISHED"} return {"FINISHED"}
@@ -95,16 +93,17 @@ class LoadBcfTopic(bpy.types.Operator):
for key, value in data_map.items(): for key, value in data_map.items():
if value is not None: if value is not None:
setattr(new, key, str(value)) setattr(new, key, str(value))
while len(new.reference_links) > 0:
new.reference_links.remove(0) new.reference_links.clear()
for reference_link in topic.reference_links: for reference_link in topic.reference_links:
new2 = new.reference_links.add() new_reference_link = new.reference_links.add()
new2.name = reference_link new_reference_link.name = reference_link
while len(new.labels) > 0:
new.labels.remove(0) new.labels.clear()
for label in topic.labels: for label in topic.labels:
new2 = new.labels.add() new_label = new.labels.add()
new2.name = label new_label.name = label
if topic.bim_snippet: if topic.bim_snippet:
data_map = { data_map = {
"type": topic.bim_snippet.snippet_type, "type": topic.bim_snippet.snippet_type,
@@ -115,10 +114,10 @@ class LoadBcfTopic(bpy.types.Operator):
for key, value in data_map.items(): for key, value in data_map.items():
if value is not None: if value is not None:
setattr(new.bim_snippet, key, value) setattr(new.bim_snippet, key, value)
while len(new.document_references) > 0:
new.document_references.remove(0) new.document_references.clear()
for doc in topic.document_references: for doc in topic.document_references:
new2 = new.document_references.add() new_document_references = new.document_references.add()
data_map = { data_map = {
"reference": doc.referenced_document, "reference": doc.referenced_document,
"description": doc.description, "description": doc.description,
@@ -127,12 +126,13 @@ class LoadBcfTopic(bpy.types.Operator):
} }
for key, value in data_map.items(): for key, value in data_map.items():
if value is not None: if value is not None:
setattr(new2, key, value) setattr(new_document_references, key, value)
while len(new.related_topics) > 0:
new.related_topics.remove(0) new.related_topics.clear()
for related_topic in topic.related_topics: for related_topic in topic.related_topics:
new2 = new.related_topics.add() new_related_topic = new.related_topics.add()
new2.name = related_topic.guid new_related_topic.name = related_topic.guid
bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid) bpy.ops.bim.load_bcf_comments(topic_guid = topic.guid)
return {"FINISHED"} return {"FINISHED"}
@@ -250,11 +250,13 @@ class AddBcfTopic(bpy.types.Operator):
bl_label = "Add BCF Topic" bl_label = "Add BCF Topic"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.author
def execute(self, context): def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.add_topic() bcfxml.add_topic()
new = context.scene.BCFProperties.topics.add()
new.name = "New Topic"
bpy.ops.bim.load_bcf_topics() bpy.ops.bim.load_bcf_topics()
return {"FINISHED"} return {"FINISHED"}
@@ -264,6 +266,15 @@ class AddBcfBimSnippet(bpy.types.Operator):
bl_label = "Add BCF BIM Snippet" bl_label = "Add BCF BIM Snippet"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
props = context.scene.BCFProperties
return all((getattr(props.topics[props.active_topic_index], attr, False) for attr in (
"bim_snippet_reference",
"bim_snippet_schema",
"bim_snippet_type"
)))
def execute(self, context): def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties props = context.scene.BCFProperties
@@ -283,10 +294,16 @@ class AddBcfRelatedTopic(bpy.types.Operator):
bl_label = "Add BCF Related Topic" bl_label = "Add BCF Related Topic"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): @classmethod
def poll(cls, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index] blender_topic = props.topics[props.active_topic_index]
if not blender_topic.related_topic:
return False
if blender_topic.related_topic == blender_topic.title:
# Prevent adding self as related topic
return False
related_topic = None related_topic = None
for topic in bcfxml.topics.values(): for topic in bcfxml.topics.values():
if topic.title == blender_topic.related_topic: if topic.title == blender_topic.related_topic:
@@ -294,7 +311,18 @@ class AddBcfRelatedTopic(bpy.types.Operator):
related_topic.guid = topic.guid related_topic.guid = topic.guid
break break
if not related_topic: if not related_topic:
return {"FINISHED"} return False
if str(related_topic.guid) in [t.name for t in blender_topic.related_topics]:
# Prevent adding the same related topic more than once
return False
return True
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
related_topic = bcf.v2.data.RelatedTopic()
related_topic.guid = next((t for t in bcfxml.topics.values() if t.title == blender_topic.related_topic)).guid
topic = bcfxml.topics[blender_topic.name] topic = bcfxml.topics[blender_topic.name]
topic.related_topics.append(related_topic) topic.related_topics.append(related_topic)
bcfxml.edit_topic(topic) bcfxml.edit_topic(topic)
@@ -307,6 +335,11 @@ class AddBcfHeaderFile(bpy.types.Operator):
bl_label = "Add BCF Header File" bl_label = "Add BCF Header File"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
props = context.scene.BCFProperties
return props.topics[props.active_topic_index].file_reference
def execute(self, context): def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties props = context.scene.BCFProperties
@@ -333,7 +366,7 @@ class ViewBcfTopic(bpy.types.Operator):
def execute(self, context): def execute(self, context):
for index, topic in enumerate(context.scene.BCFProperties.topics): for index, topic in enumerate(context.scene.BCFProperties.topics):
if topic.guid.lower() == self.topic_guid.lower(): if topic.name.lower() == self.topic_guid.lower():
context.scene.BCFProperties.active_topic_index = index context.scene.BCFProperties.active_topic_index = index
return {"FINISHED"} return {"FINISHED"}
@@ -343,45 +376,49 @@ class AddBcfViewpoint(bpy.types.Operator):
bl_label = "Add BCF Viewpoint" bl_label = "Add BCF Viewpoint"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.scene.camera
def execute(self, context): def execute(self, context):
if not context.scene.camera:
return {"FINISHED"}
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
blender_camera = context.scene.camera
props = context.scene.BCFProperties props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index] blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name] topic = bcfxml.topics[blender_topic.name]
viewpoint = bcf.v2.data.Viewpoint() viewpoint = bcf.v2.data.Viewpoint()
if context.scene.camera.data.type == "ORTHO": if blender_camera.data.type == "ORTHO":
camera = bcf.v2.data.OrthogonalCamera() camera = bcf.v2.data.OrthogonalCamera()
camera.view_to_world_scale = context.scene.camera.data.ortho_scale camera.view_to_world_scale = blender_camera.data.ortho_scale
viewpoint.orthogonal_camera = camera viewpoint.orthogonal_camera = camera
elif context.scene.camera.data.type == "PERSP": elif blender_camera.data.type == "PERSP":
camera = bcf.v2.data.PerspectiveCamera() camera = bcf.v2.data.PerspectiveCamera()
camera.field_of_view = degrees(context.scene.camera.data.angle) camera.field_of_view = degrees(blender_camera.data.angle)
viewpoint.perspective_camera = camera viewpoint.perspective_camera = camera
camera.camera_view_point.x = context.scene.camera.location.x camera.camera_view_point.x = blender_camera.location.x
camera.camera_view_point.y = context.scene.camera.location.y camera.camera_view_point.y = blender_camera.location.y
camera.camera_view_point.z = context.scene.camera.location.z camera.camera_view_point.z = blender_camera.location.z
direction = context.scene.camera.matrix_world.to_quaternion() @ Vector((0.0, 0.0, -1.0)) direction = blender_camera.matrix_world.to_quaternion() @ Vector((0.0, 0.0, -1.0))
camera.camera_direction.x = direction.x camera.camera_direction.x = direction.x
camera.camera_direction.y = direction.y camera.camera_direction.y = direction.y
camera.camera_direction.z = direction.z camera.camera_direction.z = direction.z
up = context.scene.camera.matrix_world.to_quaternion() @ Vector((0.0, 1.0, 0.0)) up = blender_camera.matrix_world.to_quaternion() @ Vector((0.0, 1.0, 0.0))
camera.camera_up_vector.x = up.x camera.camera_up_vector.x = up.x
camera.camera_up_vector.y = up.y camera.camera_up_vector.y = up.y
camera.camera_up_vector.z = up.z camera.camera_up_vector.z = up.z
old_file_format = context.scene.render.image_settings.file_format blender_render = context.scene.render
context.scene.render.image_settings.file_format = "PNG" old_file_format = blender_render.image_settings.file_format
old_filepath = context.scene.render.filepath blender_render.image_settings.file_format = "PNG"
context.scene.render.filepath = os.path.join(context.scene.BIMProperties.data_dir, "snapshot.png") old_filepath = blender_render.filepath
blender_render.filepath = os.path.join(context.scene.BIMProperties.data_dir, "snapshot.png")
bpy.ops.render.opengl(write_still=True) bpy.ops.render.opengl(write_still=True)
viewpoint.snapshot = context.scene.render.filepath viewpoint.snapshot = blender_render.filepath
bcfxml.add_viewpoint(topic, viewpoint) bcfxml.add_viewpoint(topic, viewpoint)
context.scene.render.filepath = old_filepath blender_render.filepath = old_filepath
context.scene.render.image_settings.file_format = old_file_format blender_render.image_settings.file_format = old_file_format
props.active_topic_index = props.active_topic_index # refreshes the BCF Topic props.active_topic_index = props.active_topic_index # refreshes the BCF Topic
return {"FINISHED"} return {"FINISHED"}
@@ -391,6 +428,10 @@ class RemoveBcfViewpoint(bpy.types.Operator):
bl_label = "Remove BCF Viewpoint" bl_label = "Remove BCF Viewpoint"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return getBcfViewpoints(None, context)
def execute(self, context): def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties props = context.scene.BCFProperties
@@ -423,13 +464,16 @@ class AddBcfReferenceLink(bpy.types.Operator):
bl_label = "Add BCF Reference Link" bl_label = "Add BCF Reference Link"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
props = context.scene.BCFProperties
return props.topics[props.active_topic_index].reference_link
def execute(self, context): def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index] blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name] topic = bcfxml.topics[blender_topic.name]
if not blender_topic.reference_link:
return {"FINISHED"}
topic.reference_links.append(blender_topic.reference_link) topic.reference_links.append(blender_topic.reference_link)
bcfxml.edit_topic(topic) bcfxml.edit_topic(topic)
bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index) bpy.ops.bim.load_bcf_topic(topic_guid = topic.guid, topic_index = props.active_topic_index)
@@ -442,13 +486,16 @@ class AddBcfDocumentReference(bpy.types.Operator):
bl_label = "Add BCF Document Reference" bl_label = "Add BCF Document Reference"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
props = context.scene.BCFProperties
return props.topics[props.active_topic_index].document_reference
def execute(self, context): def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index] blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name] topic = bcfxml.topics[blender_topic.name]
if not blender_topic.document_reference:
return {"FINISHED"}
document_reference = bcf.v2.data.DocumentReference() document_reference = bcf.v2.data.DocumentReference()
document_reference.referenced_document = blender_topic.document_reference document_reference.referenced_document = blender_topic.document_reference
document_reference.description = blender_topic.document_reference_description or None document_reference.description = blender_topic.document_reference_description or None
@@ -464,13 +511,16 @@ class AddBcfLabel(bpy.types.Operator):
bl_label = "Add BCF Label" bl_label = "Add BCF Label"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
props = context.scene.BCFProperties
return props.topics[props.active_topic_index].label
def execute(self, context): def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index] blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name] topic = bcfxml.topics[blender_topic.name]
if not blender_topic.label:
return {"FINISHED"}
new = blender_topic.labels.add() new = blender_topic.labels.add()
new.name = blender_topic.label new.name = blender_topic.label
topic.labels.append(blender_topic.label) topic.labels.append(blender_topic.label)
@@ -508,6 +558,8 @@ class EditBcfLabels(bpy.types.Operator):
blender_topic = props.topics[props.active_topic_index] blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name] topic = bcfxml.topics[blender_topic.name]
for index, label in enumerate(blender_topic.labels): for index, label in enumerate(blender_topic.labels):
if index >= len(topic.labels):
break
if label.name == topic.labels[index]: if label.name == topic.labels[index]:
continue continue
topic.labels[index] = blender_topic.labels[index].name topic.labels[index] = blender_topic.labels[index].name
@@ -640,16 +692,24 @@ class AddBcfComment(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
comment_guid: bpy.props.StringProperty() comment_guid: bpy.props.StringProperty()
@classmethod
def poll(cls, context):
props = context.scene.BCFProperties
topic = props.topics[props.active_topic_index]
if not topic.comment:
return False
if topic.has_related_viewpoint and not getBcfViewpoints(None, context):
return False
return True
def execute(self, context): def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index] blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name] topic = bcfxml.topics[blender_topic.name]
if not blender_topic.comment:
return {"FINISHED"}
comment = bcf.v2.data.Comment() comment = bcf.v2.data.Comment()
comment.comment = blender_topic.comment comment.comment = blender_topic.comment
if blender_topic.has_related_viewpoint and blender_topic.viewpoints: if blender_topic.has_related_viewpoint:
comment.viewpoint = bcf.v2.data.Viewpoint() comment.viewpoint = bcf.v2.data.Viewpoint()
comment.viewpoint.guid = blender_topic.viewpoints comment.viewpoint.guid = blender_topic.viewpoints
bcfxml.add_comment(topic, comment) bcfxml.add_comment(topic, comment)
@@ -662,14 +722,20 @@ class ActivateBcfViewpoint(bpy.types.Operator):
bl_label = "Activate BCF Viewpoint" bl_label = "Activate BCF Viewpoint"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name]
return topic.viewpoints
def execute(self, context): def execute(self, context):
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
bcfxml = bcfstore.BcfStore.get_bcfxml() bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties props = context.scene.BCFProperties
blender_topic = props.topics[props.active_topic_index] blender_topic = props.topics[props.active_topic_index]
topic = bcfxml.topics[blender_topic.name] topic = bcfxml.topics[blender_topic.name]
if not topic.viewpoints:
return {"FINISHED"}
viewpoint_guid = blender_topic.viewpoints viewpoint_guid = blender_topic.viewpoints
viewpoint = topic.viewpoints[viewpoint_guid] viewpoint = topic.viewpoints[viewpoint_guid]