mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 02:47:48 +00:00
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:
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user