Removing or changing spatial containers now supports updating more types of collection organisations

This commit is contained in:
Dion Moult
2021-10-05 14:52:41 +11:00
parent f5913baaca
commit 7c94d4275a
26 changed files with 582 additions and 193 deletions
@@ -188,9 +188,11 @@ class OwnerData:
def get_users(cls):
results = []
for user in tool.Ifc.get().by_type("IfcPersonAndOrganization"):
results.append({
"id": user.id(),
"label": "{} ({})".format(user.ThePerson[0] or "Unnamed", user.TheOrganization[0] or "Unnamed"),
"is_active": bpy.context.scene.BIMOwnerProperties.active_user_id == user.id(),
})
results.append(
{
"id": user.id(),
"label": "{} ({})".format(user.ThePerson[0] or "Unnamed", user.TheOrganization[0] or "Unnamed"),
"is_active": bpy.context.scene.BIMOwnerProperties.active_user_id == user.id(),
}
)
return results
@@ -27,8 +27,8 @@ import ifcopenshell.util.selector
import ifcopenshell.util.representation
import blenderbim.bim.handler
import blenderbim.tool as tool
import blenderbim.core.context as core_context
import blenderbim.core.owner as core_owner
import blenderbim.core.context
import blenderbim.core.owner
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim import import_ifc
@@ -58,10 +58,10 @@ class CreateProject(bpy.types.Operator):
)
self.file = IfcStore.get_file()
person = core_owner.add_person(tool.Ifc)
organisation = core_owner.add_organisation(tool.Ifc)
user = core_owner.add_person_and_organisation(tool.Ifc, person=person, organisation=organisation)
core_owner.set_user(tool.Owner, user=user)
person = blenderbim.core.owner.add_person(tool.Ifc)
organisation = blenderbim.core.owner.add_organisation(tool.Ifc)
user = blenderbim.core.owner.add_person_and_organisation(tool.Ifc, person=person, organisation=organisation)
blenderbim.core.owner.set_user(tool.Owner, user=user)
project = bpy.data.objects.new(self.get_name("IfcProject", "My Project"), None)
site = bpy.data.objects.new(self.get_name("IfcSite", "My Site"), None)
@@ -71,17 +71,19 @@ class CreateProject(bpy.types.Operator):
bpy.ops.bim.assign_class(obj=project.name, ifc_class="IfcProject")
bpy.ops.bim.assign_unit()
model = core_context.add_context(
model = blenderbim.core.context.add_context(
tool.Ifc, context_type="Model", context_identifier="", target_view="", parent=0
)
core_context.add_context(
blenderbim.core.context.add_context(
tool.Ifc, context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model
)
core_context.add_context(
blenderbim.core.context.add_context(
tool.Ifc, context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent=model
)
plan = core_context.add_context(tool.Ifc, context_type="Plan", context_identifier="", target_view="", parent=0)
core_context.add_context(
plan = blenderbim.core.context.add_context(
tool.Ifc, context_type="Plan", context_identifier="", target_view="", parent=0
)
blenderbim.core.context.add_context(
tool.Ifc, context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent=plan
)
@@ -22,6 +22,8 @@ import ifcopenshell.api
import ifcopenshell.util.schema
import ifcopenshell.util.element
import blenderbim.bim.handler
import blenderbim.core.spatial
import blenderbim.tool as tool
from ifcopenshell.api.void.data import Data as VoidData
from blenderbim.bim.ifc import IfcStore
@@ -236,8 +238,13 @@ class AssignClass(bpy.types.Operator):
continue
elif self.file.schema == "IFC2X3" and not element.is_a("IfcSpatialStructureElement"):
continue
bpy.ops.bim.assign_container(
relating_structure=spatial_obj.BIMObjectProperties.ifc_definition_id, related_element=obj.name
blenderbim.core.spatial.assign_container(
tool.Ifc,
tool.Collector,
tool.Container,
tool.Surveyor,
structure_obj=spatial_obj,
element_obj=obj,
)
break
@@ -30,7 +30,7 @@ classes = (
prop.BIMSpatialProperties,
prop.BIMObjectSpatialProperties,
ui.BIM_PT_spatial,
ui.BIM_UL_spatial_elements,
ui.BIM_UL_containers,
)
@@ -0,0 +1,71 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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 blenderbim.tool as tool
import ifcopenshell.util.element
class SpatialData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"is_editing": bpy.context.active_object.BIMObjectSpatialProperties.is_editing,
"parent_container_id": cls.get_parent_container_id(),
"container_id": cls.get_container_id(),
"container_list_settings": cls.get_container_list_settings(),
"is_contained": cls.is_contained(),
"label": cls.get_label(),
}
cls.is_loaded = True
@classmethod
def get_parent_container_id(cls):
container_id = bpy.context.scene.BIMSpatialProperties.active_container_id
if not container_id:
return
container = tool.Ifc.get().by_id(container_id)
if container.is_a("IfcProject"):
return
return container.Decomposes[0].RelatingObject.id()
@classmethod
def get_container_id(cls):
props = bpy.context.scene.BIMSpatialProperties
try:
return props.containers[props.active_container_index].ifc_definition_id
except:
return 0
@classmethod
def get_container_list_settings(cls):
props = bpy.context.scene.BIMSpatialProperties
return ["", props, "containers", props, "active_container_id"]
@classmethod
def is_contained(cls):
return ifcopenshell.util.element.get_container(tool.Ifc.get_entity(bpy.context.active_object))
@classmethod
def get_label(cls):
container = ifcopenshell.util.element.get_container(tool.Ifc.get_entity(bpy.context.active_object))
if container:
return f"{container.is_a()}/{container.Name or ''}"
@@ -19,150 +19,74 @@
import bpy
import ifcopenshell.api
import ifcopenshell.util.element
import blenderbim.tool as tool
import blenderbim.core.spatial as core
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.spatial.data import Data
from blenderbim.bim.module.spatial.prop import getSpatialContainers
from blenderbim.bim.module.spatial.data import SpatialData
class AssignContainer(bpy.types.Operator):
class Operator:
def execute(self, context):
IfcStore.execute_ifc_operator(self, context)
SpatialData.is_loaded = False
return {"FINISHED"}
class AssignContainer(bpy.types.Operator, Operator):
bl_idname = "bim.assign_container"
bl_label = "Assign Container"
bl_options = {"REGISTER", "UNDO"}
relating_structure: bpy.props.IntProperty()
related_element: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
structure: bpy.props.IntProperty()
def _execute(self, context):
# The active object pointer is lost when the object is unlinked from all collections in the view layer
# We need to store it first, then restore it at the end
active_object = context.active_object
self.file = IfcStore.get_file()
related_elements = (
[bpy.data.objects.get(self.related_element)] if self.related_element else context.selected_objects
)
sprops = context.scene.BIMSpatialProperties
relating_structure = (
self.relating_structure or sprops.spatial_elements[sprops.active_spatial_element_index].ifc_definition_id
)
for related_element in related_elements:
oprops = related_element.BIMObjectProperties
if not oprops.ifc_definition_id:
continue
props = related_element.BIMObjectSpatialProperties
ifcopenshell.api.run(
"spatial.assign_container",
self.file,
**{
"product": self.file.by_id(oprops.ifc_definition_id),
"relating_structure": self.file.by_id(relating_structure),
},
structure_obj = tool.Ifc.get_object(tool.Ifc.get().by_id(self.structure))
for element_obj in context.selected_objects:
core.assign_container(
tool.Ifc,
tool.Collector,
tool.Container,
tool.Surveyor,
structure_obj=structure_obj,
element_obj=element_obj,
)
bpy.ops.bim.edit_object_placement(obj=related_element.name)
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
bpy.ops.bim.disable_editing_container(obj=related_element.name)
aggregate_collection = bpy.data.collections.get(related_element.name)
relating_structure_obj = IfcStore.get_element(relating_structure)
relating_collection = None
if relating_structure_obj:
relating_collection = bpy.data.collections.get(relating_structure_obj.name)
if aggregate_collection:
self.remove_collection(context.scene.collection, aggregate_collection)
for collection in bpy.data.collections:
self.remove_collection(collection, aggregate_collection)
relating_collection.children.link(aggregate_collection)
elif relating_collection:
for collection in related_element.users_collection:
collection.objects.unlink(related_element)
relating_collection.objects.link(related_element)
# Restore the active object :
context.view_layer.objects.active = active_object
return {"FINISHED"}
def remove_collection(self, parent, child):
try:
parent.children.unlink(child)
except:
pass
class EnableEditingContainer(bpy.types.Operator):
class EnableEditingContainer(bpy.types.Operator, Operator):
bl_idname = "bim.enable_editing_container"
bl_label = "Enable Editing Container"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.active_object.BIMObjectSpatialProperties.is_editing = True
getSpatialContainers(self, context)
return {"FINISHED"}
def _execute(self, context):
core.enable_editing_container(tool.Container, obj=context.active_object)
class ChangeSpatialLevel(bpy.types.Operator):
class ChangeSpatialLevel(bpy.types.Operator, Operator):
bl_idname = "bim.change_spatial_level"
bl_label = "Change Spatial Level"
bl_options = {"REGISTER", "UNDO"}
parent_id: bpy.props.IntProperty()
parent: bpy.props.IntProperty()
def execute(self, context):
getSpatialContainers(self, context, parent_id=self.parent_id)
return {"FINISHED"}
def _execute(self, context):
core.change_spatial_level(tool.Container, parent=tool.Ifc.get().by_id(self.parent))
class DisableEditingContainer(bpy.types.Operator):
class DisableEditingContainer(bpy.types.Operator, Operator):
bl_idname = "bim.disable_editing_container"
bl_label = "Disable Editing Container"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
obj.BIMObjectSpatialProperties.is_editing = False
return {"FINISHED"}
def _execute(self, context):
core.disable_editing_container(tool.Container, obj=context.active_object)
class RemoveContainer(bpy.types.Operator):
class RemoveContainer(bpy.types.Operator, Operator):
bl_idname = "bim.remove_container"
bl_label = "Remove Container"
bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
active_object = context.active_object
self.file = IfcStore.get_file()
objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
for obj in objs:
obj_id = obj.BIMObjectProperties.ifc_definition_id
if not obj_id:
continue
ifcopenshell.api.run("spatial.remove_container", self.file, **{"product": self.file.by_id(obj_id)})
Data.load(self.file, obj_id)
aggregate_collection = bpy.data.collections.get(obj.name)
if aggregate_collection:
self.remove_collection(context.scene.collection, aggregate_collection)
for collection in bpy.data.collections:
self.remove_collection(collection, spatial_collection)
context.scene.collection.children.link(aggregate_collection)
else:
for collection in obj.users_collection:
collection.objects.unlink(obj)
context.scene.collection.objects.link(obj)
context.view_layer.objects.active = active_object
return {"FINISHED"}
def remove_collection(self, parent, child):
try:
parent.children.unlink(child)
except:
pass
for obj in context.selected_objects:
core.remove_container(tool.Ifc, tool.Collector, obj=obj)
class CopyToContainer(bpy.types.Operator):
@@ -18,8 +18,7 @@
import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from ifcopenshell.api.spatial.data import Data
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.spatial.data import SpatialData
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -33,37 +32,8 @@ from bpy.props import (
)
def getSpatialContainers(self, context, parent_id=None):
Data.load(IfcStore.get_file())
self.file = IfcStore.get_file()
props = context.scene.BIMSpatialProperties
props.spatial_elements.clear()
if parent_id:
props.active_decomposes_id = parent_id
try:
self.file.by_id(props.active_decomposes_id)
except:
props.active_decomposes_id = 0
project_id = self.file.by_type("IfcProject")[0].id()
if not props.active_decomposes_id:
props.active_decomposes_id = project_id
for ifc_definition_id, spatial_element in Data.spatial_elements.items():
if spatial_element["Decomposes"] == props.active_decomposes_id:
new = props.spatial_elements.add()
new.name = spatial_element["Name"] or "Unnamed"
new.long_name = spatial_element["LongName"] or ""
new.has_decomposition = bool(spatial_element["IsDecomposedBy"])
new.ifc_definition_id = ifc_definition_id
if props.active_decomposes_id == project_id:
props.active_decomposes_parent_id = 0
else:
props.active_decomposes_parent_id = Data.spatial_elements[props.active_decomposes_id]["Decomposes"]
def update_active_container_index(self, context):
SpatialData.is_loaded = False
class SpatialElement(PropertyGroup):
@@ -75,10 +45,9 @@ class SpatialElement(PropertyGroup):
class BIMSpatialProperties(PropertyGroup):
spatial_elements: CollectionProperty(name="Spatial Elements", type=SpatialElement)
active_spatial_element_index: IntProperty(name="Active Spatial Element Index")
active_decomposes_id: IntProperty(name="Active Decomposes Id")
active_decomposes_parent_id: IntProperty(name="Active Decomposes Parent Id")
containers: CollectionProperty(name="Containers", type=SpatialElement)
active_container_index: IntProperty(name="Active Container Index", update=update_active_container_index)
active_container_id: IntProperty(name="Active Container Id")
class BIMObjectSpatialProperties(PropertyGroup):
@@ -19,6 +19,7 @@
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.spatial.data import Data
from blenderbim.bim.module.spatial.data import SpatialData
class BIM_PT_spatial(Panel):
@@ -44,44 +45,36 @@ class BIM_PT_spatial(Panel):
return True
def draw(self, context):
oprops = context.active_object.BIMObjectProperties
sprops = context.scene.BIMSpatialProperties
props = context.active_object.BIMObjectSpatialProperties
if not oprops.ifc_definition_id:
return
if oprops.ifc_definition_id not in Data.products:
Data.load(IfcStore.get_file(), oprops.ifc_definition_id)
if not SpatialData.is_loaded:
SpatialData.load()
if props.is_editing:
if SpatialData.data["is_editing"]:
row = self.layout.row(align=True)
if sprops.active_decomposes_parent_id:
if SpatialData.data["parent_container_id"]:
op = row.operator("bim.change_spatial_level", text="", icon="FRAME_PREV")
op.parent_id = sprops.active_decomposes_parent_id
row.operator("bim.assign_container", icon="CHECKMARK")
op.parent = SpatialData.data["parent_container_id"]
row.operator("bim.assign_container", icon="CHECKMARK").structure = SpatialData.data["container_id"]
row.operator("bim.copy_to_container", icon="COPYDOWN", text="")
row.operator("bim.disable_editing_container", icon="CANCEL", text="")
self.layout.template_list(
"BIM_UL_spatial_elements", "", sprops, "spatial_elements", sprops, "active_spatial_element_index"
)
self.layout.template_list("BIM_UL_containers", *SpatialData.data["container_list_settings"])
else:
row = self.layout.row(align=True)
name = "{}/{}".format(
Data.products[oprops.ifc_definition_id]["type"], Data.products[oprops.ifc_definition_id]["Name"]
)
if name == "None/None":
name = "This object is not spatially contained"
row.label(text=name)
row.operator("bim.enable_editing_container", icon="GREASEPENCIL", text="")
row.operator("bim.remove_container", icon="X", text="")
if SpatialData.data["is_contained"]:
row.label(text=SpatialData.data["label"])
row.operator("bim.enable_editing_container", icon="GREASEPENCIL", text="")
row.operator("bim.remove_container", icon="X", text="")
else:
row.label(text="This object is not spatially contained")
row.operator("bim.enable_editing_container", icon="GREASEPENCIL", text="")
class BIM_UL_spatial_elements(UIList):
class BIM_UL_containers(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
if item.has_decomposition:
op = layout.operator("bim.change_spatial_level", text="", icon="DISCLOSURE_TRI_RIGHT", emboss=False)
op.parent_id = item.ifc_definition_id
op.parent = item.ifc_definition_id
layout.label(text=item.name)
layout.label(text=item.long_name)
layout.prop(
@@ -1,3 +1,22 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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 edit_object_placement(ifc, surveyor, obj=None):
element = ifc.get_entity(obj)
if element:
+51
View File
@@ -0,0 +1,51 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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
def assign_container(ifc, collector, container, surveyor, structure_obj=None, element_obj=None):
if not container.can_contain(structure_obj, element_obj):
return
rel = ifc.run(
"spatial.assign_container",
product=ifc.get_entity(element_obj),
relating_structure=ifc.get_entity(structure_obj),
)
blenderbim.core.geometry.edit_object_placement(ifc, surveyor, obj=element_obj)
container.disable_editing(element_obj)
collector.assign(element_obj)
return rel
def enable_editing_container(container, obj=None):
container.enable_editing(obj)
container.import_containers()
def disable_editing_container(container, obj=None):
container.disable_editing(obj)
def change_spatial_level(container, parent=None):
container.import_containers(parent=parent)
def remove_container(ifc, collector, obj=None):
ifc.run("spatial.remove_container", product=ifc.get_entity(obj))
collector.assign(obj)
+5
View File
@@ -83,6 +83,11 @@ class Container(abc.ABC):
def disable_editing(cls, obj):
pass
@classmethod
@abc.abstractmethod
def import_containers(cls, parent=None):
pass
class ContextEditor(abc.ABC):
@classmethod
+2 -1
View File
@@ -18,6 +18,8 @@
from blenderbim.tool.address_editor import AddressEditor
from blenderbim.tool.blender import Blender
from blenderbim.tool.collector import Collector
from blenderbim.tool.container import Container
from blenderbim.tool.context_editor import ContextEditor
from blenderbim.tool.ifc import Ifc
from blenderbim.tool.organisation_editor import OrganisationEditor
@@ -25,4 +27,3 @@ from blenderbim.tool.owner import Owner
from blenderbim.tool.person_editor import PersonEditor
from blenderbim.tool.role_editor import RoleEditor
from blenderbim.tool.surveyor import Surveyor
from blenderbim.tool.container import Container
@@ -0,0 +1,54 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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.element
import blenderbim.core.tool
import blenderbim.tool as tool
class Collector(blenderbim.core.tool.Collector):
@classmethod
def assign(cls, obj):
for collection in obj.users_collection:
collection.objects.unlink(obj)
element = tool.Ifc.get_entity(obj)
aggregate = ifcopenshell.util.element.get_aggregate(element)
if aggregate:
aggregate_obj = tool.Ifc.get_object(aggregate)
collection = bpy.data.collections.get(aggregate_obj.name)
if collection:
collection.objects.link(obj)
return
container = ifcopenshell.util.element.get_container(element)
if container:
container_obj = tool.Ifc.get_object(container)
collection = bpy.data.collections.get(container_obj.name)
if collection:
collection.objects.link(obj)
return
project_obj = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
if project_obj:
collection = bpy.data.collections.get(project_obj.name)
if collection:
collection.objects.link(obj)
return
@@ -1,7 +1,26 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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 blenderbim.core.tool
import blenderbim.tool as tool
class Container(blenderbim.core.tool.Container):
@classmethod
def can_contain(cls, structure_obj, element_obj):
+19
View File
@@ -1,7 +1,26 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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 blenderbim.core.tool
import blenderbim.tool as tool
class Owner(blenderbim.core.tool.Owner):
@classmethod
def set_user(cls, user):
@@ -1,3 +1,21 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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.api
import blenderbim.core.tool
+1
View File
@@ -4,3 +4,4 @@ markers =
geometry
owner
void
spatial
@@ -0,0 +1,30 @@
@spatial
Feature: Spatial
Covers spatial containment management.
Scenario: Enable editing container
Given an empty IFC project
And I add a cube
And the object "Cube" is selected
When I press "bim.enable_editing_container"
Then nothing happens
Scenario: Disable editing container
Given an empty IFC project
And I add a cube
And the object "Cube" is selected
And I press "bim.enable_editing_container"
When I press "bim.disable_editing_container"
Then nothing happens
Scenario: Assign container
Given an empty IFC project
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
And the object "IfcWall/Cube" is selected
And I press "bim.enable_editing_container"
And the variable "site" is "tool.Ifc.get().by_type('IfcSite')[0].id()"
When I press "bim.assign_container(structure={site})"
Then the object "IfcWall/Cube" is in the collection "IfcSite/My Site"
+9 -2
View File
@@ -18,6 +18,7 @@
import os
import bpy
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
from pytest_bdd import scenarios, given, when, then, parsers
@@ -103,8 +104,9 @@ def i_delete_the_selected_objects():
bpy.ops.object.delete()
@then(parsers.parse('the variable "{key}" is "{value}"'))
@given(parsers.parse('the variable "{key}" is "{value}"'))
@when(parsers.parse('the variable "{key}" is "{value}"'))
@then(parsers.parse('the variable "{key}" is "{value}"'))
def the_variable_key_is_value(key, value):
variables[key] = eval(replace_variables(value))
@@ -122,7 +124,7 @@ def the_object_name_exists(name) -> bpy.types.Object:
return obj
@then('an IFC file exists')
@then("an IFC file exists")
def an_ifc_file_exists():
ifc = IfcStore.get_file()
if not ifc:
@@ -255,3 +257,8 @@ def prop_is_value(prop, value):
if not is_value:
actual_value = eval(f"bpy.context.{prop}")
assert False, f"Value is {actual_value}"
@then(parsers.parse('the object "{name}" is in the collection "{collection}"'))
def the_object_name_is_in_the_collection_collection(name, collection):
assert collection in [c.name for c in the_object_name_exists(name).users_collection]
+19
View File
@@ -1,6 +1,25 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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.geometry as subject
from test.core.bootstrap import ifc, surveyor
class TestEditObjectPlacement:
def test_run(self, ifc, surveyor):
ifc.get_entity("obj").should_be_called().will_return("element")
+67
View File
@@ -0,0 +1,67 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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.spatial as subject
from test.core.bootstrap import ifc, collector, container, surveyor
class TestAssignContainer:
def test_run(self, ifc, collector, container, surveyor):
container.can_contain("structure_obj", "element_obj").should_be_called().will_return(True)
ifc.get_entity("structure_obj").should_be_called().will_return("structure")
ifc.get_entity("element_obj").should_be_called().will_return("element")
ifc.run(
"spatial.assign_container", product="element", relating_structure="structure"
).should_be_called().will_return("rel")
surveyor.get_absolute_matrix("element_obj").should_be_called().will_return("matrix")
ifc.run("geometry.edit_object_placement", product="element", matrix="matrix").should_be_called()
container.disable_editing("element_obj").should_be_called()
collector.assign("element_obj").should_be_called()
assert (
subject.assign_container(
ifc, collector, container, surveyor, structure_obj="structure_obj", element_obj="element_obj"
)
== "rel"
)
class TestEnableEditingContainer:
def test_run(self, container):
container.enable_editing("obj").should_be_called()
container.import_containers().should_be_called()
subject.enable_editing_container(container, obj="obj")
class TestDisableEditingContainer:
def test_run(self, container):
container.disable_editing("obj").should_be_called()
subject.disable_editing_container(container, obj="obj")
class TestChangeSpatialLevel:
def test_run(self, container):
container.import_containers(parent="parent").should_be_called()
subject.change_spatial_level(container, parent="parent")
class TestRemoveContainer:
def test_run(self, ifc, collector):
ifc.get_entity("obj").should_be_called().will_return("element")
ifc.run("spatial.remove_container", product="element").should_be_called()
collector.assign("obj").should_be_called()
subject.remove_container(ifc, collector, obj="obj")
@@ -0,0 +1,57 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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 blenderbim.tool.collector import Collector as subject
from test.bim.bootstrap import NewFile
class TestImplementsTool(NewFile):
def test_run(self):
assert isinstance(subject(), blenderbim.core.tool.Collector)
class TestAssign(NewFile):
def test_in_decomposition_mode_walls_are_placed_in_its_spatial_collection(self):
bpy.ops.bim.create_project()
wall_obj = bpy.data.objects.new("Object", None)
wall_element = tool.Ifc.get().createIfcWall()
tool.Ifc.link(wall_element, wall_obj)
bpy.context.scene.collection.objects.link(wall_obj)
ifcopenshell.api.run(
"spatial.assign_container",
tool.Ifc.get(),
product=wall_element,
relating_structure=tool.Ifc.get().by_type("IfcSite")[0],
)
subject.assign(wall_obj)
assert len(wall_obj.users_collection) == 1
assert "IfcSite" in wall_obj.users_collection[0].name
def test_in_decomposition_mode_walls_are_placed_in_the_project_if_not_decomposes(self):
bpy.ops.bim.create_project()
wall_obj = bpy.data.objects.new("Object", None)
wall_element = tool.Ifc.get().createIfcWall()
tool.Ifc.link(wall_element, wall_obj)
bpy.context.scene.collection.objects.link(wall_obj)
subject.assign(wall_obj)
assert len(wall_obj.users_collection) == 1
assert "IfcProject" in wall_obj.users_collection[0].name
@@ -1,3 +1,21 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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
+18
View File
@@ -1,3 +1,21 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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 test.bim.bootstrap
+18
View File
@@ -1,3 +1,21 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2021 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 numpy as np
import ifcopenshell