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
classes = (
operator.LoadSystems,
operator.DisableSystemEditingUI,
operator.AddSystem,
operator.EditSystem,
operator.RemoveSystem,
operator.ToggleAssigningSystem,
operator.AssignSystem,
operator.UnassignSystem,
operator.EnableEditingSystem,
operator.DisableEditingSystem,
operator.DisableSystemEditingUI,
operator.EditSystem,
operator.EnableEditingSystem,
operator.LoadSystems,
operator.RemoveSystem,
operator.SelectSystemProducts,
operator.UnassignSystem,
prop.System,
prop.BIMSystemProperties,
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/>.
import bpy
import ifcopenshell.util.attribute
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 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_label = "Load Systems"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMSystemProperties
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"}
def _execute(self, context):
core.load_systems(tool.System)
class DisableSystemEditingUI(bpy.types.Operator):
class DisableSystemEditingUI(bpy.types.Operator, Operator):
bl_idname = "bim.disable_system_editing_ui"
bl_label = "Disable System Editing UI"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMSystemProperties.is_editing = False
context.scene.BIMSystemProperties.active_system_id = 0
return {"FINISHED"}
def _execute(self, context):
core.disable_system_editing_ui(tool.System)
class AddSystem(bpy.types.Operator):
class AddSystem(bpy.types.Operator, Operator):
bl_idname = "bim.add_system"
bl_label = "Add System"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
result = ifcopenshell.api.run("system.add_system", IfcStore.get_file())
Data.load(IfcStore.get_file())
bpy.ops.bim.load_systems()
bpy.ops.bim.enable_editing_system(system=result.id())
return {"FINISHED"}
core.add_system(tool.Ifc, tool.System, ifc_class=context.scene.BIMSystemProperties.system_class)
class EditSystem(bpy.types.Operator):
class EditSystem(bpy.types.Operator, Operator):
bl_idname = "bim.edit_system"
bl_label = "Edit System"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
props = context.scene.BIMSystemProperties
attributes = {}
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}
core.edit_system(
tool.Ifc, tool.System, system=tool.Ifc.get().by_id(context.scene.BIMSystemProperties.active_system_id)
)
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_label = "Remove System"
bl_options = {"REGISTER", "UNDO"}
system: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
props = context.scene.BIMSystemProperties
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"}
core.remove_system(tool.Ifc, tool.System, system=tool.Ifc.get().by_id(self.system))
class EnableEditingSystem(bpy.types.Operator):
class EnableEditingSystem(bpy.types.Operator, Operator):
bl_idname = "bim.enable_editing_system"
bl_label = "Enable Editing System"
bl_options = {"REGISTER", "UNDO"}
system: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMSystemProperties
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"}
def _execute(self, context):
core.enable_editing_system(tool.System, system=tool.Ifc.get().by_id(self.system))
class DisableEditingSystem(bpy.types.Operator):
class DisableEditingSystem(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_system"
bl_label = "Disable Editing System"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMSystemProperties.active_system_id = 0
return {"FINISHED"}
def _execute(self, context):
core.disable_editing_system(tool.System)
class ToggleAssigningSystem(bpy.types.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):
class AssignSystem(bpy.types.Operator, Operator):
bl_idname = "bim.assign_system"
bl_label = "Assign System"
bl_options = {"REGISTER", "UNDO"}
product: bpy.props.StringProperty()
system: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
products = [bpy.data.objects.get(self.product)] if self.product else context.selected_objects
for product in products:
if not product.BIMObjectProperties.ifc_definition_id:
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"}
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if element:
core.assign_system(tool.Ifc, system=tool.Ifc.get().by_id(self.system), product=element)
class UnassignSystem(bpy.types.Operator):
class UnassignSystem(bpy.types.Operator, Operator):
bl_idname = "bim.unassign_system"
bl_label = "Unassign System"
bl_options = {"REGISTER", "UNDO"}
product: bpy.props.StringProperty()
system: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
self.file = IfcStore.get_file()
products = [bpy.data.objects.get(self.product)] if self.product else context.selected_objects
for product in products:
props = product.BIMObjectProperties
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"}
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
if element:
core.unassign_system(tool.Ifc, system=tool.Ifc.get().by_id(self.system), product=element)
class SelectSystemProducts(bpy.types.Operator):
class SelectSystemProducts(bpy.types.Operator, Operator):
bl_idname = "bim.select_system_products"
bl_label = "Select System Products"
bl_options = {"REGISTER", "UNDO"}
system: bpy.props.IntProperty()
def execute(self, context):
for obj in context.visible_objects:
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"}
def _execute(self, context):
core.select_system_products(tool.System, system=tool.Ifc.get().by_id(self.system))
@@ -17,6 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
from blenderbim.bim.module.system.data import SystemData
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
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):
name: StringProperty(name="Name")
ifc_class: StringProperty(name="IFC Class")
ifc_definition_id: IntProperty(name="IFC Definition ID")
@@ -43,3 +51,4 @@ class BIMSystemProperties(PropertyGroup):
systems: CollectionProperty(name="Systems", type=System)
active_system_index: IntProperty(name="Active System Index")
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 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):
@@ -35,15 +35,18 @@ class BIM_PT_systems(Panel):
return IfcStore.get_file()
def draw(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if not SystemData.is_loaded:
SystemData.load()
self.props = context.scene.BIMSystemProperties
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:
row.operator("bim.add_system", text="", icon="ADD")
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:
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
def draw(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if not ObjectSystemData.is_loaded:
ObjectSystemData.load()
self.props = context.scene.BIMSystemProperties
row = self.layout.row(align=True)
if self.props.is_adding:
row.label(text="Adding Systems", icon="OUTLINER")
row.operator("bim.toggle_assigning_system", text="", icon="CANCEL")
if self.props.is_editing:
row = self.layout.row()
row.alignment = "RIGHT"
row.operator("bim.disable_system_editing_ui", text="", icon="CANCEL")
self.layout.template_list(
"BIM_UL_object_systems",
"",
@@ -100,17 +103,23 @@ class BIM_PT_object_systems(Panel):
"active_system_index",
)
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.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.system = system_id
op.system = system["id"]
op = row.operator("bim.unassign_system", text="", icon="X")
op.system = system_id
op.system = system["id"]
if not systems_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):
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:
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
if context.scene.BIMSystemProperties.active_system_id == system_id:
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):
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:
row = layout.row(align=True)
row.label(text=item.name)
op = row.operator("bim.assign_system", text="", icon="ADD")
op.system = item.ifc_definition_id
row.label(text=item.name, icon=system_icons[item.ifc_class])
row.operator("bim.assign_system", text="", icon="ADD").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
@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
class Type:
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.style import Style
from blenderbim.tool.surveyor import Surveyor
from blenderbim.tool.system import System
from blenderbim.tool.type import Type
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
structural
style
system
type
unit
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()
@pytest.fixture
def system():
prophet = Prophecy(blenderbim.core.tool.System)
yield prophet
prophet.verify()
@pytest.fixture
def 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
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 "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 "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 "2022" --copyright-software "IfcOpenShell" --copyright-software-description "IFC toolkit and geometry engine" -a ./ -o ./
.PHONY: coverage
coverage:
@@ -23,13 +23,13 @@ import ifcopenshell.api
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {}
self.settings = {"ifc_class": "IfcSystem"}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
return self.file.create_entity(
"IfcSystem",
self.settings["ifc_class"],
**{
"GlobalId": ifcopenshell.guid.new(),
"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)