计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
bonsai-syntax-spatial【效率与协作】Use when creating or managing IFC spatial hierarchy in Bonsai -- IfcSite, IfcBuilding, IfcBuildingStorey, IfcSpace, and their containment relationships. Prevents the critical mistake of not establishing proper spatial decomposition (IfcRelAggregates) causing elements to be invisible in model viewers. Covers spatial containment, decomposition, and Bon…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Version: Bonsai v0.8.x | Blender 4.2.0+ | Python 3.11 Module path:
bonsai.*— NEVERblenderbim.*Source files:core/spatial.py,tool/spatial.py,bim/module/spatial/
bonsai.* imports. NEVER use blenderbim.* — renamed in 2024 (v0.8.0+).IfcRelAggregates using ifcopenshell.api.run("aggregate.assign_object", ...). NEVER create spatial elements without connecting them to the hierarchy.ifcopenshell.api.run("spatial.assign_container", ...) to place physical elements in spatial containers. NEVER set containment via direct attribute manipulation.products as a list (v0.8+ breaking change): products=[wall], NOT product=wall.IfcProject — it is the root of every IFC model. Bonsai operators enforce this constraint.IfcBuildingStorey) before creating elements. Use bpy.ops.bim.set_default_container(container=storey_id).IfcRelContainedInSpatialStructure — an element can only be contained in ONE spatial structure. Use IfcRelReferencedInSpatialStructure for multi-storey references.ifcopenshell.api.run("root.create_entity", ...) to create spatial elements. NEVER use model.create_entity() — it skips GlobalId generation and ownership assignment.IfcProject (exactly 1 per file, root node)
└── IfcSite (via IfcRelAggregates)
└── IfcBuilding (via IfcRelAggregates)
└── IfcBuildingStorey (via IfcRelAggregates)
├── IfcSpace (via IfcRelAggregates)
├── IfcWall (via IfcRelContainedInSpatialStructure)
├── IfcDoor (via IfcRelContainedInSpatialStructure)
└── IfcColumn (via IfcRelContainedInSpatialStructure)
| IFC Class | Purpose | Required? | Typical Count |
|---|---|---|---|
IfcProject | Root of model | YES (exactly 1) | 1 |
IfcSite | Geographic land parcel | Recommended | 1+ |
IfcBuilding | Physical building structure | Recommended | 1+ |
IfcBuildingStorey | Floor level | Recommended | 1+ per building |
IfcSpace | Room, corridor, void | Optional | 0+ per storey |
| Relationship | IFC Class | Purpose | Cardinality |
|---|---|---|---|
| Spatial decomposition | IfcRelAggregates | Parent-child between spatial elements | 1:N |
| Spatial containment | IfcRelContainedInSpatialStructure | Places physical elements in a spatial zone | 1:N (exclusive) |
| Spatial reference | IfcRelReferencedInSpatialStructure | Non-exclusive reference (multi-storey elements) | M:N |
What spatial operation do you need?
│
├── Create spatial hierarchy (Project/Site/Building/Storey)?
│ ├── Create elements: root.create_entity with ifc_class="IfcSite" etc.
│ └── Connect hierarchy: aggregate.assign_object
│
├── Place physical element (wall/door/column) in a storey or space?
│ └── spatial.assign_container (products=[element], relating_structure=storey)
│
├── Remove element from container?
│ └── spatial.unassign_container (products=[element])
│
├── Reference element in additional storey (multi-storey column)?
│ └── spatial.reference_structure (products=[element], relating_structure=storey2)
│
├── Remove spatial reference?
│ └── spatial.dereference_structure (products=[element], relating_structure=storey2)
│
├── Query spatial containment?
│ └── ifcopenshell.util.element.get_container(element)
│
├── Query spatial decomposition?
│ └── ifcopenshell.util.element.get_decomposition(container)
│
├── Delete spatial container?
│ └── bpy.ops.bim.delete_container(container=container_id)
│ NOTE: Cannot delete IfcProject
│
└── Generate IfcSpace from walls?
├── From cursor/object position: bpy.ops.bim.generate_space()
└── From selected walls: bpy.ops.bim.generate_spaces_from_walls()
# Bonsai v0.8.x: Build spatial hierarchy from scratch
# Requires: active Bonsai project in Blender
import bpy
import ifcopenshell
import ifcopenshell.api
model = ifcopenshell.file(schema="IFC4")
# Step 1: Create project (root)
project = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcProject", name="My Project")
# Step 2: Assign units (required before geometry)
ifcopenshell.api.run("unit.assign_unit", model)
# Step 3: Create spatial elements
site = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcSite", name="Default Site")
building = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcBuilding", name="Building A")
ground_floor = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcBuildingStorey", name="Ground Floor")
first_floor = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcBuildingStorey", name="First Floor")
# Step 4: Connect hierarchy via IfcRelAggregates
ifcopenshell.api.run("aggregate.assign_object", model,
products=[site], relating_object=project)
ifcopenshell.api.run("aggregate.assign_object", model,
products=[building], relating_object=site)
ifcopenshell.api.run("aggregate.assign_object", model,
products=[ground_floor, first_floor], relating_object=building)
# Bonsai v0.8.x: Place wall in a storey
wall = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcWall", name="Wall 001")
# Assign to ground floor (IfcRelContainedInSpatialStructure)
ifcopenshell.api.run("spatial.assign_container", model,
products=[wall], relating_structure=ground_floor)
# Bonsai v0.8.x: Column spanning two storeys
column = ifcopenshell.api.run("root.create_entity", model,
ifc_class="IfcColumn", name="Column C1")
# Primary containment in ground floor
ifcopenshell.api.run("spatial.assign_container", model,
products=[column], relating_structure=ground_floor)
# Additional reference in first floor (IfcRelReferencedInSpatialStructure)
ifcopenshell.api.run("spatial.reference_structure", model,
products=[column], relating_structure=first_floor)
# Bonsai v0.8.x: Query spatial relationships
import ifcopenshell.util.element
# Get container of an element
container = ifcopenshell.util.element.get_container(wall)
# Returns: IfcBuildingStorey or None
# Get all elements in a container
elements = ifcopenshell.util.element.get_decomposition(building)
# Returns: list of child spatial elements
# Get all parts (direct children via IfcRelAggregates)
parts = ifcopenshell.util.element.get_parts(building)
# Returns: list of IfcBuildingStorey instances
# Bonsai v0.8.x: Using Bonsai operators (inside Blender)
import bpy
# Set default container for new elements
bpy.ops.bim.set_default_container(container=storey.id())
# Assign selected objects to a container
bpy.ops.bim.assign_container(container=storey.id())
# Remove container assignment from selected objects
bpy.ops.bim.remove_container()
# Reference selected objects from a structure
bpy.ops.bim.reference_structure()
# Delete a spatial container (NOT IfcProject)
bpy.ops.bim.delete_container(container=storey.id())
# Copy selected objects to another container
bpy.ops.bim.copy_to_container(container=target_storey.id())
# Generate IfcSpace from surrounding walls
bpy.ops.bim.generate_space()
# Generate spaces from selected walls
bpy.ops.bim.generate_spaces_from_walls()
# Load spatial decomposition tree in UI
bpy.ops.bim.import_spatial_decomposition()
# Select all objects in a container
bpy.ops.bim.select_similar_container(is_recursive=True)
# Toggle IfcSpace visibility
bpy.ops.bim.toggle_spatial_elements(is_visible=True)
DELIVERY LAYER — bim/module/spatial/operator.py
BIM_OT_assign_container, BIM_OT_delete_container, etc.
Calls: core.spatial.assign_container(tool.Ifc, tool.Spatial, ...)
─────────────────────────────────────────────────────────
DOMAIN LAYER — core/spatial.py
assign_container(), reference_structure(), generate_space(), etc.
Validates via: spatial.can_contain(), spatial.can_reference()
Calls: ifc.run("spatial.assign_container", ...)
─────────────────────────────────────────────────────────
TOOL LAYER — tool/spatial.py
Spatial.get_container(), Spatial.can_contain(), etc.
Concrete implementation using bpy + ifcopenshell
# bim/module/spatial/operator.py (Delivery Layer)
class BIM_OT_assign_container(bpy.types.Operator):
bl_idname = "bim.assign_container"
bl_label = "Assign Container"
bl_options = {"REGISTER", "UNDO"}
container: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
# Dependency injection: pass tool implementations to core
core.spatial.assign_container(
tool.Ifc, tool.Collector, tool.Spatial,
container=tool.Ifc.get().by_id(self.container),
objs=context.selected_objects,
)
return {"FINISHED"}
IfcStore.execute_ifc_operator(self, context) — wraps IFC ops with undo._execute(), NOT execute().tool.Ifc, tool.Spatial) are passed as dependency injection.bl_options ALWAYS includes "UNDO" for IFC-modifying operators.can_contain)An element can be contained in a spatial structure element ONLY if:
IfcSpatialStructureElement (IFC2X3) or also IfcExternalSpatialStructureElement (IFC4+)ContainedInStructure inverse attributecan_reference)An element can reference a spatial structure ONLY if:
IfcSpatialStructureElement (IFC2X3) or IfcSpatialElement (IFC4+)ReferencedInStructures inverse attributeWhen creating a new IFC project via Bonsai UI (File > New IFC Project), Bonsai generates:
IfcProject "My Project"
└── IfcSite "My Site"
└── IfcBuilding "My Building"
└── IfcBuildingStorey "My Storey" (Elevation: 0.0)
The default container is set to IfcBuildingStorey "My Storey".
Bonsai provides two automated space generation methods:
IfcBuildingStorey)bpy.ops.bim.generate_space()IfcSpacebpy.ops.bim.generate_spaces_from_walls()IfcSpace elements from interior holes| Entity / Feature | IFC2X3 | IFC4 | IFC4X3 |
|---|---|---|---|
IfcSite | Yes | Yes | Yes |
IfcBuilding | Yes | Yes | Yes |
IfcBuildingStorey | Yes | Yes | Yes |
IfcSpace | Yes | Yes | Yes |
IfcFacility | No | No | Yes |
IfcFacilityPart | No | No | Yes |
IfcSpatialElement (base class) | No | Yes | Yes |
IfcExternalSpatialStructureElement | No | Yes | Yes |
| Containment validation class | IfcSpatialStructureElement | + IfcExternalSpatialStructureElement | + IfcExternalSpatialStructureElement |
| Reference validation class | IfcSpatialStructureElement | IfcSpatialElement | IfcSpatialElement |
| Component | Path in IfcOpenShell Monorepo |
|---|---|
| Core logic | src/bonsai/bonsai/core/spatial.py |
| Tool implementation | src/bonsai/bonsai/tool/spatial.py |
| Operators | src/bonsai/bonsai/bim/module/spatial/operator.py |
| UI panels | src/bonsai/bonsai/bim/module/spatial/ui.py |
| Properties | src/bonsai/bonsai/bim/module/spatial/prop.py |
| Interface contract | src/bonsai/bonsai/core/tool.py (class Spatial) |
ifcopenshell.api.run() invocation patterns and module reference