mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-25 09:32:37 +00:00
bonsai - bcf v3 document references #2790
This commit is contained in:
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import bcf.v3
|
import bcf.v3
|
||||||
|
import bcf.v3.bcfxml
|
||||||
|
import bcf.v3.document
|
||||||
import bcf.v3.model
|
import bcf.v3.model
|
||||||
import bcf.v3.topic
|
import bcf.v3.topic
|
||||||
import bpy
|
import bpy
|
||||||
@@ -714,30 +716,70 @@ class AddBcfDocumentReference(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
assert bcfxml
|
assert bcfxml
|
||||||
|
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
|
||||||
if not (version := (bcfxml.version.version_id or "")).startswith("2"):
|
|
||||||
self.report({"INFO"}, f"BCF {version} is not yet supported: {self.bl_rna.bl_idname}.")
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
props = context.scene.BCFProperties
|
props = context.scene.BCFProperties
|
||||||
blender_topic = props.active_topic
|
blender_topic = props.active_topic
|
||||||
topic = bcfxml.topics[blender_topic.name]
|
topic = bcfxml.topics[blender_topic.name]
|
||||||
|
|
||||||
is_external = "://" in props.document_reference
|
is_external = "://" in props.document_reference
|
||||||
filename = Path(props.document_reference).name if os.path.exists(props.document_reference) else None
|
|
||||||
|
document_path = Path(props.document_reference)
|
||||||
|
document_bytes, filename = None, None
|
||||||
|
if document_path.is_file():
|
||||||
|
filename = document_path.name
|
||||||
|
document_bytes = document_path.read_bytes()
|
||||||
|
|
||||||
if filename:
|
if filename:
|
||||||
with open(props.document_reference, "rb") as f:
|
with open(props.document_reference, "rb") as f:
|
||||||
topic.document_references[filename] = f.read()
|
document_bytes = f.read()
|
||||||
|
|
||||||
document_reference = bcf.v2.model.TopicDocumentReference(
|
document_references = tool.Bcf.get_topic_document_references(topic)
|
||||||
referenced_document=props.document_reference if is_external else filename,
|
if bcf_v2:
|
||||||
description=props.document_reference_description or None,
|
assert isinstance(topic, bcf.v2.topic.TopicHandler)
|
||||||
guid=str(uuid.uuid4()),
|
if document_bytes and filename:
|
||||||
is_external=is_external,
|
topic.document_references[filename] = document_bytes
|
||||||
)
|
assert tool.Bcf.is_list_of(document_references, bcf.v2.model.TopicDocumentReference)
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
document_references.append(document_reference)
|
||||||
|
else:
|
||||||
|
document_guid = None
|
||||||
|
if not is_external:
|
||||||
|
assert filename and document_bytes
|
||||||
|
assert isinstance(bcfxml, bcf.v3.bcfxml.BcfXml)
|
||||||
|
bcf_docs = bcfxml.documents
|
||||||
|
|
||||||
topic.topic.document_reference.append(document_reference)
|
if bcf_docs:
|
||||||
|
doc_definition = bcf_docs.definition
|
||||||
|
else:
|
||||||
|
doc_definition = bcf.v3.model.DocumentInfo()
|
||||||
|
bcf_docs = bcf.v3.document.DocumentsHandler(doc_definition)
|
||||||
|
bcfxml._documents = bcf_docs
|
||||||
|
|
||||||
|
bcf_docs.documents[filename] = document_bytes
|
||||||
|
document_guid = str(uuid.uuid4())
|
||||||
|
document = bcf.v3.model.Document(
|
||||||
|
filename=filename, description=props.document_description, guid=document_guid
|
||||||
|
)
|
||||||
|
doc_definition_docs = doc_definition.documents
|
||||||
|
if not doc_definition_docs:
|
||||||
|
doc_definition.documents = (doc_definition_docs := bcf.v3.model.DocumentInfoDocuments())
|
||||||
|
doc_definition_docs.document.append(document)
|
||||||
|
|
||||||
|
assert tool.Bcf.is_list_of(document_references, bcf.v3.model.DocumentReference)
|
||||||
|
document_reference = bcf.v3.model.DocumentReference(
|
||||||
|
document_guid=document_guid,
|
||||||
|
url=props.document_reference if is_external else None,
|
||||||
|
description=props.document_reference_description or None,
|
||||||
|
guid=str(uuid.uuid4()),
|
||||||
|
)
|
||||||
|
document_references.append(document_reference)
|
||||||
|
tool.Bcf.set_topic_document_references(topic, document_references)
|
||||||
|
|
||||||
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)
|
||||||
props.document_reference = ""
|
props.document_reference = ""
|
||||||
@@ -877,15 +919,59 @@ class RemoveBcfDocumentReference(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
bcfxml = bcfstore.BcfStore.get_bcfxml()
|
||||||
assert bcfxml
|
assert bcfxml
|
||||||
|
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
|
||||||
if not (version := (bcfxml.version.version_id or "")).startswith("2"):
|
|
||||||
self.report({"INFO"}, f"BCF {version} is not yet supported: {self.bl_rna.bl_idname}.")
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
props = context.scene.BCFProperties
|
props = context.scene.BCFProperties
|
||||||
blender_topic = props.active_topic
|
blender_topic = props.active_topic
|
||||||
topic = bcfxml.topics[blender_topic.name]
|
topic = bcfxml.topics[blender_topic.name]
|
||||||
del topic.topic.document_reference[self.index]
|
|
||||||
|
document_references = tool.Bcf.get_topic_document_references(topic)
|
||||||
|
topic_index: int = self.index
|
||||||
|
document_reference = document_references[topic_index]
|
||||||
|
|
||||||
|
# Remove document contents.
|
||||||
|
if bcf_v2:
|
||||||
|
assert isinstance(topic, bcf.v2.topic.TopicHandler)
|
||||||
|
assert isinstance(document_reference, bcf.v2.model.TopicDocumentReference)
|
||||||
|
if not document_reference.is_external:
|
||||||
|
ref_document = document_reference.referenced_document
|
||||||
|
assert ref_document
|
||||||
|
del topic.document_references[ref_document]
|
||||||
|
else:
|
||||||
|
assert isinstance(document_reference, bcf.v3.model.DocumentReference)
|
||||||
|
document_guid = document_reference.document_guid
|
||||||
|
# For bcf v3 documents are stored in bcfxml, not in the topic.
|
||||||
|
if document_guid:
|
||||||
|
assert isinstance(bcfxml, bcf.v3.bcfxml.BcfXml)
|
||||||
|
|
||||||
|
# As there's no bcfxml document manager ui yet,
|
||||||
|
# we remove document if it's not referenced by any topic.
|
||||||
|
present_in_other_topics = False
|
||||||
|
for topic_ in bcfxml.topics.values():
|
||||||
|
if topic_ == topic:
|
||||||
|
continue
|
||||||
|
document_references = tool.Bcf.get_topic_document_references(topic_)
|
||||||
|
assert tool.Bcf.is_list_of(document_references, bcf.v3.model.DocumentReference)
|
||||||
|
for ref_ in document_references:
|
||||||
|
if ref_.document_guid == document_guid:
|
||||||
|
present_in_other_topics = True
|
||||||
|
break
|
||||||
|
if present_in_other_topics:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not present_in_other_topics:
|
||||||
|
doc_handler = bcfxml.documents
|
||||||
|
assert doc_handler
|
||||||
|
docs = doc_handler.definition.documents
|
||||||
|
assert docs
|
||||||
|
doc = next(d for d in docs.document if d.guid == document_guid)
|
||||||
|
del doc_handler.documents[doc.filename]
|
||||||
|
docs.document.remove(doc)
|
||||||
|
|
||||||
|
# Remove document reference.
|
||||||
|
del document_references[topic_index]
|
||||||
|
tool.Bcf.set_topic_document_references(topic, document_references)
|
||||||
|
|
||||||
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)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|||||||
@@ -210,6 +210,7 @@ class BCFProperties(PropertyGroup):
|
|||||||
bim_snippet_schema: StringProperty(default="", name="Schema")
|
bim_snippet_schema: StringProperty(default="", name="Schema")
|
||||||
document_reference: StringProperty(default="", name="Referenced Document")
|
document_reference: StringProperty(default="", name="Referenced Document")
|
||||||
document_reference_description: StringProperty(default="", name="Description")
|
document_reference_description: StringProperty(default="", name="Description")
|
||||||
|
document_description: StringProperty(default="", name="Document Description")
|
||||||
related_topic: StringProperty(name="Related Topic")
|
related_topic: StringProperty(name="Related Topic")
|
||||||
comment: StringProperty(default="", name="Comment")
|
comment: StringProperty(default="", name="Comment")
|
||||||
has_related_viewpoint: BoolProperty(name="Has Related Viewpoint", default=False)
|
has_related_viewpoint: BoolProperty(name="Has Related Viewpoint", default=False)
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ class BIM_PT_bcf_metadata(Panel):
|
|||||||
bl_parent_id = "BIM_PT_bcf"
|
bl_parent_id = "BIM_PT_bcf"
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
|
# TODO: in bcf v3 documents can be added to the bcf
|
||||||
|
# without adding them to specific topic.
|
||||||
|
# Currently we just handle it the same way as in v2
|
||||||
|
# documents are accessed and managed from topics.
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.use_property_split = True
|
layout.use_property_split = True
|
||||||
layout.use_property_decorate = False
|
layout.use_property_decorate = False
|
||||||
@@ -127,6 +131,8 @@ class BIM_PT_bcf_metadata(Panel):
|
|||||||
if not bcfxml or props.active_topic_index >= len(props.topics):
|
if not bcfxml or props.active_topic_index >= len(props.topics):
|
||||||
layout.label(text="No BCF project is loaded")
|
layout.label(text="No BCF project is loaded")
|
||||||
return
|
return
|
||||||
|
bcf_verison = bcfxml.version.version_id or ""
|
||||||
|
bcf_v3 = bcf_verison.startswith("3")
|
||||||
|
|
||||||
topic = props.active_topic
|
topic = props.active_topic
|
||||||
bcf_topic = bcfxml.topics[topic.name]
|
bcf_topic = bcfxml.topics[topic.name]
|
||||||
@@ -239,7 +245,12 @@ class BIM_PT_bcf_metadata(Panel):
|
|||||||
row.prop(props, "document_reference")
|
row.prop(props, "document_reference")
|
||||||
row.operator("bim.select_bcf_document_reference", icon="FILE_FOLDER", text="")
|
row.operator("bim.select_bcf_document_reference", icon="FILE_FOLDER", text="")
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(props, "document_reference_description")
|
row.prop(
|
||||||
|
props, "document_reference_description", text="Reference Description" if bcf_v3 else "Document Description"
|
||||||
|
)
|
||||||
|
if bcf_v3:
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(props, "document_description")
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.operator("bim.add_bcf_document_reference")
|
row.operator("bim.add_bcf_document_reference")
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ import bcf.v2.topic
|
|||||||
import bcf.v3.model
|
import bcf.v3.model
|
||||||
import bcf.v3.topic
|
import bcf.v3.topic
|
||||||
import bcf.agnostic.topic
|
import bcf.agnostic.topic
|
||||||
from typing import Any, Union, Optional
|
from typing import Any, Union, TypeVar, TypeGuard
|
||||||
|
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
class Bcf(bonsai.core.tool.Bcf):
|
class Bcf(bonsai.core.tool.Bcf):
|
||||||
@@ -34,6 +37,13 @@ class Bcf(bonsai.core.tool.Bcf):
|
|||||||
def get_path(cls) -> str:
|
def get_path(cls) -> str:
|
||||||
return bpy.context.scene.BCFProperties.bcf_file
|
return bpy.context.scene.BCFProperties.bcf_file
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_list_of(cls, a: list[Any], t: type[T]) -> TypeGuard[list[T]]:
|
||||||
|
"""Narrow type for a list assuming list is not mixing types."""
|
||||||
|
if not a:
|
||||||
|
return True
|
||||||
|
return isinstance(a[0], t)
|
||||||
|
|
||||||
# BCF version agnostic API.
|
# BCF version agnostic API.
|
||||||
## topic
|
## topic
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -47,6 +57,26 @@ class Bcf(bonsai.core.tool.Bcf):
|
|||||||
document_references = document_references.document_reference if document_references else []
|
document_references = document_references.document_reference if document_references else []
|
||||||
return document_references
|
return document_references
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_topic_document_references(
|
||||||
|
cls,
|
||||||
|
topic: bcf.agnostic.topic.TopicHandler,
|
||||||
|
document_references: Union[list[bcf.v2.model.TopicDocumentReference], list[bcf.v3.model.DocumentReference]],
|
||||||
|
) -> None:
|
||||||
|
topic_ = topic.topic
|
||||||
|
if isinstance(topic_, bcf.v2.model.Topic):
|
||||||
|
assert cls.is_list_of(document_references, bcf.v2.model.TopicDocumentReference)
|
||||||
|
topic_.document_reference = document_references
|
||||||
|
else:
|
||||||
|
assert cls.is_list_of(document_references, bcf.v3.model.DocumentReference)
|
||||||
|
topic_document_references = topic_.document_references
|
||||||
|
if topic_document_references is None:
|
||||||
|
if not document_references:
|
||||||
|
return
|
||||||
|
topic_.document_references = (topic_document_references := bcf.v3.model.TopicDocumentReferences())
|
||||||
|
return
|
||||||
|
topic_document_references.document_reference = document_references
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_topic_header_files(
|
def get_topic_header_files(
|
||||||
cls, topic: bcf.agnostic.topic.TopicHandler
|
cls, topic: bcf.agnostic.topic.TopicHandler
|
||||||
|
|||||||
Reference in New Issue
Block a user