Fix bug where BlenderBIM Add-on BCF module was completely broken. Now works with latest BCF library for BCF-XML v2.

This commit is contained in:
Dion Moult
2023-02-18 21:12:36 +11:00
parent e712ef344d
commit 2f73af7e6a
4 changed files with 248 additions and 165 deletions
@@ -20,43 +20,44 @@ import bpy
from . import ui, prop, operator
classes = (
operator.NewBcfProject,
operator.LoadBcfProject,
operator.LoadBcfTopics,
operator.LoadBcfTopic,
operator.LoadBcfComments,
operator.EditBcfProjectName,
operator.EditBcfAuthor,
operator.EditBcfTopicName,
operator.EditBcfTopic,
operator.SaveBcfProject,
operator.AddBcfTopic,
operator.AddBcfHeaderFile,
operator.AddBcfBimSnippet,
operator.AddBcfReferenceLink,
operator.AddBcfDocumentReference,
operator.AddBcfLabel,
operator.AddBcfRelatedTopic,
operator.ViewBcfTopic,
operator.RemoveBcfTopic,
operator.RemoveBcfComment,
operator.RemoveBcfBimSnippet,
operator.RemoveBcfReferenceLink,
operator.RemoveBcfDocumentReference,
operator.RemoveBcfRelatedTopic,
operator.EditBcfReferenceLinks,
operator.EditBcfLabels,
operator.RemoveBcfLabel,
operator.EditBcfComment,
operator.AddBcfComment,
operator.ActivateBcfViewpoint,
operator.AddBcfBimSnippet,
operator.AddBcfComment,
operator.AddBcfDocumentReference,
operator.AddBcfHeaderFile,
operator.AddBcfLabel,
operator.AddBcfReferenceLink,
operator.AddBcfRelatedTopic,
operator.AddBcfTopic,
operator.AddBcfViewpoint,
operator.RemoveBcfViewpoint,
operator.RemoveBcfFile,
operator.EditBcfAuthor,
operator.EditBcfComment,
operator.EditBcfLabels,
operator.EditBcfProjectName,
operator.EditBcfReferenceLinks,
operator.EditBcfTopic,
operator.EditBcfTopicName,
operator.ExtractBcfFile,
operator.LoadBcfComments,
operator.LoadBcfProject,
operator.LoadBcfTopic,
operator.LoadBcfTopics,
operator.NewBcfProject,
operator.OpenBcfReferenceLink,
operator.SelectBcfHeaderFile,
operator.RemoveBcfBimSnippet,
operator.RemoveBcfComment,
operator.RemoveBcfDocumentReference,
operator.RemoveBcfFile,
operator.RemoveBcfLabel,
operator.RemoveBcfReferenceLink,
operator.RemoveBcfRelatedTopic,
operator.RemoveBcfTopic,
operator.RemoveBcfViewpoint,
operator.SaveBcfProject,
operator.SelectBcfBimSnippetReference,
operator.SelectBcfDocumentReference,
operator.SelectBcfHeaderFile,
operator.ViewBcfTopic,
prop.BcfReferenceLink,
prop.BcfLabel,
prop.BcfBimSnippet,
@@ -20,14 +20,20 @@ import os
import bpy
import bcf
import bcf.bcfxml
import bcf.v2.model
import uuid
import numpy as np
import tempfile
import webbrowser
import ifcopenshell
import ifcopenshell.util.unit
from . import bcfstore
import blenderbim.bim.module.bcf.prop as bcf_prop
from . import bcfstore
from pathlib import Path
from blenderbim.bim.ifc import IfcStore
from math import radians, degrees, atan, tan, cos, sin
from mathutils import Vector, Matrix, Euler, geometry
from xsdata.models.datatype import XmlDateTime
class NewBcfProject(bpy.types.Operator):
@@ -56,7 +62,6 @@ class LoadBcfProject(bpy.types.Operator):
if self.filepath:
bcfstore.BcfStore.bcfxml = bcf.bcfxml.load(self.filepath)
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.get_project()
context.scene.BCFProperties.name = bcfxml.project.name
bpy.ops.bim.load_bcf_topics()
context.scene.BCFProperties.is_loaded = True
@@ -74,7 +79,6 @@ class LoadBcfTopics(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.get_topics()
context.scene.BCFProperties.topics.clear()
for index, topic_guid in enumerate(bcfxml.topics.keys()):
new = context.scene.BCFProperties.topics.add()
@@ -91,7 +95,7 @@ class LoadBcfTopic(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.get_topic(self.topic_guid)
topic = bcfxml.topics[self.topic_guid]
bcfxml.get_header(self.topic_guid)
new = context.scene.BCFProperties.topics[self.topic_index]
data_map = {
@@ -125,10 +129,10 @@ class LoadBcfTopic(bpy.types.Operator):
if topic.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,
"type": topic.topic.bim_snippet.snippet_type,
"is_external": topic.topic.bim_snippet.is_external,
"reference": topic.topic.bim_snippet.reference,
"schema": topic.topic.bim_snippet.reference_schema,
}
for key, value in data_map.items():
if value is not None:
@@ -164,10 +168,9 @@ class LoadBcfComments(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.get_comments(self.topic_guid)
blender_topic = context.scene.BCFProperties.topics.get(self.topic_guid)
blender_topic.comments.clear()
for comment in bcfxml.topics[self.topic_guid].comments.values():
for comment in bcfxml.topics[self.topic_guid].comments:
new = blender_topic.comments.add()
data_map = {
"name": comment.guid,
@@ -192,7 +195,6 @@ class EditBcfProjectName(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.project.name = context.scene.BCFProperties.name
bcfxml.edit_project()
return {"FINISHED"}
@@ -218,7 +220,6 @@ class EditBcfTopicName(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.topics[blender_topic.name]
topic.title = blender_topic.title
bcfxml.edit_topic(topic)
return {"FINISHED"}
@@ -242,7 +243,6 @@ class EditBcfTopic(bpy.types.Operator):
topic.topic_status = blender_topic.status or None
topic.topic_type = blender_topic.type or None
bcfxml.edit_topic(topic)
props.refresh_topic(context)
return {"FINISHED"}
@@ -256,7 +256,7 @@ class SaveBcfProject(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
bcfxml.save_project(self.filepath)
bcfxml.save(self.filepath)
return {"FINISHED"}
def invoke(self, context, event):
@@ -299,15 +299,17 @@ class AddBcfBimSnippet(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bim_snippet = bcf.v2.data.BimSnippet()
bim_snippet.reference = props.bim_snippet_reference
bim_snippet.reference_schema = props.bim_snippet_schema
bim_snippet.snippet_type = props.bim_snippet_type
bcfxml.add_bim_snippet(topic, bim_snippet)
is_external = "://" in props.bim_snippet_reference
bim_snippet = bcf.v2.model.BimSnippet(
reference=props.bim_snippet_reference if is_external else Path(props.bim_snippet_reference).name,
reference_schema=props.bim_snippet_schema,
snippet_type=props.bim_snippet_type,
is_external=is_external,
)
topic.topic.bim_snippet = bim_snippet
with open(props.bim_snippet_reference, "r") as f:
topic.bim_snippet = f.read()
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
bim_snippet.reference = ""
bim_snippet.reference_schema = ""
bim_snippet.snippet_type = ""
return {"FINISHED"}
@@ -326,15 +328,14 @@ class AddBcfRelatedTopic(bpy.types.Operator):
if props.related_topic == blender_topic.title:
# Prevent adding self as related topic
return False
related_topic = None
related_topic_guid = None
for topic in bcfxml.topics.values():
if topic.title == props.related_topic:
related_topic = bcf.v2.data.RelatedTopic()
related_topic.guid = topic.guid
if topic.topic.title == props.related_topic:
related_topic_guid = topic.guid
break
if not related_topic:
if not related_topic_guid:
return False
if str(related_topic.guid) in [t.name for t in blender_topic.related_topics]:
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
@@ -343,11 +344,12 @@ class AddBcfRelatedTopic(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.active_topic
related_topic = bcf.v2.data.RelatedTopic()
related_topic.guid = next((t for t in bcfxml.topics.values() if t.title == props.related_topic)).guid
topic = bcfxml.topics[blender_topic.name]
topic.related_topics.append(related_topic)
bcfxml.edit_topic(topic)
topic.topic.related_topic.append(
bcf.v2.model.TopicRelatedTopic(
guid=next((t for t in bcfxml.topics.values() if t.topic.title == props.related_topic)).guid
)
)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
props.related_topic = ""
return {"FINISHED"}
@@ -367,15 +369,24 @@ class AddBcfHeaderFile(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
header_file = bcf.v2.data.HeaderFile()
header_file.reference = props.file_reference
if not os.path.exists(header_file.reference):
header_file.filename = header_file.reference
if len(props.file_ifc_project) == 22:
header_file.ifc_project = props.file_ifc_project
if len(props.file_ifc_spatial_structure_element) == 22:
header_file.ifc_spatial_structure_element = props.file_ifc_spatial_structure_element
bcfxml.add_file(topic, header_file)
is_external = "://" in props.file_reference
filename = Path(props.file_reference).name if os.path.exists(props.file_reference) else None
if filename:
with open(props.file_reference, "r") as f:
topic.reference_files[filename] = f.read()
header_file = bcf.v2.model.HeaderFile(
filename=filename,
date=XmlDateTime.now(),
reference=props.file_reference if is_external else Path(props.file_reference).name,
ifc_project=props.file_ifc_project,
ifc_spatial_structure_element=props.file_ifc_spatial_structure_element,
is_external=is_external,
)
topic.header.file.append(header_file)
props.refresh_topic(context)
return {"FINISHED"}
@@ -409,27 +420,34 @@ class AddBcfViewpoint(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
viewpoint = bcf.v2.data.Viewpoint()
if blender_camera.data.type == "ORTHO":
camera = bcf.v2.data.OrthogonalCamera()
camera.view_to_world_scale = blender_camera.data.ortho_scale
viewpoint.orthogonal_camera = camera
elif blender_camera.data.type == "PERSP":
camera = bcf.v2.data.PerspectiveCamera()
camera.field_of_view = degrees(blender_camera.data.angle)
viewpoint.perspective_camera = camera
camera.camera_view_point.x = blender_camera.location.x
camera.camera_view_point.y = blender_camera.location.y
camera.camera_view_point.z = blender_camera.location.z
direction = blender_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 = blender_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
camera_view_point = bcf.v2.model.Point(
x=blender_camera.location.x, y=blender_camera.location.y, z=blender_camera.location.z
)
camera_direction = bcf.v2.model.Direction(x=direction.x, y=direction.y, z=direction.z)
camera_up_vector = bcf.v2.model.Direction(x=up.x, y=up.y, z=up.z)
if blender_camera.data.type == "ORTHO":
camera = bcf.v2.model.OrthogonalCamera(
view_to_world_scale=blender_camera.data.ortho_scale,
camera_view_point=camera_view_point,
camera_direction=camera_direction,
camera_up_vector=camera_up_vector,
)
visualization_info = bcf.v2.model.VisualizationInfo(orthogonal_camera=camera)
elif blender_camera.data.type == "PERSP":
camera = bcf.v2.model.PerspectiveCamera(
field_of_view=degrees(blender_camera.data.angle),
camera_view_point=camera_view_point,
camera_direction=camera_direction,
camera_up_vector=camera_up_vector,
)
visualization_info = bcf.v2.model.VisualizationInfo(guid=str(uuid.uuid4()), perspective_camera=camera)
# TODO allow the user to enable or disable snapshotting
snapshot = None
blender_render = context.scene.render
old_file_format = blender_render.image_settings.file_format
@@ -437,9 +455,17 @@ class AddBcfViewpoint(bpy.types.Operator):
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)
viewpoint.snapshot = blender_render.filepath
with open(blender_render.filepath, "rb") as f:
snapshot = f.read()
# viewpoint.snapshot = blender_render.filepath
# TODO we should handle selection and visibility state
vizinfo = bcf.v2.visinfo.VisualizationInfoHandler(visualization_info=visualization_info, snapshot=snapshot)
topic.viewpoints[vizinfo.guid + ".bcfv"] = vizinfo
topic.markup.viewpoints.append(
bcf.v2.model.ViewPoint(viewpoint=vizinfo.guid + ".bcfv", guid=vizinfo.guid, snapshot=vizinfo.guid + ".png")
)
bcfxml.add_viewpoint(topic, viewpoint)
blender_render.filepath = old_filepath
blender_render.image_settings.file_format = old_file_format
props.refresh_topic(context)
@@ -459,9 +485,15 @@ class RemoveBcfViewpoint(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
blender_topic = props.active_topic
viewpoint_guid = blender_topic.viewpoints
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_viewpoint(viewpoint_guid, topic)
for key, viewpoint in topic.viewpoints.items():
if viewpoint.guid == blender_topic.viewpoints:
break
del topic.viewpoints[key]
for i, viewpoint in enumerate(topic.markup.viewpoints):
if viewpoint.guid == blender_topic.viewpoints:
break
del topic.markup.viewpoints[i]
props.refresh_topic(context)
return {"FINISHED"}
@@ -477,7 +509,7 @@ class RemoveBcfFile(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_file(topic, self.index)
del topic.header.file[self.index]
props.refresh_topic(context)
return {"FINISHED"}
@@ -495,8 +527,7 @@ class RemoveBcfTopic(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
props = context.scene.BCFProperties
topic_to_delete = props.active_topic
bcfxml.delete_topic(topic_to_delete.name)
del bcfxml.topics[props.active_topic.name]
bpy.ops.bim.load_bcf_topics()
return {"FINISHED"}
@@ -515,8 +546,7 @@ class AddBcfReferenceLink(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
topic.reference_links.append(props.reference_link)
bcfxml.edit_topic(topic)
topic.topic.reference_link.append(props.reference_link)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
props.reference_link = ""
return {"FINISHED"}
@@ -536,10 +566,23 @@ class AddBcfDocumentReference(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
document_reference = bcf.v2.data.DocumentReference()
document_reference.referenced_document = props.document_reference
document_reference.description = props.document_reference_description or None
bcfxml.add_document_reference(topic, document_reference)
is_external = "://" in props.document_reference
filename = Path(props.document_reference).name if os.path.exists(props.document_reference) else None
if filename:
with open(props.document_reference, "rb") as f:
topic.document_references[filename] = f.read()
document_reference = bcf.v2.model.TopicDocumentReference(
referenced_document=props.document_reference if is_external else filename,
description=props.document_reference_description or None,
guid=str(uuid.uuid4()),
is_external=is_external,
)
topic.topic.document_reference.append(document_reference)
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
props.document_reference = ""
props.document_reference_description = ""
@@ -562,8 +605,7 @@ class AddBcfLabel(bpy.types.Operator):
topic = bcfxml.topics[blender_topic.name]
new = blender_topic.labels.add()
new.name = props.label
topic.labels.append(props.label)
bcfxml.edit_topic(topic)
topic.topic.labels.append(props.label)
props.label = ""
return {"FINISHED"}
@@ -578,11 +620,10 @@ class EditBcfReferenceLinks(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
for index, reference_link in enumerate(topic.reference_links):
for index, reference_link in enumerate(topic.topic.reference_link):
if reference_link == blender_topic.reference_links[index].name:
continue
topic.reference_links[index] = blender_topic.reference_links[index].name
bcfxml.edit_topic(topic)
topic.topic.reference_link[index] = blender_topic.reference_links[index].name
return {"FINISHED"}
@@ -597,12 +638,11 @@ class EditBcfLabels(bpy.types.Operator):
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
for index, label in enumerate(blender_topic.labels):
if index >= len(topic.labels):
if index >= len(topic.topic.labels):
break
if label.name == topic.labels[index]:
if label.name == topic.topic.labels[index]:
continue
topic.labels[index] = blender_topic.labels[index].name
bcfxml.edit_topic(topic)
topic.topic.labels[index] = blender_topic.labels[index].name
return {"FINISHED"}
@@ -617,8 +657,7 @@ class RemoveBcfReferenceLink(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
del topic.reference_links[self.index]
bcfxml.edit_topic(topic)
del topic.topic.reference_link[self.index]
blender_topic.reference_links.remove(self.index)
return {"FINISHED"}
@@ -634,8 +673,7 @@ class RemoveBcfLabel(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
del topic.labels[self.index]
bcfxml.edit_topic(topic)
del topic.topic.labels[self.index]
blender_topic.labels.remove(self.index)
return {"FINISHED"}
@@ -650,7 +688,7 @@ class RemoveBcfBimSnippet(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_bim_snippet(topic)
topic.topic.bim_snippet = None
blender_topic.bim_snippet.schema = ""
blender_topic.bim_snippet.reference = ""
blender_topic.bim_snippet.type = ""
@@ -668,7 +706,7 @@ class RemoveBcfDocumentReference(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_document_reference(topic, self.index)
del topic.topic.document_reference[self.index]
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
return {"FINISHED"}
@@ -684,8 +722,7 @@ class RemoveBcfRelatedTopic(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
del topic.related_topics[self.index]
bcfxml.edit_topic(topic)
del topic.topic.related_topic[self.index]
bpy.ops.bim.load_bcf_topic(topic_guid=topic.guid, topic_index=props.active_topic_index)
return {"FINISHED"}
@@ -701,7 +738,10 @@ class RemoveBcfComment(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
bcfxml.delete_comment(self.comment_guid, topic)
for i, comment in enumerate(topic.comments):
if comment.guid == self.comment_guid:
break
del topic.comments[i]
bpy.ops.bim.load_bcf_comments(topic_guid=topic.guid)
return {"FINISHED"}
@@ -718,9 +758,11 @@ class EditBcfComment(bpy.types.Operator):
blender_topic = props.active_topic
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)
for i, comment in enumerate(topic.comments):
if comment.guid == self.comment_guid:
comment.comment = blender_comment.comment
comment.modified_date = XmlDateTime.now()
comment.modified_author = context.scene.BCFProperties.author
bpy.ops.bim.load_bcf_comments(topic_guid=topic.guid)
return {"FINISHED"}
@@ -745,12 +787,15 @@ class AddBcfComment(bpy.types.Operator):
props = context.scene.BCFProperties
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
comment = bcf.v2.data.Comment()
comment.comment = props.comment
comment = bcf.v2.model.Comment(
date=XmlDateTime.now(),
author=context.scene.BCFProperties.author,
comment=props.comment,
guid=str(uuid.uuid4()),
)
if props.has_related_viewpoint:
comment.viewpoint = bcf.v2.data.Viewpoint()
comment.viewpoint.guid = blender_topic.viewpoints
bcfxml.add_comment(topic, comment)
comment.viewpoint = bcf.v2.model.CommentViewpoint(guid=blender_topic.viewpoints)
topic.comments.append(comment)
bpy.ops.bim.load_bcf_comments(topic_guid=topic.guid)
props.comment = ""
props.has_related_viewpoint = False
@@ -791,11 +836,13 @@ class ActivateBcfViewpoint(bpy.types.Operator):
cam_height = context.scene.render.resolution_y
cam_aspect = cam_width / cam_height
obj.data.background_images.clear()
if viewpoint.snapshot:
obj.data.show_background_images = True
obj.data.background_images.clear()
background = obj.data.background_images.new()
background.image = bpy.data.images.load(os.path.join(bcfxml.filepath, topic.guid, viewpoint.snapshot))
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(viewpoint.snapshot)
background.image = bpy.data.images.load(f.name)
src_width = background.image.size[0]
src_height = background.image.size[1]
src_aspect = src_width / src_height
@@ -805,6 +852,9 @@ class ActivateBcfViewpoint(bpy.types.Operator):
else:
background.frame_method = "CROP"
background.display_depth = "FRONT"
else:
obj.data.show_background_images = False
area = next(area for area in context.screen.areas if area.type == "VIEW_3D")
area.spaces[0].region_3d.view_perspective = "CAMERA"
@@ -814,27 +864,27 @@ class ActivateBcfViewpoint(bpy.types.Operator):
gp = bpy.data.grease_pencils.get("BCF")
if gp:
bpy.data.grease_pencils.remove(gp)
if viewpoint.lines:
if viewpoint.visualization_info.lines:
self.draw_lines(viewpoint, context)
self.delete_clipping_planes(context)
if viewpoint.clipping_planes:
if viewpoint.visualization_info.clipping_planes:
self.create_clipping_planes(viewpoint)
self.delete_bitmaps(context)
if viewpoint.bitmaps:
if viewpoint.visualization_info.bitmap:
self.create_bitmaps(bcfxml, viewpoint, topic)
self.setup_camera(viewpoint, obj, cam_aspect, context)
return {"FINISHED"}
def setup_camera(self, viewpoint, obj, cam_aspect, context):
if viewpoint.orthogonal_camera:
camera = viewpoint.orthogonal_camera
if viewpoint.visualization_info.orthogonal_camera:
camera = viewpoint.visualization_info.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.ortho_scale = viewpoint.visualization_info.orthogonal_camera.view_to_world_scale
elif viewpoint.visualization_info.perspective_camera:
camera = viewpoint.visualization_info.perspective_camera
obj.data.type = "PERSP"
if cam_aspect >= 1:
obj.data.angle = radians(camera.field_of_view)
@@ -875,15 +925,15 @@ class ActivateBcfViewpoint(bpy.types.Operator):
obj.matrix_world = Matrix(matrix.tolist())
def set_viewpoint_components(self, viewpoint, context):
if not viewpoint.components:
if not viewpoint.visualization_info.components:
return
# Operators with context overrides are used because they are
# significantly faster than looping through all objects
exception_global_ids = {v.ifc_guid for v in viewpoint.components.visibility.exceptions}
exception_global_ids = {v.ifc_guid for v in viewpoint.visualization_info.components.visibility.exceptions}
if viewpoint.components.visibility.default_visibility:
if viewpoint.visualization_info.components.visibility.default_visibility:
old = context.area.type
context.area.type = "VIEW_3D"
bpy.ops.object.hide_view_clear()
@@ -907,11 +957,13 @@ class ActivateBcfViewpoint(bpy.types.Operator):
bpy.ops.object.hide_view_set(context_override, unselected=True)
context.area.type = old
if viewpoint.components.view_setup_hints:
if not viewpoint.components.view_setup_hints.spaces_visible:
if viewpoint.visualization_info.components.view_setup_hints:
if not viewpoint.visualization_info.components.view_setup_hints.spaces_visible:
self.hide_spaces(context)
if viewpoint.components.view_setup_hints.openings_visible is not None:
self.set_openings_visibility(viewpoint.components.view_setup_hints.openings_visible, context)
if viewpoint.visualization_info.components.view_setup_hints.openings_visible is not None:
self.set_openings_visibility(
viewpoint.visualization_info.components.view_setup_hints.openings_visible, context
)
else:
self.hide_spaces(context)
self.set_openings_visibility(False, context)
@@ -931,7 +983,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
collection.hide_viewport = not is_visible
def set_selection(self, viewpoint):
selected_global_ids = [s.ifc_guid for s in viewpoint.components.selection]
selected_global_ids = [s.ifc_guid for s in viewpoint.visualization_info.components.selection]
bpy.ops.object.select_all(action="DESELECT")
for global_id in selected_global_ids:
obj = IfcStore.get_element(global_id)
@@ -940,7 +992,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
def set_colours(self, viewpoint):
global_id_colours = {}
for coloring in viewpoint.components.coloring:
for coloring in viewpoint.visualization_info.components.coloring:
for component in coloring.components:
global_id_colours.setdefault(component.ifc_guid, coloring.color)
for global_id, color in global_id_colours.items():
@@ -967,9 +1019,9 @@ class ActivateBcfViewpoint(bpy.types.Operator):
frame = layer.frames.new(1)
stroke = frame.strokes.new()
stroke.display_mode = "3DSPACE"
stroke.points.add(len(viewpoint.lines) * 2)
stroke.points.add(len(viewpoint.visualization_info.lines.line) * 2)
coords = []
for l in viewpoint.lines:
for l in viewpoint.visualization_info.lines.line:
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]
)
@@ -977,7 +1029,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
def create_clipping_planes(self, viewpoint):
n = 0
for plane in viewpoint.clipping_planes:
for plane in viewpoint.visualization_info.clipping_planes.clipping_plane:
bpy.ops.bim.add_section_plane()
if n == 0:
obj = bpy.data.objects["Section"]
@@ -1010,10 +1062,14 @@ class ActivateBcfViewpoint(bpy.types.Operator):
collection = bpy.data.collections.get("Bitmaps")
if not collection:
collection = bpy.data.collections.new("Bitmaps")
for bitmap in viewpoint.bitmaps:
for bitmap in viewpoint.visualization_info.bitmap:
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))
# image = bpy.data.images.load(os.path.join(bcfxml.filepath, topic.guid, bitmap.reference))
with tempfile.NamedTemporaryFile(delete=False) as f:
topic.extract_file(bitmap, f.name)
# f.write(bitmap.what)
image = bpy.data.images.load(f.name)
src_width = image.size[0]
src_height = image.size[1]
if src_height > src_width:
@@ -1052,6 +1108,7 @@ class SelectBcfHeaderFile(bpy.types.Operator):
bl_label = "Select BCF Header File"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml;*.ifcjson", options={"HIDDEN"})
def execute(self, context):
if self.filepath:
@@ -1093,3 +1150,25 @@ class SelectBcfDocumentReference(bpy.types.Operator):
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
class ExtractBcfFile(bpy.types.Operator):
bl_idname = "bim.extract_bcf_file"
bl_label = "Extract BCF Header File"
bl_options = {"REGISTER", "UNDO"}
entity_type: bpy.props.StringProperty()
index: bpy.props.IntProperty()
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = bcfxml.topics[context.scene.BCFProperties.active_topic.name]
if self.entity_type == "HEADER_FILE":
entity = topic.header.file[self.index]
elif self.entity_type == "BIM_SNIPPET":
entity = topic.markup.topic.bim_snippet
elif self.entity_type == "DOCUMENT_REFERENCE":
entity = topic.markup.topic.document_reference[self.index]
webbrowser.open(str(topic.extract_file(entity).parent))
return {"FINISHED"}
@@ -95,8 +95,8 @@ def getBcfViewpoints(self, context, force_update=False):
props = context.scene.BCFProperties
bcfxml = bcfstore.BcfStore.get_bcfxml()
topic = props.active_topic
viewpoints = bcfxml.get_viewpoints(topic.name) if topic else {}
bcfviewpoints_enum.extend([(v, f"Viewpoint {i+1}", "") for i, v in enumerate(viewpoints.keys())])
viewpoints = bcfxml.topics[topic.name].viewpoints.keys() if topic else []
bcfviewpoints_enum.extend([(v, f"Viewpoint {i+1}", "") for i, v in enumerate(viewpoints)])
return bcfviewpoints_enum
+12 -9
View File
@@ -127,20 +127,21 @@ class BIM_PT_bcf_metadata(Panel):
layout.label(text="Header Files:")
if bcf_topic.header:
for index, f in enumerate(bcf_topic.header.files):
for index, f in enumerate(bcf_topic.header.file):
box = self.layout.box()
row = box.row(align=True)
row.label(text=f.filename, icon="FILE_BLANK")
if f.is_external:
row.operator("bim.open_uri", icon="URL", text="").uri = f.reference
else:
op = row.operator("bim.open_uri", icon="FILE_FOLDER", text="")
op.uri = os.path.join(bcfxml.filepath, topic.name, f.reference)
op = row.operator("bim.extract_bcf_file", icon="FILE_FOLDER", text="")
op.index = index
op.entity_type = "HEADER_FILE"
row.operator("bim.remove_bcf_file", icon="X", text="").index = index
row = box.row()
row.label(text="Date")
row.label(text=f.date)
row.label(text=str(f.date))
if f.ifc_project:
row = box.row()
@@ -196,8 +197,8 @@ class BIM_PT_bcf_metadata(Panel):
if topic.bim_snippet.is_external:
row.operator("bim.open_uri", icon="URL", text="").uri = topic.bim_snippet.reference
else:
op = row.operator("bim.open_uri", icon="FILE_FOLDER", text="")
op.uri = os.path.join(bcfxml.filepath, topic.name, topic.bim_snippet.reference)
op = row.operator("bim.extract_bcf_file", icon="FILE_FOLDER", text="")
op.entity_type = "BIM_SNIPPET"
row.operator("bim.remove_bcf_bim_snippet", icon="X", text="")
else:
row = layout.row(align=True)
@@ -218,8 +219,10 @@ class BIM_PT_bcf_metadata(Panel):
if doc.is_external:
row.operator("bim.open_uri", icon="URL", text="").uri = doc.reference
else:
op = row.operator("bim.open_uri", icon="FILE_FOLDER", text="")
op.uri = os.path.join(bcfxml.filepath, topic.name, doc.reference)
op = row.operator("bim.extract_bcf_file", icon="FILE_FOLDER", text="")
op.entity_type = "DOCUMENT_REFERENCE"
op.index = index
row.operator("bim.remove_bcf_document_reference", icon="X", text="").index = index
row = box.row(align=True)
row.prop(doc, "description")
@@ -236,7 +239,7 @@ class BIM_PT_bcf_metadata(Panel):
try:
row = layout.row(align=True)
op = row.operator(
"bim.view_bcf_topic", text=f"Select {bcfxml.topics[related_topic.name.lower()].title}"
"bim.view_bcf_topic", text=f"Select {bcfxml.topics[related_topic.name.lower()].topic.title}"
)
op.topic_guid = related_topic.name
row.operator("bim.remove_bcf_related_topic", icon="X", text="").index = index