This commit is contained in:
Andrej730
2025-02-17 12:33:15 +05:00
parent 8f261be338
commit b0fbea60ae
32 changed files with 381 additions and 185 deletions
+4 -2
View File
@@ -21,6 +21,7 @@ import bpy
import bcf
import bcf.bcfxml
import bcf.v2.bcfxml
import bonsai.tool as tool
from typing import Union
@@ -30,7 +31,8 @@ class BcfStore:
@classmethod
def get_bcfxml(cls) -> Union[bcf.bcfxml.BcfXml, None]:
if not cls.bcfxml:
bcf_filepath = bpy.context.scene.BCFProperties.bcf_file
props = tool.Bcf.get_bcf_props()
bcf_filepath = props.bcf_file
if not os.path.isabs(bcf_filepath):
bcf_filepath = os.path.abspath(os.path.join(bpy.path.abspath("//"), bcf_filepath))
if bcf_filepath:
@@ -46,7 +48,7 @@ class BcfStore:
@classmethod
def set(cls, bcfxml: Union[bcf.bcfxml.BcfXml, None], filepath: str) -> None:
cls.bcfxml = bcfxml
props = bpy.context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
props.bcf_file = filepath
# Set bcf_version prop on load.
+70 -51
View File
@@ -55,7 +55,7 @@ class NewBcfProject(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
bcf_v2 = props.bcf_version == "2"
bcf_class = bcf.v2.bcfxml.BcfXml if bcf_v2 else bcf.v3.bcfxml.BcfXml
bcfxml = bcf_class.create_new("New Project")
@@ -103,7 +103,8 @@ class LoadBcfProject(bpy.types.Operator):
assert bcfxml.project
if bcfxml.project.name is None:
bcfxml.project.name = nameless
context.scene.BCFProperties.name = bcfxml.project.name
props = tool.Bcf.get_bcf_props()
props.name = bcfxml.project.name
bpy.ops.bim.load_bcf_topics()
self.report({"INFO"}, f"BCF Project '{Path(self.filepath).name}' is loaded.")
return {"FINISHED"}
@@ -131,7 +132,7 @@ class LoadBcfTopics(bpy.types.Operator):
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
props.topics.clear()
# workaround, one non standard topic would break reading entire bcf
# ignored these topics ATM
@@ -163,7 +164,8 @@ class LoadBcfTopic(bpy.types.Operator):
assert bcfxml
topic = bcfxml.topics[self.topic_guid]
bcfxml.get_header(self.topic_guid)
new = context.scene.BCFProperties.topics[self.topic_index]
props = tool.Bcf.get_bcf_props()
new = props.topics[self.topic_index]
data_map = {
"name": topic.guid,
"title": topic.topic.title,
@@ -244,7 +246,8 @@ class LoadBcfComments(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
blender_topic = context.scene.BCFProperties.topics.get(self.topic_guid)
props = tool.Bcf.get_bcf_props()
blender_topic = props.topics.get(self.topic_guid)
blender_topic.comments.clear()
for comment in bcfxml.topics[self.topic_guid].comments:
new = blender_topic.comments.add()
@@ -274,7 +277,9 @@ class EditBcfProjectName(bpy.types.Operator):
# Bonsai creates default project on load.
assert bcfxml.project
bcfxml.project.name = context.scene.BCFProperties.name
props = tool.Bcf.get_bcf_props()
bcfxml.project.name = props.name
return {"FINISHED"}
@@ -284,7 +289,7 @@ class EditBcfTopicName(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
@@ -300,7 +305,7 @@ class EditBcfTopic(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
@@ -371,13 +376,15 @@ class AddBcfTopic(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.author
props = tool.Bcf.get_bcf_props()
return props.author
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
bcfxml.add_topic("New Topic", "", context.scene.BCFProperties.author)
props = tool.Bcf.get_bcf_props()
bcfxml.add_topic("New Topic", "", props.author)
bpy.ops.bim.load_bcf_topics()
return {"FINISHED"}
@@ -389,7 +396,7 @@ class AddBcfBimSnippet(bpy.types.Operator):
@classmethod
def poll(cls, context):
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
props_are_filled = all(
(getattr(props, attr) for attr in ("bim_snippet_reference", "bim_snippet_schema", "bim_snippet_type"))
)
@@ -403,7 +410,7 @@ class AddBcfBimSnippet(bpy.types.Operator):
assert bcfxml
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
is_external = "://" in props.bim_snippet_reference
@@ -435,7 +442,7 @@ class AddBcfRelatedTopic(bpy.types.Operator):
assert bcfxml
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
related_topics = tool.Bcf.get_topic_related_topics(topic)
@@ -462,14 +469,15 @@ class AddBcfHeaderFile(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.file_reference
props = tool.Bcf.get_bcf_props()
return props.file_reference
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
@@ -519,9 +527,10 @@ class ViewBcfTopic(bpy.types.Operator):
topic_guid: bpy.props.StringProperty()
def execute(self, context):
for index, topic in enumerate(context.scene.BCFProperties.topics):
props = tool.Bcf.get_bcf_props()
for index, topic in enumerate(props.topics):
if topic.name.lower() == self.topic_guid.lower():
context.scene.BCFProperties.active_topic_index = index
props.active_topic_index = index
break
return {"FINISHED"}
@@ -546,7 +555,7 @@ class AddBcfViewpoint(bpy.types.Operator):
blender_camera = context.scene.camera
assert blender_camera
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
@@ -675,7 +684,7 @@ class RemoveBcfViewpoint(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
if not bcfxml:
return False
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
topic = props.active_topic
if not topic:
return False
@@ -690,7 +699,7 @@ class RemoveBcfViewpoint(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
del topic.viewpoints[blender_topic.viewpoints]
@@ -715,7 +724,7 @@ class RemoveBcfFile(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
header_files = tool.Bcf.get_topic_header_files(topic)
@@ -733,13 +742,14 @@ class RemoveBcfTopic(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.topics
props = tool.Bcf.get_bcf_props()
return props.topics
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
del bcfxml.topics[props.active_topic.name]
bpy.ops.bim.load_bcf_topics()
return {"FINISHED"}
@@ -752,13 +762,14 @@ class AddBcfReferenceLink(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.reference_link
props = tool.Bcf.get_bcf_props()
return bool(props.reference_link)
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
reference_links = tool.Bcf.get_topic_reference_links(topic)
@@ -776,14 +787,15 @@ class AddBcfDocumentReference(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.document_reference
props = tool.Bcf.get_bcf_props()
return bool(props.document_reference)
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
@@ -855,13 +867,14 @@ class AddBcfLabel(bpy.types.Operator):
@classmethod
def poll(cls, context):
return context.scene.BCFProperties.label
props = tool.Bcf.get_bcf_props()
return bool(props.label)
def execute(self, context):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
new = blender_topic.labels.add()
@@ -883,7 +896,7 @@ class EditBcfReferenceLinks(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
reference_links = [r.name for r in blender_topic.reference_links]
@@ -900,7 +913,7 @@ class EditBcfLabels(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
labels = [l.name for l in blender_topic.labels]
@@ -918,7 +931,7 @@ class RemoveBcfReferenceLink(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
reference_links = tool.Bcf.get_topic_reference_links(topic)
@@ -938,7 +951,7 @@ class RemoveBcfLabel(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
labels = tool.Bcf.get_topic_labels(topic)
@@ -957,7 +970,7 @@ class RemoveBcfBimSnippet(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
tool.Bcf.set_topic_bim_snippet(topic, None)
@@ -978,7 +991,7 @@ class RemoveBcfDocumentReference(bpy.types.Operator):
assert bcfxml
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
@@ -1043,7 +1056,7 @@ class RemoveBcfRelatedTopic(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
related_topics = tool.Bcf.get_topic_related_topics(topic)
@@ -1063,7 +1076,7 @@ class RemoveBcfComment(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
comments = topic.comments
@@ -1084,7 +1097,7 @@ class EditBcfComment(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
blender_comment = blender_topic.comments.get(self.comment_guid)
topic = bcfxml.topics[blender_topic.name]
@@ -1092,7 +1105,7 @@ class EditBcfComment(bpy.types.Operator):
if comment.guid == self.comment_guid:
comment.comment = blender_comment.comment
comment.modified_date = XmlDateTime.now()
comment.modified_author = context.scene.BCFProperties.author
comment.modified_author = props.author
bpy.ops.bim.load_bcf_comments(topic_guid=topic.guid)
return {"FINISHED"}
@@ -1105,7 +1118,7 @@ class AddBcfComment(bpy.types.Operator):
@classmethod
def poll(cls, context):
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
if not props.comment:
cls.poll_message_set("No comment to add.")
return False
@@ -1128,7 +1141,7 @@ class AddBcfComment(bpy.types.Operator):
assert bcfxml
bcf_v2 = (bcfxml.version.version_id or "").startswith("2")
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
comments = topic.comments
@@ -1136,7 +1149,7 @@ class AddBcfComment(bpy.types.Operator):
if bcf_v2:
comment = bcf.v2.model.Comment(
date=XmlDateTime.now(),
author=context.scene.BCFProperties.author,
author=props.author,
comment=props.comment,
guid=str(uuid.uuid4()),
)
@@ -1149,7 +1162,7 @@ class AddBcfComment(bpy.types.Operator):
else:
comment = bcf.v3.model.Comment(
date=XmlDateTime.now(),
author=context.scene.BCFProperties.author,
author=props.author,
comment=props.comment,
guid=str(uuid.uuid4()),
)
@@ -1179,7 +1192,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
@classmethod
def poll(cls, context):
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
if blender_topic is None:
cls.poll_message_set("No topic is active.")
@@ -1197,7 +1210,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
topic = bcfxml.topics[blender_topic.name]
if self.viewpoint_guid:
@@ -1517,7 +1530,8 @@ class OpenBcfReferenceLink(bpy.types.Operator):
index: bpy.props.IntProperty()
def execute(self, context):
webbrowser.open(context.scene.BCFProperties.topic_links[self.index].name)
props = tool.Bcf.get_bcf_props()
webbrowser.open(props.topic_links[self.index].name)
return {"FINISHED"}
@@ -1530,7 +1544,8 @@ class SelectBcfHeaderFile(bpy.types.Operator):
def execute(self, context):
if self.filepath:
context.scene.BCFProperties.file_reference = self.filepath
props = tool.Bcf.get_bcf_props()
props.file_reference = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
@@ -1546,7 +1561,8 @@ class SelectBcfBimSnippetReference(bpy.types.Operator):
def execute(self, context):
if self.filepath:
context.scene.BCFProperties.bim_snippet_reference = self.filepath
props = tool.Bcf.get_bcf_props()
props.bim_snippet_reference = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
@@ -1562,7 +1578,8 @@ class SelectBcfDocumentReference(bpy.types.Operator):
def execute(self, context):
if self.filepath:
context.scene.BCFProperties.document_reference = self.filepath
props = tool.Bcf.get_bcf_props()
props.document_reference = self.filepath
return {"FINISHED"}
def invoke(self, context, event):
@@ -1585,7 +1602,8 @@ class LoadBcfHeaderIfcFile(bpy.types.Operator):
assert bcfxml
bcf_path = tool.Bcf.get_path()
topic = bcfxml.topics[context.scene.BCFProperties.active_topic.name]
props = tool.Bcf.get_bcf_props()
topic = bcfxml.topics[props.active_topic.name]
entity = tool.Bcf.get_topic_header_files(topic)[self.index]
ifc_path = bcf.agnostic.topic.extract_file(topic, entity)
bpy.ops.bim.load_project(filepath=ifc_path)
@@ -1605,7 +1623,8 @@ class ExtractBcfFile(bpy.types.Operator):
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
topic = bcfxml.topics[context.scene.BCFProperties.active_topic.name]
props = tool.Bcf.get_bcf_props()
topic = bcfxml.topics[props.active_topic.name]
if self.entity_type == "HEADER_FILE":
entity = tool.Bcf.get_topic_header_files(topic)[self.index]
+75 -4
View File
@@ -17,6 +17,7 @@
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import bonsai.tool as tool
from . import bcfstore
from bonsai.bim.prop import StrProperty
from bpy.types import PropertyGroup
@@ -34,6 +35,7 @@ from functools import partial
from typing import Literal
from typing_extensions import assert_never
from bcf.agnostic.extensions import get_extensions_attributes
from typing import TYPE_CHECKING, Union
bcfviewpoints_enum = None
@@ -95,7 +97,7 @@ def getBcfViewpoints(self, context, force_update=False):
global bcfviewpoints_enum
if bcfviewpoints_enum is None or force_update: # Retrieving Viewpoints is slow. Make sure we only do when needed
bcfviewpoints_enum = []
props = context.scene.BCFProperties
props = tool.Bcf.get_bcf_props()
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
topic = props.active_topic
@@ -110,6 +112,12 @@ class BcfBimSnippet(PropertyGroup):
type: StringProperty(name="Type")
is_external: BoolProperty(name="Is External")
if TYPE_CHECKING:
schema: str
reference: str
type: str
is_external: bool
class BcfDocumentReference(PropertyGroup):
reference: StringProperty(name="Reference")
@@ -117,6 +125,12 @@ class BcfDocumentReference(PropertyGroup):
guid: StringProperty(name="GUID")
is_external: BoolProperty(name="Is External")
if TYPE_CHECKING:
reference: str
description: str
guid: str
is_external: bool
class BcfComment(PropertyGroup):
name: StringProperty(name="GUID")
@@ -128,6 +142,16 @@ class BcfComment(PropertyGroup):
modified_author: StringProperty(name="Modified Author")
is_editable: BoolProperty(name="Is Editable", default=False, update=updateBcfCommentIsEditable)
if TYPE_CHECKING:
name: str
date: str
author: str
comment: str
viewpoint: str
modified_date: str
modified_author: str
is_editable: bool
def get_extensions_items(
self: "BCFProperties", context: bpy.types.Context, edit_text: str, extensions_attr: str
@@ -183,6 +207,30 @@ class BcfTopic(PropertyGroup):
comments: CollectionProperty(name="Comments", type=BcfComment)
is_editable: BoolProperty(name="Edit Topic Attributes", default=False, update=updateBcfTopicIsEditable)
if TYPE_CHECKING:
name: str
title: str
type: str
status: str
priority: str
stage: str
creation_date: str
creation_author: str
modified_date: str
modified_author: str
assigned_to: str
due_date: str
description: str
viewpoints: str
files: bpy.types.bpy_prop_collection_idprop[StrProperty]
reference_links: bpy.types.bpy_prop_collection_idprop[BcfReferenceLink]
labels: bpy.types.bpy_prop_collection_idprop[BcfLabel]
bim_snippet: BcfBimSnippet
document_references: bpy.types.bpy_prop_collection_idprop[BcfDocumentReference]
related_topics: bpy.types.bpy_prop_collection_idprop[StrProperty]
comments: bpy.types.bpy_prop_collection_idprop[BcfComment]
is_editable: bool
def get_related_topics(self: "BCFProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
global RELATED_TOPICS_ENUM_ITEMS
@@ -235,7 +283,30 @@ class BCFProperties(PropertyGroup):
comment: StringProperty(default="", name="Comment")
has_related_viewpoint: BoolProperty(name="Has Related Viewpoint", default=False)
def clear_input_fields(self):
if TYPE_CHECKING:
bcf_file: str
bcf_version: str
comment_text_width: int
name: str
author: str
topics: bpy.types.bpy_prop_collection_idprop[BcfTopic]
active_topic_index: int
file_reference: str
file_ifc_project: str
file_ifc_spatial_structure_element: str
reference_link: str
label: str
bim_snippet_reference: str
bim_snippet_type: str
bim_snippet_schema: str
document_reference: str
document_reference_description: str
document_description: str
related_topic: str
comment: str
has_related_viewpoint: bool
def clear_input_fields(self) -> None:
self.file_reference = ""
self.file_ifc_project = ""
self.file_ifc_spatial_structure_element = ""
@@ -250,7 +321,7 @@ class BCFProperties(PropertyGroup):
self.has_related_viewpoint = False
@property
def active_topic(self):
def active_topic(self) -> Union[BcfTopic, None]:
if len(self.topics) == 0:
return None
if self.active_topic_index < 0:
@@ -259,5 +330,5 @@ class BCFProperties(PropertyGroup):
self.active_topic_index = len(self.topics) - 1
return self.topics[self.active_topic_index]
def refresh_topic(self, context):
def refresh_topic(self, context: bpy.types.Context) -> None:
refreshBcfTopic(self, context)
+6 -10
View File
@@ -37,8 +37,7 @@ class BIM_PT_bcf(Panel):
layout.use_property_split = True
layout.use_property_decorate = False
scene = context.scene
props = scene.BCFProperties
props = tool.Bcf.get_bcf_props()
if not bcfstore.BcfStore.get_bcfxml():
row = layout.row(align=True)
@@ -63,18 +62,17 @@ class BIM_PT_bcf(Panel):
row = layout.row()
row.prop(props, "author")
props = context.scene.BCFProperties
row = layout.row()
row.template_list("BIM_UL_topics", "", props, "topics", props, "active_topic_index")
col = row.column(align=True)
col.operator("bim.add_bcf_topic", icon="ADD", text="")
col.operator("bim.remove_bcf_topic", icon="REMOVE", text="")
if props.active_topic_index < len(props.topics):
topic = props.active_topic
topic = props.active_topic
if topic is not None:
is_editable = topic.is_editable
col.prop(topic, "is_editable", icon="CHECKMARK" if topic.is_editable else "GREASEPENCIL", icon_only=True)
topic = props.active_topic
row = layout.row()
row.enabled = is_editable
row.prop(topic, "description", text="")
@@ -131,8 +129,7 @@ class BIM_PT_bcf_metadata(Panel):
layout.use_property_split = True
layout.use_property_decorate = False
scene = context.scene
props = scene.BCFProperties
props = tool.Bcf.get_bcf_props()
bcfxml = bcfstore.BcfStore.get_bcfxml()
if not bcfxml or props.active_topic_index >= len(props.topics):
@@ -296,8 +293,7 @@ class BIM_PT_bcf_comments(Panel):
layout.use_property_split = True
layout.use_property_decorate = False
scene = context.scene
props = scene.BCFProperties
props = tool.Bcf.get_bcf_props()
if props.active_topic_index >= len(props.topics):
layout.label(text="No BCF project is loaded")
@@ -118,7 +118,7 @@ class MaterialClassificationsData(ReferencesData):
def references(cls):
results = []
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
if props.materials and props.active_material_index < len(props.materials):
material = props.materials[props.active_material_index]
if material.ifc_definition_id:
@@ -312,7 +312,7 @@ class BIM_PT_material_classifications(Panel, ReferenceUI):
def poll(cls, context):
if not tool.Ifc.get():
return False
props = context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
if props.is_editing and (material := props.active_material) and material.ifc_definition_id:
return True
return False
@@ -689,13 +689,15 @@ class PurgeUnusedObjects(bpy.types.Operator, tool.Ifc.Operator):
scene = context.scene
if object_type == "PROFILE":
if scene.BIMProfileProperties.is_editing:
props = tool.Profile.get_profile_props()
if props.is_editing:
bpy.ops.bim.load_profiles()
elif object_type == "STYLE":
if scene.BIMStylesProperties.is_editing:
bpy.ops.bim.load_styles()
elif object_type == "MATERIAL":
if scene.BIMMaterialProperties.is_editing:
props = tool.Material.get_material_props()
if props.is_editing:
bpy.ops.bim.load_materials()
@@ -737,13 +739,15 @@ class MergeIdenticalObjects(bpy.types.Operator, tool.Ifc.Operator):
scene = context.scene
if object_type == "PROFILE":
if scene.BIMProfileProperties.is_editing:
props = tool.Profile.get_profile_props()
if props.is_editing:
bpy.ops.bim.load_profiles()
elif object_type == "STYLE":
if scene.BIMStylesProperties.is_editing:
bpy.ops.bim.load_styles()
elif object_type == "MATERIAL":
if scene.BIMMaterialProperties.is_editing:
props = tool.Material.get_material_props()
if props.is_editing:
bpy.ops.bim.load_materials()
@@ -2136,7 +2136,8 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
continue
if tool.Profile.is_editing_profile():
profile_id = context.scene.BIMProfileProperties.active_profile_id
props = tool.Profile.get_profile_props()
profile_id = props.active_profile_id
if profile_id:
profile = tool.Ifc.get().by_id(profile_id)
if tool.Ifc.get_object(profile): # We are editing an arbitrary profile
@@ -2148,7 +2149,7 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
elif tool.Model.get_usage_type(element) == "PROFILE":
bpy.ops.bim.edit_extrusion_axis()
# if in the process of editing arbitrary profile
elif context.scene.BIMProfileProperties.active_arbitrary_profile_id:
elif props.active_arbitrary_profile_id:
bpy.ops.bim.edit_arbitrary_profile()
else:
bpy.ops.bim.edit_extrusion_profile()
+3 -2
View File
@@ -47,7 +47,8 @@ class LibrariesData:
@classmethod
def library_attributes(cls):
library_id = bpy.context.scene.BIMLibraryProperties.active_library_id
props = tool.Library.get_library_props()
library_id = props.active_library_id
if not library_id:
return []
results = []
@@ -63,7 +64,7 @@ class LibrariesData:
@classmethod
def reference_attributes(cls):
props = bpy.context.scene.BIMLibraryProperties
props = tool.Library.get_library_props()
try:
reference_id = props.references[props.active_reference_index].ifc_definition_id
except:
@@ -30,6 +30,7 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
from typing import TYPE_CHECKING, Literal
def update_active_reference_index(self, context):
@@ -40,6 +41,10 @@ class LibraryReference(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
if TYPE_CHECKING:
name: str
ifc_definition_id: int
class BIMLibraryProperties(PropertyGroup):
editing_mode: EnumProperty(
@@ -58,3 +63,12 @@ class BIMLibraryProperties(PropertyGroup):
active_reference_id: IntProperty(name="Active Reference Id")
references: CollectionProperty(type=LibraryReference, name="References")
active_reference_index: IntProperty(name="Active Reference Index", update=update_active_reference_index)
if TYPE_CHECKING:
editing_mode: Literal["NONE", "LIBRARY", "REFERENCES", "REFERENCE"]
library_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
active_library_id: int
reference_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
active_reference_id: int
references: bpy.types.bpy_prop_collection_idprop[LibraryReference]
active_reference_index: int
+14 -4
View File
@@ -16,10 +16,16 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy
import bonsai.bim.helper
import bonsai.tool as tool
from bpy.types import Panel, UIList
from bonsai.bim.module.library.data import LibrariesData, LibraryReferencesData
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.library.prop import BIMLibraryProperties, LibraryReference
class BIM_PT_libraries(Panel):
@@ -38,7 +44,7 @@ class BIM_PT_libraries(Panel):
def draw(self, context):
if not LibrariesData.is_loaded:
LibrariesData.load()
self.props = context.scene.BIMLibraryProperties
self.props = tool.Library.get_library_props()
if self.props.editing_mode == "LIBRARY":
self.draw_editable_library_ui()
@@ -110,7 +116,7 @@ class BIM_PT_library_references(Panel):
def draw(self, context):
if not LibraryReferencesData.is_loaded:
LibraryReferencesData.load()
self.props = context.scene.BIMLibraryProperties
self.props = tool.Library.get_library_props()
if self.props.editing_mode == "REFERENCES":
self.layout.template_list(
@@ -129,7 +135,9 @@ class BIM_PT_library_references(Panel):
class BIM_UL_library_references(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self, context, layout: bpy.types.UILayout, data, item: LibraryReference, icon, active_data, active_propname
):
if item:
row = layout.row(align=True)
row.label(text=item.name)
@@ -140,7 +148,9 @@ class BIM_UL_library_references(UIList):
class BIM_UL_object_library_references(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self, context, layout: bpy.types.UILayout, data, item: LibraryReference, icon, active_data, active_propname
):
if item:
row = layout.row(align=True)
row.label(text=item.name)
@@ -48,7 +48,8 @@ class MaterialsData:
@classmethod
def total_materials(cls):
return len(tool.Ifc.get().by_type(bpy.context.scene.BIMMaterialProperties.material_type))
props = tool.Material.get_material_props()
return len(tool.Ifc.get().by_type(props.material_type))
@classmethod
def material_types(cls):
@@ -101,7 +102,7 @@ class MaterialsData:
@classmethod
def material_styles_data(cls) -> dict[int, list[dict[str, Any]]]:
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
material_styles_data: dict[int, list[dict[str, Any]]] = {}
for material_item in props.materials:
@@ -19,6 +19,7 @@
import bpy
import json
import ifcopenshell.api
import ifcopenshell.api.material
import ifcopenshell.util.element
import ifcopenshell.util.attribute
import ifcopenshell.util.representation
@@ -40,7 +41,8 @@ class LoadMaterials(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
core.load_materials(tool.Material, context.scene.BIMMaterialProperties.material_type)
props = tool.Material.get_material_props()
core.load_materials(tool.Material, props.material_type)
return {"FINISHED"}
@@ -327,12 +329,13 @@ class AddProfile(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file()
props = tool.Material.get_material_props()
ifcopenshell.api.run(
"material.add_profile",
self.file,
profile_set=self.file.by_id(self.profile_set),
material=self.file.by_id(int(obj.BIMObjectMaterialProperties.material)),
profile=self.file.by_id(int(context.scene.BIMMaterialProperties.profiles)),
profile=self.file.by_id(int(props.profiles)),
)
@@ -662,7 +665,7 @@ class EnableEditingMaterialSetItem(bpy.types.Operator):
def execute(self, context):
self.file = IfcStore.get_file()
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.mprops = context.scene.BIMMaterialProperties
self.mprops = tool.Material.get_material_props()
self.props = obj.BIMObjectMaterialProperties
self.props.active_material_set_item_id = self.material_set_item
@@ -706,7 +709,7 @@ class EditMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
self.file = IfcStore.get_file()
props = obj.BIMObjectMaterialProperties
mprops = context.scene.BIMMaterialProperties
mprops = tool.Material.get_material_props()
element = tool.Ifc.get_entity(obj)
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
@@ -763,7 +766,7 @@ class ExpandMaterialCategory(bpy.types.Operator):
return self.execute(context)
def execute(self, context):
props = context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
for index, category in (
(i, c)
for i, c in enumerate(props.materials)
@@ -792,7 +795,7 @@ class ContractMaterialCategory(bpy.types.Operator):
return self.execute(context)
def execute(self, context):
props = context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
for index, category in (
(i, c)
for i, c in enumerate(props.materials)
@@ -812,7 +815,7 @@ class EnableEditingMaterialStyle(bpy.types.Operator):
material: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
props.active_material_id = self.material
props.editing_material_type = "STYLE"
@@ -843,7 +846,7 @@ class EditMaterialStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
ifc_file = tool.Ifc.get()
material = ifc_file.by_id(props.active_material_id)
style = ifc_file.by_id(int(props.styles))
@@ -867,7 +870,7 @@ class UnassignMaterialStyle(bpy.types.Operator, tool.Ifc.Operator):
context: bpy.props.IntProperty()
def _execute(self, context):
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
material = tool.Ifc.get().by_id(props.materials[props.active_material_index].ifc_definition_id)
style = tool.Ifc.get().by_id(self.style)
context = tool.Ifc.get().by_id(self.context)
@@ -888,7 +891,7 @@ class SelectMaterialInMaterialsUI(bpy.types.Operator):
material_id: int
def execute(self, context):
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
ifc_file = tool.Ifc.get()
material_id = self.material_id
material = ifc_file.by_id(material_id)
@@ -121,7 +121,7 @@ def set_material_name(self: "Material", new_category_name: str) -> None:
material.Category = new_category_name
# Reload UI elements if necessary.
props: "BIMMaterialProperties" = self.id_data.BIMMaterialProperties
props = tool.Material.get_material_props()
new_category_name_already_in_use = bool(
next((m for m in props.materials if m.is_category and m.name == new_category_name), None)
)
+12 -6
View File
@@ -29,7 +29,7 @@ from bonsai.bim.module.drawing.helper import format_distance
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.material.prop import Material
from bonsai.bim.module.material.prop import Material, BIMMaterialProperties
class BIM_PT_materials(Panel):
@@ -49,7 +49,7 @@ class BIM_PT_materials(Panel):
if not MaterialsData.is_loaded:
MaterialsData.load()
self.props = context.scene.BIMMaterialProperties
self.props = tool.Material.get_material_props()
material = tool.Material.get_active_material_item()
material_id = material.ifc_definition_id if material else None
@@ -156,7 +156,7 @@ class BIM_PT_object_material(Panel):
self.file = IfcStore.get_file()
self.oprops = context.active_object.BIMObjectProperties
self.props = context.active_object.BIMObjectMaterialProperties
self.mprops = context.scene.BIMMaterialProperties
self.mprops = tool.Material.get_material_props()
if not ObjectMaterialData.data["materials"]:
row = self.layout.row(align=True)
@@ -401,10 +401,16 @@ class BIM_PT_object_material(Panel):
class BIM_UL_materials(UIList):
def draw_item(
self, context, layout: bpy.types.UILayout, data, item: Material, icon, active_data, active_propname
self,
context,
layout: bpy.types.UILayout,
data: BIMMaterialProperties,
item: Material,
icon,
active_data,
active_propname,
) -> None:
mprops = context.scene.BIMMaterialProperties
material_type = mprops.material_type
material_type = data.material_type
if item:
row = layout.row(align=True)
+2 -2
View File
@@ -51,7 +51,7 @@ class ProfileData:
@classmethod
def active_profile_users(cls):
profiles_props = bpy.context.scene.BIMProfileProperties
profiles_props = tool.Profile.get_profile_props()
if profiles_props.active_profile_index >= len(profiles_props.profiles):
return 0
profile_prop = profiles_props.profiles[profiles_props.active_profile_index]
@@ -83,7 +83,7 @@ class ProfileData:
@classmethod
def is_arbitrary_profile(cls):
props = bpy.context.scene.BIMProfileProperties
props = tool.Profile.get_profile_props()
if props.active_profile_id:
profile = tool.Ifc.get().by_id(props.active_profile_id)
if profile.is_a("IfcArbitraryClosedProfileDef"):
@@ -35,7 +35,7 @@ class LoadProfiles(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMProfileProperties
props = tool.Profile.get_profile_props()
props.profiles.clear()
filter_material_profiles = props.is_filtering_material_profiles
@@ -64,7 +64,8 @@ class DisableProfileEditingUI(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMProfileProperties.is_editing = False
props = tool.Profile.get_profile_props()
props.is_editing = False
return {"FINISHED"}
@@ -75,7 +76,7 @@ class RemoveProfileDef(bpy.types.Operator, tool.Ifc.Operator):
profile: bpy.props.IntProperty()
def _execute(self, context):
props = context.scene.BIMProfileProperties
props = tool.Profile.get_profile_props()
current_index = props.active_profile_index
ifc_file = tool.Ifc.get()
@@ -113,7 +114,7 @@ class EnableEditingProfile(bpy.types.Operator):
profile: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMProfileProperties
props = tool.Profile.get_profile_props()
props.profile_attributes.clear()
bonsai.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.profile), props.profile_attributes)
props.active_profile_id = self.profile
@@ -126,7 +127,8 @@ class DisableEditingProfile(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMProfileProperties.active_profile_id = 0
props = tool.Profile.get_profile_props()
props.active_profile_id = 0
bpy.ops.bim.disable_editing_arbitrary_profile()
return {"FINISHED"}
@@ -137,7 +139,7 @@ class EditProfile(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMProfileProperties
props = tool.Profile.get_profile_props()
attributes = bonsai.bim.helper.export_attributes(props.profile_attributes)
profile = tool.Ifc.get().by_id(props.active_profile_id)
ifcopenshell.api.run("profile.edit_profile", tool.Ifc.get(), profile=profile, attributes=attributes)
@@ -152,7 +154,7 @@ class AddProfileDef(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMProfileProperties
props = tool.Profile.get_profile_props()
profile_class = props.profile_classes
if profile_class == "IfcArbitraryClosedProfileDef":
obj = props.object_to_profile
@@ -229,7 +231,7 @@ class EnableEditingArbitraryProfile(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMProfileProperties
props = tool.Profile.get_profile_props()
active_profile = props.profiles[props.active_profile_index]
profile_id = active_profile.ifc_definition_id
props.active_arbitrary_profile_id = profile_id
@@ -253,7 +255,7 @@ def disable_editing_arbitrary_profile(context):
bpy.data.objects.remove(obj)
bpy.data.meshes.remove(profile_mesh)
props = context.scene.BIMProfileProperties
props = tool.Profile.get_profile_props()
props.active_arbitrary_profile_id = 0
# need to update profile manager ui
# if this was called from decorator
@@ -276,7 +278,7 @@ class EditArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMProfileProperties
props = tool.Profile.get_profile_props()
old_profile = tool.Ifc.get().by_id(props.active_arbitrary_profile_id)
obj = context.active_object
@@ -322,7 +324,7 @@ class SelectProfileInProfilesUI(bpy.types.Operator):
profile_id: bpy.props.IntProperty()
def execute(self, context):
props = bpy.context.scene.BIMProfileProperties
props = tool.Profile.get_profile_props()
ifc_file = tool.Ifc.get()
profile = ifc_file.by_id(self.profile_id)
bpy.ops.bim.load_profiles()
+13 -2
View File
@@ -35,7 +35,7 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Union
def get_profile_classes(self, context):
@@ -91,6 +91,17 @@ class BIMProfileProperties(PropertyGroup):
poll=lambda self, obj: obj.type == "MESH",
)
if TYPE_CHECKING:
is_editing: bool
profiles: bpy.types.bpy_prop_collection_idprop[Profile]
active_profile_index: int
active_profile_id: int
active_arbitrary_profile_id: int
profile_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
profile_classes: str
is_filtering_material_profiles: bool
object_to_profile: Union[bpy.types.Object, None]
def generate_thumbnail_for_active_profile():
from PIL import Image, ImageDraw
@@ -98,7 +109,7 @@ def generate_thumbnail_for_active_profile():
if bpy.app.background:
return
props = bpy.context.scene.BIMProfileProperties
props = tool.Profile.get_profile_props()
ifc_file = tool.Ifc.get()
preview_collection = ProfileData.preview_collection
+16 -3
View File
@@ -16,12 +16,17 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy
import bonsai.bim.helper
import bonsai.tool as tool
from bpy.types import Panel, UIList
from bonsai.bim.module.profile.data import ProfileData
from bonsai.bim.module.profile.prop import generate_thumbnail_for_active_profile
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.profile.prop import BIMProfileProperties, Profile
class BIM_PT_profiles(Panel):
@@ -40,7 +45,7 @@ class BIM_PT_profiles(Panel):
def draw(self, context):
if not ProfileData.is_loaded:
ProfileData.load()
self.props = context.scene.BIMProfileProperties
self.props = tool.Profile.get_profile_props()
active_profile = None
if self.props.is_editing and (active_profile := tool.Profile.get_active_profile_ui()):
@@ -129,8 +134,16 @@ class BIM_PT_profiles(Panel):
class BIM_UL_profiles(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
props = context.scene.BIMProfileProperties
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: BIMProfileProperties,
item: Profile,
icon,
active_data,
active_propname,
):
if item:
row = layout.row(align=True)
row.prop(item, "name", text="", emboss=False)
+2 -2
View File
@@ -175,7 +175,7 @@ class MaterialPsetsData(Data):
@classmethod
def load(cls):
ifc_definition_id = None
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
if props.materials and props.active_material_index < len(props.materials):
ifc_definition_id = props.materials[props.active_material_index].ifc_definition_id
@@ -188,7 +188,7 @@ class MaterialPsetsData(Data):
@classmethod
def pset_name(cls):
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
if props.materials and props.active_material_index < len(props.materials):
material = props.materials[props.active_material_index]
if material.ifc_definition_id:
+1 -1
View File
@@ -173,7 +173,7 @@ def get_group_qto_names(self, context):
def get_profile_pset_names(self, context):
global psetnames
pprops = context.scene.BIMProfileProperties
pprops = tool.Profile.get_profile_props()
ifc_class = IfcStore.get_file().by_id(pprops.profiles[pprops.active_profile_index].ifc_definition_id).is_a()
if ifc_class not in psetnames:
psets = bonsai.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True, schema=tool.Ifc.get_schema())
+4 -4
View File
@@ -403,13 +403,13 @@ class BIM_PT_material_psets(Panel):
ifc_file = tool.Ifc.get()
if not ifc_file or ifc_file.schema == "IFC2X3":
return False # We don't support material psets in IFC2X3 because they suck
props = context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
if props.is_editing and (material := props.active_material) and material.ifc_definition_id:
return True
return False
def draw(self, context):
props = context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
if props.materials and props.active_material_index < len(props.materials):
ifc_definition_id = props.materials[props.active_material_index].ifc_definition_id
@@ -663,10 +663,10 @@ class BIM_PT_profile_psets(Panel):
@classmethod
def poll(cls, context):
props = context.scene.BIMProfileProperties
props = tool.Profile.get_profile_props()
if not props.is_editing:
return False
total_profiles = len(context.scene.BIMProfileProperties.profiles)
total_profiles = len(props.profiles)
if total_profiles > 0 and props.active_profile_index < total_profiles:
return True
return False
+11 -2
View File
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bcf.v2.visinfo
import bonsai.core.tool
import bonsai.tool as tool
@@ -32,16 +33,24 @@ import bcf.agnostic.model
import bcf.agnostic.topic
import bcf.agnostic.visinfo
from typing import Any, Union, TypeVar, TypeGuard, Optional
from typing import Any, Union, TypeVar, TypeGuard, Optional, TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.bcf.prop import BCFProperties
T = TypeVar("T")
class Bcf(bonsai.core.tool.Bcf):
@classmethod
def get_bcf_props(cls) -> "BCFProperties":
return bpy.context.scene.BCFProperties
@classmethod
def get_path(cls) -> str:
return bpy.context.scene.BCFProperties.bcf_file
props = cls.get_bcf_props()
return props.bcf_file
@classmethod
def is_list_of(cls, a: list[Any], t: type[T]) -> TypeGuard[list[T]]:
+4 -6
View File
@@ -191,9 +191,8 @@ class Blender(bonsai.core.tool.Blender):
if obj_type == "Object":
return bpy.data.objects.get(obj).BIMObjectProperties.ifc_definition_id
elif obj_type == "Material":
return context.scene.BIMMaterialProperties.materials[
context.scene.BIMMaterialProperties.active_material_index
].ifc_definition_id
props = tool.Material.get_material_props()
return props.materials[props.active_material_index].ifc_definition_id
elif obj_type == "MaterialSetItem":
return bpy.data.objects.get(obj).BIMObjectMaterialProperties.active_material_set_item_id
elif obj_type == "Task":
@@ -208,9 +207,8 @@ class Blender(bonsai.core.tool.Blender):
context.scene.BIMResourceProperties.active_resource_index
].ifc_definition_id
elif obj_type == "Profile":
return context.scene.BIMProfileProperties.profiles[
context.scene.BIMProfileProperties.active_profile_index
].ifc_definition_id
props = tool.Profile.get_profile_props()
return props.profiles[props.active_profile_index].ifc_definition_id
elif obj_type == "WorkSchedule":
return context.scene.BIMWorkScheduleProperties.active_work_schedule_id
elif obj_type == "Group":
+24 -12
View File
@@ -16,52 +16,62 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import ifcopenshell
import bpy
import bonsai.bim.helper
import bonsai.core.tool
import bonsai.tool as tool
from typing import Literal, Any, Union
from typing import Literal, Any, Union, TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.library.prop import BIMLibraryProperties
class Library(bonsai.core.tool.Library):
@classmethod
def get_library_props(cls) -> BIMLibraryProperties:
return bpy.context.scene.BIMLibraryProperties
@classmethod
def clear_editing_mode(cls) -> None:
bpy.context.scene.BIMLibraryProperties.editing_mode = "NONE"
cls.get_library_props().editing_mode = "NONE"
@classmethod
def export_library_attributes(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMLibraryProperties
props = cls.get_library_props()
return bonsai.bim.helper.export_attributes(props.library_attributes)
@classmethod
def export_reference_attributes(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMLibraryProperties
props = cls.get_library_props()
return bonsai.bim.helper.export_attributes(props.reference_attributes)
@classmethod
def get_active_library(cls) -> ifcopenshell.entity_instance:
return tool.Ifc.get().by_id(bpy.context.scene.BIMLibraryProperties.active_library_id)
props = cls.get_library_props()
return tool.Ifc.get().by_id(props.active_library_id)
@classmethod
def get_active_reference(cls) -> ifcopenshell.entity_instance:
return tool.Ifc.get().by_id(bpy.context.scene.BIMLibraryProperties.active_reference_id)
props = cls.get_library_props()
return tool.Ifc.get().by_id(props.active_reference_id)
@classmethod
def import_library_attributes(cls, library: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMLibraryProperties
props = cls.get_library_props()
props.library_attributes.clear()
bonsai.bim.helper.import_attributes2(library, props.library_attributes)
@classmethod
def import_reference_attributes(cls, reference: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMLibraryProperties
props = cls.get_library_props()
props.reference_attributes.clear()
bonsai.bim.helper.import_attributes2(reference, props.reference_attributes)
@classmethod
def import_references(cls, library: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMLibraryProperties
props = cls.get_library_props()
props.references.clear()
if tool.Ifc.get_schema() == "IFC2X3":
references = library.LibraryReference
@@ -74,7 +84,7 @@ class Library(bonsai.core.tool.Library):
@classmethod
def set_active_library(cls, library: Union[ifcopenshell.entity_instance, None]) -> None:
props = bpy.context.scene.BIMLibraryProperties
props = cls.get_library_props()
if library is None:
props.active_library_id = 0
else:
@@ -82,8 +92,10 @@ class Library(bonsai.core.tool.Library):
@classmethod
def set_active_reference(cls, reference: ifcopenshell.entity_instance) -> None:
bpy.context.scene.BIMLibraryProperties.active_reference_id = reference.id()
props = cls.get_library_props()
props.active_reference_id = reference.id()
@classmethod
def set_editing_mode(cls, mode: Literal["LIBRARY", "REFERENCES", "REFERENCE"]) -> None:
bpy.context.scene.BIMLibraryProperties.editing_mode = mode
props = cls.get_library_props()
props.editing_mode = mode
+20 -10
View File
@@ -33,12 +33,18 @@ from typing_extensions import assert_never
if TYPE_CHECKING:
# Avoid circular imports.
from bonsai.bim.module.material.prop import Material as MaterialItem
from bonsai.bim.module.material.prop import BIMMaterialProperties
class Material(bonsai.core.tool.Material):
@classmethod
def get_material_props(cls) -> BIMMaterialProperties:
return bpy.context.scene.BIMMaterialProperties
@classmethod
def disable_editing_materials(cls) -> None:
bpy.context.scene.BIMMaterialProperties.is_editing = False
props = tool.Material.get_material_props()
props.is_editing = False
@classmethod
def duplicate_material(cls, material: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
@@ -55,11 +61,13 @@ class Material(bonsai.core.tool.Material):
@classmethod
def enable_editing_materials(cls) -> None:
bpy.context.scene.BIMMaterialProperties.is_editing = True
props = tool.Material.get_material_props()
props.is_editing = True
@classmethod
def get_active_material_type(cls) -> str:
return bpy.context.scene.BIMMaterialProperties.material_type
props = tool.Material.get_material_props()
return props.material_type
@classmethod
def get_elements_by_material(cls, material: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
@@ -68,7 +76,7 @@ class Material(bonsai.core.tool.Material):
@classmethod
def get_active_material_item(cls) -> Union[MaterialItem, None]:
"""Get active material props item if index is valid, otherwise, return None."""
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
if 0 <= props.active_material_index < len(props.materials):
return props.materials[props.active_material_index]
return None
@@ -80,7 +88,7 @@ class Material(bonsai.core.tool.Material):
@classmethod
def import_material_definitions(cls, material_type: str) -> None:
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
# Store active category name to reselect it later.
# Occurs when we expand/contract all categories.
@@ -140,7 +148,8 @@ class Material(bonsai.core.tool.Material):
@classmethod
def is_editing_materials(cls) -> bool:
return bpy.context.scene.BIMMaterialProperties.is_editing
props = tool.Material.get_material_props()
return props.is_editing
@classmethod
def is_material_used_in_sets(cls, material: ifcopenshell.entity_instance) -> bool:
@@ -156,23 +165,24 @@ class Material(bonsai.core.tool.Material):
@classmethod
def load_material_attributes(cls, material: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
props.material_attributes.clear()
bonsai.bim.helper.import_attributes2(material, props.material_attributes)
@classmethod
def enable_editing_material(cls, material: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
props.active_material_id = material.id()
props.editing_material_type = "ATTRIBUTES"
@classmethod
def get_material_attributes(cls) -> dict[str, Any]:
return bonsai.bim.helper.export_attributes(bpy.context.scene.BIMMaterialProperties.material_attributes)
props = tool.Material.get_material_props()
return bonsai.bim.helper.export_attributes(props.material_attributes)
@classmethod
def disable_editing_material(cls) -> None:
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
props.active_material_id = 0
props.editing_material_type = ""
+3 -2
View File
@@ -26,6 +26,7 @@ import collections.abc
import numpy as np
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.grid
import ifcopenshell.api.pset
import ifcopenshell.geom
import ifcopenshell.util.element
@@ -1973,14 +1974,14 @@ class Model(bonsai.core.tool.Model):
extrusion.Position = position
@classmethod
def get_existing_x_angle(cls, extrusion):
def get_existing_x_angle(cls, extrusion: ifcopenshell.entity_instance) -> float:
x, y, z = extrusion.ExtrudedDirection.DirectionRatios
x_angle = Vector((0, 1)).angle_signed(Vector((y, z)))
return x_angle
@classmethod
def create_axis_curve(cls, obj: bpy.types.Object, grid_axis: ifcopenshell.entity_instance):
def create_axis_curve(cls, obj: bpy.types.Object, grid_axis: ifcopenshell.entity_instance) -> None:
m = tool.Surveyor.get_absolute_matrix(obj)
points = [m @ np.array(v.co.to_4d()) for v in obj.data.vertices[0:2]]
ifcopenshell.api.grid.create_axis_curve(
+6 -1
View File
@@ -33,9 +33,14 @@ from typing import Union, TYPE_CHECKING
if TYPE_CHECKING:
import bonsai.bim.module.profile.prop
from bonsai.bim.module.profile.prop import BIMProfileProperties
class Profile(bonsai.core.tool.Profile):
@classmethod
def get_profile_props(cls) -> BIMProfileProperties:
return bpy.context.scene.BIMProfileProperties
@classmethod
def draw_image_for_ifc_profile(
cls, draw: PIL.ImageDraw.ImageDraw, profile: ifcopenshell.entity_instance, size: float
@@ -116,7 +121,7 @@ class Profile(bonsai.core.tool.Profile):
@classmethod
def get_active_profile_ui(cls) -> Union[bonsai.bim.module.profile.prop.Profile, None]:
props = bpy.context.scene.BIMProfileProperties
props = cls.get_profile_props()
index = props.active_profile_index
if len(props.profiles) > index >= 0:
return props.profiles[index]
+10 -7
View File
@@ -31,7 +31,7 @@ class TestImplementsTool(NewFile):
class TestClearEditingMode(NewFile):
def test_run(self):
props = bpy.context.scene.BIMLibraryProperties
props = tool.Library.get_library_props()
props.editing_mode = "LIBRARY"
subject.clear_editing_mode()
assert props.editing_mode == "NONE"
@@ -78,7 +78,7 @@ class TestImportLibraryAttributes(NewFile):
tool.Ifc.set(ifc := ifcopenshell.file())
library = ifc.createIfcLibraryInformation("Name", "Version", None, "VersionDate", "Location", "Description")
subject.import_library_attributes(library)
props = bpy.context.scene.BIMLibraryProperties
props = tool.Library.get_library_props()
assert props.library_attributes.get("Name").string_value == "Name"
assert props.library_attributes.get("Version").string_value == "Version"
assert props.library_attributes.get("VersionDate").string_value == "VersionDate"
@@ -91,7 +91,7 @@ class TestImportReferenceAttributes(NewFile):
tool.Ifc.set(ifc := ifcopenshell.file())
reference = ifc.createIfcLibraryReference("Location", "Identification", "Name", "Description", "Language")
subject.import_reference_attributes(reference)
props = bpy.context.scene.BIMLibraryProperties
props = tool.Library.get_library_props()
assert props.reference_attributes.get("Location").string_value == "Location"
assert props.reference_attributes.get("Identification").string_value == "Identification"
assert props.reference_attributes.get("Name").string_value == "Name"
@@ -105,7 +105,7 @@ class TestImportReferences(NewFile):
library = ifc.createIfcLibraryInformation()
reference = ifc.createIfcLibraryReference(Name="Reference", ReferencedLibrary=library)
subject.import_references(library)
props = bpy.context.scene.BIMLibraryProperties
props = tool.Library.get_library_props()
assert props.references[0].ifc_definition_id == reference.id()
assert props.references[0].name == "Reference"
@@ -116,7 +116,8 @@ class TestSetActiveLibrary(NewFile):
tool.Ifc.set(ifc)
library = ifc.createIfcLibraryInformation()
subject.set_active_library(library)
assert bpy.context.scene.BIMLibraryProperties.active_library_id == library.id()
props = tool.Library.get_library_props()
assert props.active_library_id == library.id()
class TestSetActiveReference(NewFile):
@@ -125,10 +126,12 @@ class TestSetActiveReference(NewFile):
tool.Ifc.set(ifc)
reference = ifc.createIfcLibraryReference()
subject.set_active_reference(reference)
assert bpy.context.scene.BIMLibraryProperties.active_reference_id == reference.id()
props = tool.Library.get_library_props()
assert props.active_reference_id == reference.id()
class TestSetEditingMode(NewFile):
def test_run(self):
subject.set_editing_mode("LIBRARY")
assert bpy.context.scene.BIMLibraryProperties.editing_mode == "LIBRARY"
props = tool.Library.get_library_props()
assert props.editing_mode == "LIBRARY"
+18 -14
View File
@@ -35,25 +35,28 @@ class TestImplementsTool(NewFile):
class TestDisableEditingMaterials(NewFile):
def test_run(self):
bpy.context.scene.BIMMaterialProperties.is_editing = True
props = tool.Material.get_material_props()
props.is_editing = True
subject.disable_editing_materials()
assert bpy.context.scene.BIMMaterialProperties.is_editing is False
assert props.is_editing is False
class TestEnableEditingMaterials(NewFile):
def test_run(self):
bpy.context.scene.BIMMaterialProperties.is_editing = False
props = tool.Material.get_material_props()
props.is_editing = False
subject.enable_editing_materials()
assert bpy.context.scene.BIMMaterialProperties.is_editing is True
assert props.is_editing is True
class TestGetActiveMaterialType(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
bpy.context.scene.BIMMaterialProperties.material_type = "IfcMaterial"
props = tool.Material.get_material_props()
props.material_type = "IfcMaterial"
assert subject.get_active_material_type() == "IfcMaterial"
bpy.context.scene.BIMMaterialProperties.material_type = "IfcMaterialLayerSet"
props.material_type = "IfcMaterialLayerSet"
assert subject.get_active_material_type() == "IfcMaterialLayerSet"
@@ -73,7 +76,7 @@ class TestImportMaterialDefinitions(NewFile):
tool.Ifc.set(ifc)
material = ifc.createIfcMaterial(Name="Name", Category="Category")
subject.import_material_definitions("IfcMaterial")
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
assert props.materials[0].ifc_definition_id == 0
assert props.materials[0].name == "Category"
assert props.materials[0].is_category is True
@@ -85,7 +88,7 @@ class TestImportMaterialDefinitions(NewFile):
tool.Ifc.set(ifc)
material = ifc.createIfcMaterial(Name="Name", Category="Category")
subject.import_material_definitions("IfcMaterial")
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
props.materials[0].is_expanded = True
subject.import_material_definitions("IfcMaterial")
assert len(props.materials) == 2
@@ -98,7 +101,7 @@ class TestImportMaterialDefinitions(NewFile):
tool.Ifc.set(ifc)
material = ifc.createIfcMaterialLayerSet(LayerSetName="Name")
subject.import_material_definitions("IfcMaterialLayerSet")
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Name"
assert props.materials[0].total_elements == 0
@@ -108,7 +111,7 @@ class TestImportMaterialDefinitions(NewFile):
tool.Ifc.set(ifc)
material = ifc.createIfcMaterialProfileSet(Name="Name")
subject.import_material_definitions("IfcMaterialProfileSet")
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Name"
assert props.materials[0].total_elements == 0
@@ -118,7 +121,7 @@ class TestImportMaterialDefinitions(NewFile):
tool.Ifc.set(ifc)
material = ifc.createIfcMaterialConstituentSet(Name="Name")
subject.import_material_definitions("IfcMaterialConstituentSet")
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Name"
assert props.materials[0].total_elements == 0
@@ -128,7 +131,7 @@ class TestImportMaterialDefinitions(NewFile):
tool.Ifc.set(ifc)
material = ifc.createIfcMaterialList()
subject.import_material_definitions("IfcMaterialList")
props = bpy.context.scene.BIMMaterialProperties
props = tool.Material.get_material_props()
assert props.materials[0].ifc_definition_id == material.id()
assert props.materials[0].name == "Unnamed"
assert props.materials[0].total_elements == 0
@@ -136,9 +139,10 @@ class TestImportMaterialDefinitions(NewFile):
class TestIsEditingMaterials(NewFile):
def test_run(self):
bpy.context.scene.BIMMaterialProperties.is_editing = False
props = tool.Material.get_material_props()
props.is_editing = False
assert subject.is_editing_materials() is False
bpy.context.scene.BIMMaterialProperties.is_editing = True
props.is_editing = True
assert subject.is_editing_materials() is True
@@ -22,13 +22,14 @@ import ifcopenshell.util.element
import ifcopenshell.util.unit
import ifcopenshell.util.placement
import numpy as np
from ifcopenshell.util.shape_builder import VectorType, V, ifc_safe_vector_type
def create_axis_curve(
file: ifcopenshell.file,
*,
p1: np.ndarray,
p2: np.ndarray,
p1: VectorType,
p2: VectorType,
grid_axis: ifcopenshell.entity_instance,
is_si: bool = True,
) -> None:
@@ -60,7 +61,7 @@ def create_axis_curve(
model, p1=np.array((0., 0., 0.)), p2=np.array((0., 10., 0.)), grid_axis=axis_1)
"""
existing_curve = grid_axis.AxisCurve
p1, p2 = V(p1), V(p2)
if is_si:
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file)
p1 /= unit_scale
@@ -70,8 +71,8 @@ def create_axis_curve(
grid_matrix_i = np.linalg.inv(ifcopenshell.util.placement.get_local_placement(grid.ObjectPlacement))
grid_axis.AxisCurve = file.createIfcPolyline(
(
file.createIfcCartesianPoint((grid_matrix_i @ p1).tolist()),
file.createIfcCartesianPoint((grid_matrix_i @ p2).tolist()),
file.createIfcCartesianPoint(ifc_safe_vector_type(grid_matrix_i @ p1)),
file.createIfcCartesianPoint(ifc_safe_vector_type(grid_matrix_i @ p2)),
)
)
@@ -264,7 +264,6 @@ class Usecase:
"IfcProductDefinitionShape": ["HasShapeAspects"],
"IfcRepresentationMap": ["HasShapeAspects"],
}
print('appending type product!')
self.existing_contexts = self.file.by_type("IfcGeometricRepresentationContext")
element = self.add_element(self.settings["element"])
self.reuse_existing_contexts()