Fix #1576. Managing IfcSystem subtypes is now possible.

This commit is contained in:
Dion Moult
2022-01-20 18:21:57 +11:00
parent 000e3acb2b
commit 67952ed159
20 changed files with 817 additions and 166 deletions
@@ -20,17 +20,16 @@ import bpy
from . import ui, prop, operator from . import ui, prop, operator
classes = ( classes = (
operator.LoadSystems,
operator.DisableSystemEditingUI,
operator.AddSystem, operator.AddSystem,
operator.EditSystem,
operator.RemoveSystem,
operator.ToggleAssigningSystem,
operator.AssignSystem, operator.AssignSystem,
operator.UnassignSystem,
operator.EnableEditingSystem,
operator.DisableEditingSystem, operator.DisableEditingSystem,
operator.DisableSystemEditingUI,
operator.EditSystem,
operator.EnableEditingSystem,
operator.LoadSystems,
operator.RemoveSystem,
operator.SelectSystemProducts, operator.SelectSystemProducts,
operator.UnassignSystem,
prop.System, prop.System,
prop.BIMSystemProperties, prop.BIMSystemProperties,
ui.BIM_PT_systems, ui.BIM_PT_systems,
@@ -0,0 +1,82 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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
import ifcopenshell
import ifcopenshell.util.schema
import blenderbim.tool as tool
def refresh():
SystemData.is_loaded = False
ObjectSystemData.is_loaded = False
class SystemData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"system_class": cls.system_class(),
"total_systems": cls.total_systems(),
}
cls.is_loaded = True
@classmethod
def system_class(cls):
declaration = tool.Ifc.schema().declaration_by_name("IfcSystem")
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
# We're only interested in systems for services. Not sure why IFC groups these together.
return [
(c, c, "")
for c in sorted([d.name() for d in declarations])
if c not in ("IfcZone", "IfcStructuralAnalysisModel")
]
@classmethod
def total_systems(cls):
return len(tool.Ifc.get().by_type("IfcSystem"))
class ObjectSystemData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"systems": cls.systems(),
"total_systems": cls.total_systems(),
}
cls.is_loaded = True
@classmethod
def systems(cls):
results = []
element = tool.Ifc.get_entity(bpy.context.active_object)
if not element:
return results
for system in ifcopenshell.util.system.get_element_systems(element):
results.append({"id": system.id(), "name": system.Name or "Unnamed", "ifc_class": system.is_a()})
return results
@classmethod
def total_systems(cls):
return len(tool.Ifc.get().by_type("IfcSystem"))
@@ -17,208 +17,118 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
import ifcopenshell.util.attribute
import ifcopenshell.api import ifcopenshell.api
import blenderbim.bim.helper import blenderbim.tool as tool
import blenderbim.core.system as core
import blenderbim.bim.handler
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.system.data import Data
class LoadSystems(bpy.types.Operator): class Operator:
def execute(self, context):
IfcStore.execute_ifc_operator(self, context)
blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"}
class LoadSystems(bpy.types.Operator, Operator):
bl_idname = "bim.load_systems" bl_idname = "bim.load_systems"
bl_label = "Load Systems" bl_label = "Load Systems"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def _execute(self, context):
props = context.scene.BIMSystemProperties core.load_systems(tool.System)
props.systems.clear()
for ifc_definition_id, system in Data.systems.items():
new = props.systems.add()
new.ifc_definition_id = ifc_definition_id
new.name = system["Name"]
props.is_editing = True
bpy.ops.bim.disable_editing_system()
return {"FINISHED"}
class DisableSystemEditingUI(bpy.types.Operator): class DisableSystemEditingUI(bpy.types.Operator, Operator):
bl_idname = "bim.disable_system_editing_ui" bl_idname = "bim.disable_system_editing_ui"
bl_label = "Disable System Editing UI" bl_label = "Disable System Editing UI"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def _execute(self, context):
context.scene.BIMSystemProperties.is_editing = False core.disable_system_editing_ui(tool.System)
context.scene.BIMSystemProperties.active_system_id = 0
return {"FINISHED"}
class AddSystem(bpy.types.Operator): class AddSystem(bpy.types.Operator, Operator):
bl_idname = "bim.add_system" bl_idname = "bim.add_system"
bl_label = "Add System" bl_label = "Add System"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
result = ifcopenshell.api.run("system.add_system", IfcStore.get_file()) core.add_system(tool.Ifc, tool.System, ifc_class=context.scene.BIMSystemProperties.system_class)
Data.load(IfcStore.get_file())
bpy.ops.bim.load_systems()
bpy.ops.bim.enable_editing_system(system=result.id())
return {"FINISHED"}
class EditSystem(bpy.types.Operator): class EditSystem(bpy.types.Operator, Operator):
bl_idname = "bim.edit_system" bl_idname = "bim.edit_system"
bl_label = "Edit System" bl_label = "Edit System"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMSystemProperties core.edit_system(
attributes = {} tool.Ifc, tool.System, system=tool.Ifc.get().by_id(context.scene.BIMSystemProperties.active_system_id)
for attribute in props.system_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
attributes[attribute.name] = attribute.string_value
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"system.edit_system",
self.file,
**{"system": self.file.by_id(props.active_system_id), "attributes": attributes}
) )
Data.load(IfcStore.get_file())
bpy.ops.bim.load_systems()
return {"FINISHED"}
class RemoveSystem(bpy.types.Operator): class RemoveSystem(bpy.types.Operator, Operator):
bl_idname = "bim.remove_system" bl_idname = "bim.remove_system"
bl_label = "Remove System" bl_label = "Remove System"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
system: bpy.props.IntProperty() system: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMSystemProperties core.remove_system(tool.Ifc, tool.System, system=tool.Ifc.get().by_id(self.system))
self.file = IfcStore.get_file()
ifcopenshell.api.run("system.remove_system", self.file, **{"system": self.file.by_id(self.system)})
Data.load(IfcStore.get_file())
bpy.ops.bim.load_systems()
return {"FINISHED"}
class EnableEditingSystem(bpy.types.Operator): class EnableEditingSystem(bpy.types.Operator, Operator):
bl_idname = "bim.enable_editing_system" bl_idname = "bim.enable_editing_system"
bl_label = "Enable Editing System" bl_label = "Enable Editing System"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
system: bpy.props.IntProperty() system: bpy.props.IntProperty()
def execute(self, context): def _execute(self, context):
props = context.scene.BIMSystemProperties core.enable_editing_system(tool.System, system=tool.Ifc.get().by_id(self.system))
props.system_attributes.clear()
blenderbim.bim.helper.import_attributes("IfcSystem", props.system_attributes, Data.systems[self.system])
props.active_system_id = self.system
return {"FINISHED"}
class DisableEditingSystem(bpy.types.Operator): class DisableEditingSystem(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_system" bl_idname = "bim.disable_editing_system"
bl_label = "Disable Editing System" bl_label = "Disable Editing System"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def _execute(self, context):
context.scene.BIMSystemProperties.active_system_id = 0 core.disable_editing_system(tool.System)
return {"FINISHED"}
class ToggleAssigningSystem(bpy.types.Operator): class AssignSystem(bpy.types.Operator, Operator):
bl_idname = "bim.toggle_assigning_system"
bl_label = "Toggle Assigning System"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMSystemProperties.is_adding = not context.scene.BIMSystemProperties.is_adding
return {"FINISHED"}
class AssignSystem(bpy.types.Operator):
bl_idname = "bim.assign_system" bl_idname = "bim.assign_system"
bl_label = "Assign System" bl_label = "Assign System"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
product: bpy.props.StringProperty()
system: bpy.props.IntProperty() system: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() for obj in context.selected_objects:
products = [bpy.data.objects.get(self.product)] if self.product else context.selected_objects element = tool.Ifc.get_entity(obj)
for product in products: if element:
if not product.BIMObjectProperties.ifc_definition_id: core.assign_system(tool.Ifc, system=tool.Ifc.get().by_id(self.system), product=element)
continue
ifcopenshell.api.run(
"system.assign_system",
self.file,
**{
"product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id),
"system": self.file.by_id(self.system),
}
)
Data.load(self.file)
return {"FINISHED"}
class UnassignSystem(bpy.types.Operator): class UnassignSystem(bpy.types.Operator, Operator):
bl_idname = "bim.unassign_system" bl_idname = "bim.unassign_system"
bl_label = "Unassign System" bl_label = "Unassign System"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
product: bpy.props.StringProperty()
system: bpy.props.IntProperty() system: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context): def _execute(self, context):
self.file = IfcStore.get_file() for obj in context.selected_objects:
products = [bpy.data.objects.get(self.product)] if self.product else context.selected_objects element = tool.Ifc.get_entity(obj)
for product in products: if element:
props = product.BIMObjectProperties core.unassign_system(tool.Ifc, system=tool.Ifc.get().by_id(self.system), product=element)
if not props.ifc_definition_id:
continue
if not (props.ifc_definition_id in Data.products and self.system in Data.products[props.ifc_definition_id]):
continue
ifcopenshell.api.run(
"system.unassign_system",
self.file,
**{
"product": self.file.by_id(product.BIMObjectProperties.ifc_definition_id),
"system": self.file.by_id(self.system),
}
)
Data.load(self.file)
return {"FINISHED"}
class SelectSystemProducts(bpy.types.Operator): class SelectSystemProducts(bpy.types.Operator, Operator):
bl_idname = "bim.select_system_products" bl_idname = "bim.select_system_products"
bl_label = "Select System Products" bl_label = "Select System Products"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
system: bpy.props.IntProperty() system: bpy.props.IntProperty()
def execute(self, context): def _execute(self, context):
for obj in context.visible_objects: core.select_system_products(tool.System, system=tool.Ifc.get().by_id(self.system))
obj.select_set(False)
if not obj.BIMObjectProperties.ifc_definition_id:
continue
product_systems = Data.products.get(obj.BIMObjectProperties.ifc_definition_id, [])
if self.system in product_systems:
obj.select_set(True)
return {"FINISHED"}
@@ -17,6 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
from blenderbim.bim.module.system.data import SystemData
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from bpy.props import ( from bpy.props import (
@@ -31,8 +32,15 @@ from bpy.props import (
) )
def get_system_class(self, context):
if not SystemData.is_loaded:
SystemData.load()
return SystemData.data["system_class"]
class System(PropertyGroup): class System(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
ifc_class: StringProperty(name="IFC Class")
ifc_definition_id: IntProperty(name="IFC Definition ID") ifc_definition_id: IntProperty(name="IFC Definition ID")
@@ -43,3 +51,4 @@ class BIMSystemProperties(PropertyGroup):
systems: CollectionProperty(name="Systems", type=System) systems: CollectionProperty(name="Systems", type=System)
active_system_index: IntProperty(name="Active System Index") active_system_index: IntProperty(name="Active System Index")
active_system_id: IntProperty(name="Active System Id") active_system_id: IntProperty(name="Active System Id")
system_class: EnumProperty(items=get_system_class, name="Class")
@@ -18,7 +18,7 @@
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.system.data import Data from blenderbim.bim.module.system.data import SystemData, ObjectSystemData
class BIM_PT_systems(Panel): class BIM_PT_systems(Panel):
@@ -35,15 +35,18 @@ class BIM_PT_systems(Panel):
return IfcStore.get_file() return IfcStore.get_file()
def draw(self, context): def draw(self, context):
if not Data.is_loaded: if not SystemData.is_loaded:
Data.load(IfcStore.get_file()) SystemData.load()
self.props = context.scene.BIMSystemProperties self.props = context.scene.BIMSystemProperties
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text="{} Systems Found".format(len(Data.systems)), icon="OUTLINER") row.label(text="{} Systems Found".format(SystemData.data["total_systems"]), icon="OUTLINER")
if self.props.is_editing: if self.props.is_editing:
row.operator("bim.add_system", text="", icon="ADD")
row.operator("bim.disable_system_editing_ui", text="", icon="CANCEL") row.operator("bim.disable_system_editing_ui", text="", icon="CANCEL")
row = self.layout.row(align=True)
row.prop(self.props, "system_class", text="")
row.operator("bim.add_system", text="", icon="ADD")
else: else:
row.operator("bim.load_systems", text="", icon="GREASEPENCIL") row.operator("bim.load_systems", text="", icon="GREASEPENCIL")
@@ -84,13 +87,13 @@ class BIM_PT_object_systems(Panel):
return IfcStore.get_file() and context.active_object.BIMObjectProperties.ifc_definition_id return IfcStore.get_file() and context.active_object.BIMObjectProperties.ifc_definition_id
def draw(self, context): def draw(self, context):
if not Data.is_loaded: if not ObjectSystemData.is_loaded:
Data.load(IfcStore.get_file()) ObjectSystemData.load()
self.props = context.scene.BIMSystemProperties self.props = context.scene.BIMSystemProperties
row = self.layout.row(align=True) if self.props.is_editing:
if self.props.is_adding: row = self.layout.row()
row.label(text="Adding Systems", icon="OUTLINER") row.alignment = "RIGHT"
row.operator("bim.toggle_assigning_system", text="", icon="CANCEL") row.operator("bim.disable_system_editing_ui", text="", icon="CANCEL")
self.layout.template_list( self.layout.template_list(
"BIM_UL_object_systems", "BIM_UL_object_systems",
"", "",
@@ -100,17 +103,23 @@ class BIM_PT_object_systems(Panel):
"active_system_index", "active_system_index",
) )
else: else:
row.label(text=f"{len(Data.systems)} Systems in IFC Project", icon="OUTLINER")
row.operator("bim.toggle_assigning_system", text="", icon="ADD")
systems_object = Data.products.get(context.active_object.BIMObjectProperties.ifc_definition_id, [])
for system_id in systems_object:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text=Data.systems[system_id].get("Name", "Unnamed")) row.label(text=f"{ObjectSystemData.data['total_systems']} Systems in IFC Project", icon="OUTLINER")
row.operator("bim.load_systems", text="", icon="GREASEPENCIL")
system_icons = {
"IfcSystem": "EXTERNAL_DRIVE",
"IfcDistributionSystem": "NETWORK_DRIVE",
"IfcDistributionCircuit": "DRIVER",
"IfcBuildingSystem": "MOD_BUILD",
}
for system in ObjectSystemData.data["systems"]:
row = self.layout.row(align=True)
row.label(text=system["name"], icon=system_icons[system["ifc_class"]])
op = row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF") op = row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF")
op.system = system_id op.system = system["id"]
op = row.operator("bim.unassign_system", text="", icon="X") op = row.operator("bim.unassign_system", text="", icon="X")
op.system = system_id op.system = system["id"]
if not systems_object: if not systems_object:
self.layout.label(text="No System associated with Active Object") self.layout.label(text="No System associated with Active Object")
@@ -118,9 +127,15 @@ class BIM_PT_object_systems(Panel):
class BIM_UL_systems(UIList): class BIM_UL_systems(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
system_icons = {
"IfcSystem": "EXTERNAL_DRIVE",
"IfcDistributionSystem": "NETWORK_DRIVE",
"IfcDistributionCircuit": "DRIVER",
"IfcBuildingSystem": "MOD_BUILD",
}
if item: if item:
row = layout.row(align=True) row = layout.row(align=True)
row.label(text=item.name) row.label(text=item.name, icon=system_icons[item.ifc_class])
system_id = item.ifc_definition_id system_id = item.ifc_definition_id
if context.scene.BIMSystemProperties.active_system_id == system_id: if context.scene.BIMSystemProperties.active_system_id == system_id:
op = row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF") op = row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF")
@@ -143,8 +158,13 @@ class BIM_UL_systems(UIList):
class BIM_UL_object_systems(UIList): class BIM_UL_object_systems(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
system_icons = {
"IfcSystem": "EXTERNAL_DRIVE",
"IfcDistributionSystem": "NETWORK_DRIVE",
"IfcDistributionCircuit": "DRIVER",
"IfcBuildingSystem": "MOD_BUILD",
}
if item: if item:
row = layout.row(align=True) row = layout.row(align=True)
row.label(text=item.name) row.label(text=item.name, icon=system_icons[item.ifc_class])
op = row.operator("bim.assign_system", text="", icon="ADD") row.operator("bim.assign_system", text="", icon="ADD").system = item.ifc_definition_id
op.system = item.ifc_definition_id
+66
View File
@@ -0,0 +1,66 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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/>.
def load_systems(system):
system.import_systems()
system.enable_system_editing_ui()
system.disable_editing_system()
def disable_system_editing_ui(system):
system.disable_editing_system()
system.disable_system_editing_ui()
def add_system(ifc, system, ifc_class=None):
ifc.run("system.add_system", ifc_class=ifc_class)
system.import_systems()
def edit_system(ifc, system_tool, system=None):
attributes = system_tool.export_system_attributes()
ifc.run("system.edit_system", system=system, attributes=attributes)
system_tool.disable_editing_system()
system_tool.import_systems()
def remove_system(ifc, system_tool, system=None):
ifc.run("system.remove_system", system=system)
system_tool.import_systems()
def enable_editing_system(system_tool, system=None):
system_tool.import_system_attributes(system)
system_tool.set_active_system(system)
def disable_editing_system(system):
system.disable_editing_system()
def assign_system(ifc, system=None, product=None):
ifc.run("system.assign_system", product=product, system=system)
def unassign_system(ifc, system=None, product=None):
ifc.run("system.unassign_system", product=product, system=system)
def select_system_products(system_tool, system=None):
system_tool.select_system_products(system)
+12
View File
@@ -315,6 +315,18 @@ class Surveyor:
def get_absolute_matrix(cls, obj): pass def get_absolute_matrix(cls, obj): pass
@interface
class System:
def disable_editing_system(cls): pass
def disable_system_editing_ui(cls): pass
def enable_system_editing_ui(cls): pass
def export_system_attributes(cls): pass
def import_system_attributes(cls, system): pass
def import_systems(cls): pass
def select_system_products(cls, system): pass
def set_active_system(cls, system): pass
@interface @interface
class Type: class Type:
def change_object_data(cls, obj, data, is_global=False): pass def change_object_data(cls, obj, data, is_global=False): pass
@@ -37,5 +37,6 @@ from blenderbim.tool.spatial import Spatial
from blenderbim.tool.structural import Structural from blenderbim.tool.structural import Structural
from blenderbim.tool.style import Style from blenderbim.tool.style import Style
from blenderbim.tool.surveyor import Surveyor from blenderbim.tool.surveyor import Surveyor
from blenderbim.tool.system import System
from blenderbim.tool.type import Type from blenderbim.tool.type import Type
from blenderbim.tool.unit import Unit from blenderbim.tool.unit import Unit
+67
View File
@@ -0,0 +1,67 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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
import ifcopenshell.util.system
import blenderbim.core.tool
import blenderbim.tool as tool
class System(blenderbim.core.tool.System):
@classmethod
def disable_editing_system(cls):
bpy.context.scene.BIMSystemProperties.active_system_id = 0
@classmethod
def disable_system_editing_ui(cls):
bpy.context.scene.BIMSystemProperties.is_editing = False
@classmethod
def enable_system_editing_ui(cls):
bpy.context.scene.BIMSystemProperties.is_editing = True
@classmethod
def export_system_attributes(cls):
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMSystemProperties.system_attributes)
@classmethod
def import_system_attributes(cls, system):
blenderbim.bim.helper.import_attributes2(system, bpy.context.scene.BIMSystemProperties.system_attributes)
@classmethod
def import_systems(cls):
props = bpy.context.scene.BIMSystemProperties
props.systems.clear()
for system in tool.Ifc.get().by_type("IfcSystem"):
if system.is_a() in ["IfcZone", "IfcStructuralAnalysisModel"]:
continue
new = props.systems.add()
new.ifc_definition_id = system.id()
new.name = system.Name or "Unnamed"
new.ifc_class = system.is_a()
@classmethod
def select_system_products(cls, system):
for element in ifcopenshell.util.system.get_system_elements(system):
obj = tool.Ifc.get_object(element)
if obj:
obj.select_set(True)
@classmethod
def set_active_system(cls, system):
bpy.context.scene.BIMSystemProperties.active_system_id = system.id()
+1
View File
@@ -23,6 +23,7 @@ markers =
spatial spatial
structural structural
style style
system
type type
unit unit
void void
@@ -0,0 +1,97 @@
@system
Feature: System
Scenario: Load systems
Given an empty IFC project
When I press "bim.load_systems"
Then nothing happens
Scenario: Disable system editing UI
Given an empty IFC project
When I press "bim.load_systems"
And I press "bim.disable_system_editing_ui"
Then nothing happens
Scenario: Add system
Given an empty IFC project
And I press "bim.load_systems"
When I press "bim.add_system"
Then nothing happens
Scenario: Edit system
Given an empty IFC project
And I press "bim.load_systems"
And I press "bim.add_system"
And the variable "system" is "{ifc}.by_type('IfcSystem')[0].id()"
And I press "bim.enable_editing_system(system={system})"
When I press "bim.edit_system"
Then nothing happens
Scenario: Edit system
Given an empty IFC project
And I press "bim.load_systems"
And I press "bim.add_system"
And the variable "system" is "{ifc}.by_type('IfcSystem')[0].id()"
When I press "bim.remove_system(system={system})"
Then nothing happens
Scenario: Enable editing system
Given an empty IFC project
And I press "bim.load_systems"
And I press "bim.add_system"
And the variable "system" is "{ifc}.by_type('IfcSystem')[0].id()"
When I press "bim.enable_editing_system(system={system})"
Then nothing happens
Scenario: Disable editing system
Given an empty IFC project
And I press "bim.load_systems"
And I press "bim.add_system"
And the variable "system" is "{ifc}.by_type('IfcSystem')[0].id()"
And I press "bim.enable_editing_system(system={system})"
When I press "bim.disable_editing_system"
Then nothing happens
Scenario: Assign system
Given an empty IFC project
And I press "bim.load_systems"
And I press "bim.add_system"
And the variable "system" is "{ifc}.by_type('IfcSystem')[0].id()"
And I press "bim.enable_editing_system(system={system})"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_class" to "IfcPump"
And I press "bim.assign_class"
And the object "IfcPump/Cube" is selected
When I press "bim.assign_system(system={system})"
Then nothing happens
Scenario: Unassign system
Given an empty IFC project
And I press "bim.load_systems"
And I press "bim.add_system"
And the variable "system" is "{ifc}.by_type('IfcSystem')[0].id()"
And I press "bim.enable_editing_system(system={system})"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_class" to "IfcPump"
And I press "bim.assign_class"
And the object "IfcPump/Cube" is selected
And I press "bim.assign_system(system={system})"
When I press "bim.unassign_system(system={system})"
Then nothing happens
Scenario: Select system products
Given an empty IFC project
And I press "bim.load_systems"
And I press "bim.add_system"
And the variable "system" is "{ifc}.by_type('IfcSystem')[0].id()"
And I press "bim.enable_editing_system(system={system})"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_class" to "IfcPump"
And I press "bim.assign_class"
And the object "IfcPump/Cube" is selected
And I press "bim.assign_system(system={system})"
When I press "bim.select_system_products(system={system})"
Then nothing happens
+7
View File
@@ -168,6 +168,13 @@ def surveyor():
prophet.verify() prophet.verify()
@pytest.fixture
def system():
prophet = Prophecy(blenderbim.core.tool.System)
yield prophet
prophet.verify()
@pytest.fixture @pytest.fixture
def type(): def type():
prophet = Prophecy(blenderbim.core.tool.Type) prophet = Prophecy(blenderbim.core.tool.Type)
+90
View File
@@ -0,0 +1,90 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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.core.system as subject
from test.core.bootstrap import ifc, system
class TestLoadSystems:
def test_run(self, system):
system.import_systems().should_be_called()
system.enable_system_editing_ui().should_be_called()
system.disable_editing_system().should_be_called()
subject.load_systems(system)
class TestDisableSystemEditingUI:
def test_run(self, system):
system.disable_editing_system().should_be_called()
system.disable_system_editing_ui().should_be_called()
subject.disable_system_editing_ui(system)
class TestAddSystem:
def test_run(self, ifc, system):
ifc.run("system.add_system", ifc_class="ifc_class").should_be_called()
system.import_systems().should_be_called()
subject.add_system(ifc, system, ifc_class="ifc_class")
class TestEditSystem:
def test_run(self, ifc, system):
system.export_system_attributes().should_be_called().will_return("attributes")
ifc.run("system.edit_system", system="system", attributes="attributes").should_be_called()
system.disable_editing_system().should_be_called()
system.import_systems().should_be_called()
subject.edit_system(ifc, system, system="system")
class TestRemoveSystem:
def test_run(self, ifc, system):
ifc.run("system.remove_system", system="system").should_be_called()
system.import_systems().should_be_called()
subject.remove_system(ifc, system, system="system")
class TestEnableEditingSystem:
def test_run(self, system):
system.import_system_attributes("system").should_be_called()
system.set_active_system("system").should_be_called()
subject.enable_editing_system(system, system="system")
class TestDisableEditingSystem:
def test_run(self, system):
system.disable_editing_system().should_be_called()
subject.disable_editing_system(system)
class TestAssignSystem:
def test_run(self, ifc):
ifc.run("system.assign_system", product="product", system="system").should_be_called()
subject.assign_system(ifc, system="system", product="product")
class TestUnassignSystem:
def test_run(self, ifc):
ifc.run("system.unassign_system", product="product", system="system").should_be_called()
subject.unassign_system(ifc, system="system", product="product")
class TestSelectSystemProducts:
def test_run(self, system):
system.select_system_products("system").should_be_called()
subject.select_system_products(system, system="system")
+152
View File
@@ -0,0 +1,152 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 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
import ifcopenshell
import blenderbim.core.tool
import blenderbim.tool as tool
from test.bim.bootstrap import NewFile
from blenderbim.tool.system import System as subject
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.System)
class TestDisableEditingSystem(NewFile):
def test_run(self):
bpy.context.scene.BIMSystemProperties.active_system_id = 10
subject.disable_editing_system()
assert bpy.context.scene.BIMSystemProperties.active_system_id == 0
class TestDisableSystemEditingUI(NewFile):
def test_run(self):
subject.enable_system_editing_ui()
subject.disable_system_editing_ui()
assert bpy.context.scene.BIMSystemProperties.is_editing is False
class TestEnableSystemEditingUI(NewFile):
def test_run(self):
subject.enable_system_editing_ui()
assert bpy.context.scene.BIMSystemProperties.is_editing is True
class TestExportSystemAttributes(NewFile):
def test_run(self):
TestImportSystemAttributes().test_importing_a_system()
assert subject.export_system_attributes() == {
"GlobalId": "GlobalId",
"Name": "Name",
"Description": "Description",
"ObjectType": "ObjectType",
}
class TestImportSystemAttributes(NewFile):
def test_importing_a_system(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
system = ifc.createIfcSystem()
system.GlobalId = "GlobalId"
system.Name = "Name"
system.Description = "Description"
system.ObjectType = "ObjectType"
subject().import_system_attributes(system)
props = bpy.context.scene.BIMSystemProperties
assert props.system_attributes.get("GlobalId").string_value == "GlobalId"
assert props.system_attributes.get("Name").string_value == "Name"
assert props.system_attributes.get("Description").string_value == "Description"
assert props.system_attributes.get("ObjectType").string_value == "ObjectType"
def test_importing_a_building_system(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
system = ifc.createIfcBuildingSystem()
system.GlobalId = "GlobalId"
system.Name = "Name"
system.Description = "Description"
system.ObjectType = "ObjectType"
system.PredefinedType = "SHADING"
system.LongName = "LongName"
subject().import_system_attributes(system)
props = bpy.context.scene.BIMSystemProperties
assert props.system_attributes.get("GlobalId").string_value == "GlobalId"
assert props.system_attributes.get("Name").string_value == "Name"
assert props.system_attributes.get("Description").string_value == "Description"
assert props.system_attributes.get("ObjectType").string_value == "ObjectType"
assert props.system_attributes.get("PredefinedType").enum_value == "SHADING"
assert props.system_attributes.get("LongName").string_value == "LongName"
def test_importing_a_distribution_system(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
system = ifc.createIfcDistributionSystem()
system.GlobalId = "GlobalId"
system.Name = "Name"
system.Description = "Description"
system.ObjectType = "ObjectType"
system.PredefinedType = "ELECTRICAL"
system.LongName = "LongName"
subject().import_system_attributes(system)
props = bpy.context.scene.BIMSystemProperties
assert props.system_attributes.get("GlobalId").string_value == "GlobalId"
assert props.system_attributes.get("Name").string_value == "Name"
assert props.system_attributes.get("Description").string_value == "Description"
assert props.system_attributes.get("ObjectType").string_value == "ObjectType"
assert props.system_attributes.get("PredefinedType").enum_value == "ELECTRICAL"
assert props.system_attributes.get("LongName").string_value == "LongName"
class TestImportSystems(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
system = ifc.createIfcDistributionSystem()
zone = ifc.createIfcZone()
subject.import_systems()
props = bpy.context.scene.BIMSystemProperties
assert len(props.systems) == 1
assert props.systems[0].ifc_definition_id == system.id()
assert props.systems[0].name == "Unnamed"
assert props.systems[0].ifc_class == "IfcDistributionSystem"
class TestSelectSystemProducts(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcPump")
system = ifcopenshell.api.run("system.add_system", ifc, ifc_class="IfcSystem")
ifcopenshell.api.run("system.assign_system", ifc, product=element, system=system)
obj = bpy.data.objects.new("Object", None)
bpy.context.scene.collection.objects.link(obj)
tool.Ifc.link(element, obj)
subject.select_system_products(system)
assert obj in bpy.context.selected_objects
class TestSetActiveSystem(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc().set(ifc)
system = ifcopenshell.api.run("system.add_system", ifc, ifc_class="IfcSystem")
subject.set_active_system(system)
assert bpy.context.scene.BIMSystemProperties.active_system_id == system.id()
+2 -2
View File
@@ -9,8 +9,8 @@ qa:
.PHONY: license .PHONY: license
license: license:
#copyright-header --license LGPL3 --copyright-holder "Thomas Krijnen <thomas@aecgeeks.com>" --copyright-year "2021" --copyright-software "IfcOpenShell" --copyright-software-description "IFC toolkit and geometry engine" -a ./ -o ./ #copyright-header --license LGPL3 --copyright-holder "Thomas Krijnen <thomas@aecgeeks.com>" --copyright-year "2022" --copyright-software "IfcOpenShell" --copyright-software-description "IFC toolkit and geometry engine" -a ./ -o ./
copyright-header --license LGPL3 --copyright-holder "Dion Moult <dion@thinkmoult.com>" --copyright-year "2021" --copyright-software "IfcOpenShell" --copyright-software-description "IFC toolkit and geometry engine" -a ./ -o ./ copyright-header --license LGPL3 --copyright-holder "Dion Moult <dion@thinkmoult.com>" --copyright-year "2022" --copyright-software "IfcOpenShell" --copyright-software-description "IFC toolkit and geometry engine" -a ./ -o ./
.PHONY: coverage .PHONY: coverage
coverage: coverage:
@@ -23,13 +23,13 @@ import ifcopenshell.api
class Usecase: class Usecase:
def __init__(self, file, **settings): def __init__(self, file, **settings):
self.file = file self.file = file
self.settings = {} self.settings = {"ifc_class": "IfcSystem"}
for key, value in settings.items(): for key, value in settings.items():
self.settings[key] = value self.settings[key] = value
def execute(self): def execute(self):
return self.file.create_entity( return self.file.create_entity(
"IfcSystem", self.settings["ifc_class"],
**{ **{
"GlobalId": ifcopenshell.guid.new(), "GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
@@ -0,0 +1,36 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
def get_system_elements(system):
results = []
for rel in system.IsGroupedBy:
results.extend(rel.RelatedObjects)
return results
def get_element_systems(element):
results = []
for rel in element.HasAssignments:
if rel.is_a("IfcRelAssignsToGroup") and rel.RelatingGroup.is_a() in [
"IfcSystem",
"IfcDistributionSystem",
"IfcBuildingSystem",
]:
results.append(rel.RelatingGroup)
return results
@@ -0,0 +1,28 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import test.bootstrap
import ifcopenshell.api
class TestAddSystem(test.bootstrap.IFC4):
def test_adding_a_system(self):
system = ifcopenshell.api.run("system.add_system", self.file, ifc_class="IfcSystem")
system2 = ifcopenshell.api.run("system.add_system", self.file, ifc_class="IfcDistributionSystem")
assert system.is_a("IfcSystem")
assert system2.is_a("IfcDistributionSystem")
@@ -0,0 +1,27 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import test.bootstrap
import ifcopenshell.api
class TestRemoveSystem(test.bootstrap.IFC4):
def test_removing_a_system(self):
system = ifcopenshell.api.run("system.add_system", self.file, ifc_class="IfcSystem")
ifcopenshell.api.run("system.remove_system", self.file, system=system)
assert len(self.file.by_type("IfcSystem")) == 0
@@ -0,0 +1,47 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import pytest
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.util.system as subject
class TestGetSystemElements(test.bootstrap.IFC4):
def test_run(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcPump")
system = ifcopenshell.api.run("system.add_system", self.file, ifc_class="IfcSystem")
ifcopenshell.api.run("system.assign_system", self.file, product=element, system=system)
assert subject.get_system_elements(system) == [element]
class TestGetElementSystems(test.bootstrap.IFC4):
def test_run(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcPump")
system = ifcopenshell.api.run("system.add_system", self.file, ifc_class="IfcSystem")
ifcopenshell.api.run("system.assign_system", self.file, product=element, system=system)
assert subject.get_element_systems(element) == [system]
def test_do_not_get_non_services_groups(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcPump")
ifcopenshell.api.run("system.assign_system", self.file, product=element, system=self.file.createIfcGroup())
ifcopenshell.api.run("system.assign_system", self.file, product=element, system=self.file.createIfcZone())
ifcopenshell.api.run(
"system.assign_system", self.file, product=element, system=self.file.createIfcStructuralAnalysisModel()
)
assert not subject.get_element_systems(element)