界面已本地化;技能介绍与说明文档按所选语言由 AI 自动翻译,代码、命令、路径和专业术语保留原文,以原作者文档为准。
探索技能
效率与协作 · SKILL.md

bonsai-syntax-spatial

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 快照 · 自动分类 · 以原作者文档为准

技能说明

GitHub

name: bonsai-syntax-spatial description: > 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 Bonsai-specific spatial navigation tools. Keywords: spatial structure, IfcSite, IfcBuilding, IfcBuildingStorey, IfcSpace, IfcRelContainedInSpatialStructure, IfcRelAggregates, spatial hierarchy, add storey, create building structure. license: MIT compatibility: Designed for Claude Code. Requires Python 3.x. metadata: author: OpenAEC-Foundation version: "1.0"

Bonsai Spatial Structure Syntax

Version: Bonsai v0.8.x | Blender 4.2.0+ | Python 3.11 Module path: bonsai.* — NEVER blenderbim.* Source files: core/spatial.py, tool/spatial.py, bim/module/spatial/

Critical Warnings

  1. ALWAYS use bonsai.* imports. NEVER use blenderbim.* — renamed in 2024 (v0.8.0+).
  2. ALWAYS build spatial hierarchy via IfcRelAggregates using ifcopenshell.api.run("aggregate.assign_object", ...). NEVER create spatial elements without connecting them to the hierarchy.
  3. ALWAYS use ifcopenshell.api.run("spatial.assign_container", ...) to place physical elements in spatial containers. NEVER set containment via direct attribute manipulation.
  4. ALWAYS pass products as a list (v0.8+ breaking change): products=[wall], NOT product=wall.
  5. NEVER delete IfcProject — it is the root of every IFC model. Bonsai operators enforce this constraint.
  6. ALWAYS set a default container (typically IfcBuildingStorey) before creating elements. Use bpy.ops.bim.set_default_container(container=storey_id).
  7. NEVER assign an element to multiple containers via IfcRelContainedInSpatialStructure — an element can only be contained in ONE spatial structure. Use IfcRelReferencedInSpatialStructure for multi-storey references.
  8. ALWAYS use ifcopenshell.api.run("root.create_entity", ...) to create spatial elements. NEVER use model.create_entity() — it skips GlobalId generation and ownership assignment.

IFC Spatial Hierarchy

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)

Spatial Elements

IFC ClassPurposeRequired?Typical Count
IfcProjectRoot of modelYES (exactly 1)1
IfcSiteGeographic land parcelRecommended1+
IfcBuildingPhysical building structureRecommended1+
IfcBuildingStoreyFloor levelRecommended1+ per building
IfcSpaceRoom, corridor, voidOptional0+ per storey

Three Key Relationships

RelationshipIFC ClassPurposeCardinality
Spatial decompositionIfcRelAggregatesParent-child between spatial elements1:N
Spatial containmentIfcRelContainedInSpatialStructurePlaces physical elements in a spatial zone1:N (exclusive)
Spatial referenceIfcRelReferencedInSpatialStructureNon-exclusive reference (multi-storey elements)M:N

Decision Tree: Spatial Operations

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()

Essential Patterns

Pattern 1: Build Complete Spatial Hierarchy

# 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)

Pattern 2: Assign Element to Container

# 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)

Pattern 3: Multi-Storey Element Reference

# 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)

Pattern 4: Query Spatial Information

# 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

Pattern 5: Bonsai Operators for Spatial Operations

# 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)

Bonsai Three-Layer Architecture (Spatial Module)

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

Operator Execution Pattern

# 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"}

Key Rules

  1. Operators call IfcStore.execute_ifc_operator(self, context) — wraps IFC ops with undo.
  2. Actual logic goes in _execute(), NOT execute().
  3. Tool classes (tool.Ifc, tool.Spatial) are passed as dependency injection.
  4. bl_options ALWAYS includes "UNDO" for IFC-modifying operators.

Spatial Validation Rules

Containment Validation (can_contain)

An element can be contained in a spatial structure element ONLY if:

  1. The container is an IfcSpatialStructureElement (IFC2X3) or also IfcExternalSpatialStructureElement (IFC4+)
  2. The element has a ContainedInStructure inverse attribute

Reference Validation (can_reference)

An element can reference a spatial structure ONLY if:

  1. The structure is an IfcSpatialStructureElement (IFC2X3) or IfcSpatialElement (IFC4+)
  2. The element has a ReferencedInStructures inverse attribute

Default Project Structure

When 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".

Space Generation

Bonsai provides two automated space generation methods:

From Context (Surrounding Walls)

  1. Set default container (an IfcBuildingStorey)
  2. Position cursor or select an object at the space location
  3. Call bpy.ops.bim.generate_space()
  4. Bonsai bisects visible walls/columns at storey elevation, polygonizes boundaries, finds the enclosing polygon, and extrudes it to create an IfcSpace

From Selected Walls

  1. Select wall objects that enclose spaces
  2. Call bpy.ops.bim.generate_spaces_from_walls()
  3. Bonsai computes the union of wall footprints and creates IfcSpace elements from interior holes

IFC Schema Version Differences

Entity / FeatureIFC2X3IFC4IFC4X3
IfcSiteYesYesYes
IfcBuildingYesYesYes
IfcBuildingStoreyYesYesYes
IfcSpaceYesYesYes
IfcFacilityNoNoYes
IfcFacilityPartNoNoYes
IfcSpatialElement (base class)NoYesYes
IfcExternalSpatialStructureElementNoYesYes
Containment validation classIfcSpatialStructureElement+ IfcExternalSpatialStructureElement+ IfcExternalSpatialStructureElement
Reference validation classIfcSpatialStructureElementIfcSpatialElementIfcSpatialElement

Source Code Paths

ComponentPath in IfcOpenShell Monorepo
Core logicsrc/bonsai/bonsai/core/spatial.py
Tool implementationsrc/bonsai/bonsai/tool/spatial.py
Operatorssrc/bonsai/bonsai/bim/module/spatial/operator.py
UI panelssrc/bonsai/bonsai/bim/module/spatial/ui.py
Propertiessrc/bonsai/bonsai/bim/module/spatial/prop.py
Interface contractsrc/bonsai/bonsai/core/tool.py (class Spatial)

Dependencies

  • ifcos-syntax-api — For ifcopenshell.api.run() invocation patterns and module reference
  • bonsai-core-architecture — For three-layer architecture, IfcStore, tool.Ifc patterns

Reference Links

我的收藏