Skip to content

Commit

Permalink
Basic integration of bSDD with BlenderBIM Add-on: you can now assign …
Browse files Browse the repository at this point in the history
…classification references from the bSDD and set the active bSDD domain.
  • Loading branch information
Moult committed Sep 19, 2023
1 parent 812d850 commit a5a958d
Show file tree
Hide file tree
Showing 10 changed files with 431 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/blenderbim/Makefile
Expand Up @@ -203,6 +203,8 @@ endif
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcfm/ifcfm dist/blenderbim/libs/site/packages/
# Provides IFCCOBie functionality
cp -r dist/working/IfcOpenShell-0.7.0/src/ifccobie/* dist/blenderbim/libs/site/packages/
# Provides bSDD functionality
cp -r dist/working/IfcOpenShell-0.7.0/src/bsdd/* dist/blenderbim/libs/site/packages/
# Provides IFCDiff functionality
cp -r dist/working/IfcOpenShell-0.7.0/src/ifcdiff/* dist/blenderbim/libs/site/packages/
# Provides IFCCSV functionality
Expand Down
1 change: 1 addition & 0 deletions src/blenderbim/blenderbim/bim/__init__.py
Expand Up @@ -30,6 +30,7 @@
"project": None,
"search": None,
"bcf": None,
"bsdd": None,
"root": None,
"unit": None,
"model": None,
Expand Down
40 changes: 40 additions & 0 deletions src/blenderbim/blenderbim/bim/module/bsdd/__init__.py
@@ -0,0 +1,40 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.

import bpy
from . import ui, prop, operator

classes = (
operator.LoadBSDDDomains,
operator.SearchBSDDClassifications,
operator.SetActiveBSDDDomain,
prop.BSDDDomain,
prop.BSDDClassification,
prop.BIMBSDDProperties,
ui.BIM_UL_bsdd_domains,
ui.BIM_UL_bsdd_classifications,
ui.BIM_PT_bsdd,
)


def register():
bpy.types.Scene.BIMBSDDProperties = bpy.props.PointerProperty(type=prop.BIMBSDDProperties)


def unregister():
del bpy.types.Scene.BIMBSDDProperties
85 changes: 85 additions & 0 deletions src/blenderbim/blenderbim/bim/module/bsdd/operator.py
@@ -0,0 +1,85 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.

import os
import bpy
import bsdd
import ifcopenshell
import blenderbim.tool as tool


class LoadBSDDDomains(bpy.types.Operator):
bl_idname = "bim.load_bsdd_domains"
bl_label = "Load bSDD Domains"
bl_options = {"REGISTER", "UNDO"}

def execute(self, context):
props = context.scene.BIMBSDDProperties
props.domains.clear()
client = bsdd.Client()
for domain in sorted(client.Domain(), key=lambda x: x["name"]):
new = props.domains.add()
new.name = domain["name"]
new.namespace_uri = domain["namespaceUri"]
new.default_language_code = domain["defaultLanguageCode"]
new.organization_name_owner = domain["organizationNameOwner"]
new.status = domain["status"]
new.version = domain["version"]
return {"FINISHED"}


class SetActiveBSDDDomain(bpy.types.Operator):
bl_idname = "bim.set_active_bsdd_domain"
bl_label = "Load bSDD Domains"
bl_options = {"REGISTER", "UNDO"}
name: bpy.props.StringProperty()
uri: bpy.props.StringProperty()

def execute(self, context):
props = context.scene.BIMBSDDProperties
props.active_domain = self.name
props.active_uri = self.uri
return {"FINISHED"}


class SearchBSDDClassifications(bpy.types.Operator):
bl_idname = "bim.search_bsdd_classifications"
bl_label = "Search bSDD Classifications"
bl_options = {"REGISTER", "UNDO"}

def execute(self, context):
props = context.scene.BIMBSDDProperties
props.classifications.clear()
client = bsdd.Client()
related_ifc_entities = []
if len(props.keyword) < 3:
return {"FINISHED"}
if props.should_filter_ifc_class and context.active_object:
element = tool.Ifc.get_entity(context.active_object)
if element:
related_ifc_entities = [element.is_a()]
results = client.ClassificationSearchOpen(props.keyword, DomainNamespaceUris=[props.active_uri], RelatedIfcEntities=related_ifc_entities)
for result in sorted(results["classifications"], key=lambda x: x["referenceCode"]):
new = props.classifications.add()
new.name = result["name"]
new.reference_code = result["referenceCode"]
new.description = result["description"]
new.namespace_uri = result["namespaceUri"]
new.domain_name = result["domainName"]
new.domain_namespace_uri = result["domainNamespaceUri"]
return {"FINISHED"}
59 changes: 59 additions & 0 deletions src/blenderbim/blenderbim/bim/module/bsdd/prop.py
@@ -0,0 +1,59 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.

import bpy
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
StringProperty,
EnumProperty,
BoolProperty,
IntProperty,
FloatProperty,
FloatVectorProperty,
CollectionProperty,
)


class BSDDDomain(PropertyGroup):
name: StringProperty(name="Name")
namespace_uri: StringProperty(name="URI")
default_language_code: StringProperty(name="Language")
organization_name_owner: StringProperty(name="Organization")
status: StringProperty(name="Status")
version: StringProperty(name="Version")


class BSDDClassification(PropertyGroup):
name: StringProperty(name="Name")
reference_code: StringProperty(name="Reference Code")
description: StringProperty(name="Description")
namespace_uri: StringProperty(name="Namespace URI")
domain_name: StringProperty(name="Domain Name")
domain_namespace_uri: StringProperty(name="Domain Namespace URI")


class BIMBSDDProperties(PropertyGroup):
active_domain: StringProperty(name="Active Domain")
active_uri: StringProperty(name="Active URI")
domains: CollectionProperty(name="Domains", type=BSDDDomain)
active_domain_index: IntProperty(name="Active Domain Index")
classifications: CollectionProperty(name="Classifications", type=BSDDClassification)
active_classification_index: IntProperty(name="Active Classification Index")
keyword: StringProperty(name="Keyword")
should_filter_ifc_class: BoolProperty(name="Filter Active IFC Class", default=True)
71 changes: 71 additions & 0 deletions src/blenderbim/blenderbim/bim/module/bsdd/ui.py
@@ -0,0 +1,71 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.

import blenderbim.tool as tool
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore


class BIM_PT_bsdd(Panel):
bl_label = "buildingSMART Data Dictionary"
bl_idname = "BIM_PT_bsdd"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_project_setup"

def draw(self, context):
props = context.scene.BIMBSDDProperties
if props.active_domain:
row = self.layout.row()
row.label(text="Active: " + props.active_domain, icon="URL")
else:
row = self.layout.row()
row.label(text="No Active bSDD Domain", icon="ERROR")

if len(props.domains):
self.layout.template_list(
"BIM_UL_bsdd_domains",
"",
props,
"domains",
props,
"active_domain_index",
)
else:
row = self.layout.row()
row.operator("bim.load_bsdd_domains")


class BIM_UL_bsdd_domains(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
row.label(text=f"{item.name} ({item.organization_name_owner})")
op = row.operator("bim.set_active_bsdd_domain", text="", icon="RESTRICT_SELECT_OFF")
op.name = item.name
op.uri = item.namespace_uri


class BIM_UL_bsdd_classifications(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
row = layout.row(align=True)
row.label(text=item.reference_code)
row.label(text=item.name)
Expand Up @@ -21,7 +21,9 @@

classes = (
operator.AddClassification,
operator.AddClassificationFromBSDD,
operator.AddClassificationReference,
operator.AddClassificationReferenceFromBSDD,
operator.ChangeClassificationLevel,
operator.DisableEditingClassification,
operator.DisableEditingClassificationReference,
Expand Down
69 changes: 69 additions & 0 deletions src/blenderbim/blenderbim/bim/module/classification/operator.py
Expand Up @@ -54,6 +54,25 @@ def _execute(self, context):
)


class AddClassificationFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_classification_from_bsdd"
bl_label = "Add Classification From bSDD"
bl_options = {"REGISTER", "UNDO"}

def _execute(self, context):
props = context.scene.BIMBSDDProperties
domain = [d for d in props.domains if d.name == props.active_domain][0]
for element in tool.Ifc.get().by_type("IfcClassification"):
if element.Name == props.active_domain or element.Location == domain.namespace_uri:
return
classification = ifcopenshell.api.run(
"classification.add_classification", tool.Ifc.get(), classification=props.active_domain
)
classification.Source = domain.organization_name_owner
classification.Location = domain.namespace_uri
classification.Edition = domain.version


class EnableEditingClassification(bpy.types.Operator):
bl_idname = "bim.enable_editing_classification"
bl_label = "Enable Editing Classification"
Expand Down Expand Up @@ -254,6 +273,56 @@ def _execute(self, context):
)


class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_classification_reference_from_bsdd"
bl_label = "Add Classification Reference From bSDD"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
obj_type: bpy.props.StringProperty()

def _execute(self, context):
if self.obj_type == "Object":
if context.selected_objects:
objects = [o.name for o in context.selected_objects]
else:
objects = [context.active_object.name]
else:
objects = [self.obj]
props = context.scene.BIMClassificationProperties
bprops = context.scene.BIMBSDDProperties

bsdd_classification = bprops.classifications[bprops.active_classification_index]

classification = None
for element in tool.Ifc.get().by_type("IfcClassification"):
if (
element.Name == bsdd_classification.domain_name
or element.Location == bsdd_classification.domain_namespace_uri
):
classification = element
break

if not classification:
classification = ifcopenshell.api.run(
"classification.add_classification", tool.Ifc.get(), classification=bsdd_classification.domain_name
)
classification.Location = bsdd_classification.domain_namespace_uri

for obj in objects:
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
if not ifc_definition_id:
continue
reference = ifcopenshell.api.run(
"classification.add_reference",
tool.Ifc.get(),
product=tool.Ifc.get().by_id(ifc_definition_id),
classification=classification,
identification=bsdd_classification.reference_code,
name=bsdd_classification.name,
)
reference.Location = bsdd_classification.namespace_uri


class ChangeClassificationLevel(bpy.types.Operator):
bl_idname = "bim.change_classification_level"
bl_label = "Change Classification Level"
Expand Down
9 changes: 9 additions & 0 deletions src/blenderbim/blenderbim/bim/module/classification/prop.py
Expand Up @@ -48,6 +48,15 @@ class ClassificationReference(PropertyGroup):


class BIMClassificationProperties(PropertyGroup):
classification_source: EnumProperty(
items=[
("FILE", "IFC File", ""),
("BSDD", "buildingSMART Data Dictionary", ""),
("MANUAL", "Manual Entry", ""),
],
name="Classification Source",
default="FILE",
)
available_classifications: EnumProperty(items=get_available_classifications, name="Available Classifications")
classification_attributes: CollectionProperty(name="Classification Attributes", type=Attribute)
active_classification_id: IntProperty(name="Active Classification Id")
Expand Down

0 comments on commit a5a958d

Please sign in to comment.