For Developers¶
This page provides detailed documentation for developers working on TraitBlender. For a high-level overview of the architecture, see the Architecture page.
Configuration System
See Architecture: Configuration System for an overview.
Overview¶
The Configuration System manages all scene settings (camera, lighting, world, etc.) with automatic synchronization to Blender objects. It uses a dynamic registration system that allows new config sections to be added without modifying core code.
How It Works¶
Registration Flow¶
- Config Template Creation: Each config section is a class decorated with
@register("section_name") - AST Discovery: The
config_templates/__init__.pyuses AST parsing to find all@registerdecorated classes - Dynamic Class Creation:
configure_traitblender()creates a dynamicTraitBlenderConfigclass with all discovered sections - Property Registration: The dynamic class is registered as
bpy.context.scene.traitblender_config
Dynamic Getters/Setters¶
Config properties use get_property() and set_property() functions that:
- Accept a Blender property path string (e.g., 'bpy.data.worlds["World"].node_tree.nodes["Background"].inputs[0].default_value')
- Check for object dependencies before accessing
- Handle missing objects gracefully (return defaults or do nothing)
- Use eval()/exec() to access/set Blender properties dynamically
YAML Serialization¶
Configs can be serialized to/from YAML:
- to_dict(): Converts config to nested dictionary
- from_dict(): Loads config from dictionary
- __str__(): Returns YAML-formatted string representation
Key Components¶
core/config/register_config.py: BaseTraitBlenderConfigclass and@registerdecoratorcore/config/config_templates/: Individual config section classescore/helpers/prop_gettersetter.py: Dynamic property getters/setterscore/helpers/config_validator.py: Property path validation
Adding a New Config Section¶
- Create a new file in
core/config/config_templates/(e.g.,my_section.py) - Import
registerandTraitBlenderConfig: - Create a class decorated with
@register("section_name"):@register("my_section") class MySectionConfig(TraitBlenderConfig): print_index = 5 # Optional: controls YAML output order my_property: bpy.props.FloatProperty( name="My Property", description="Description", default=1.0, get=get_property('bpy.data.objects["MyObject"].some_property', object_dependencies={"objects": ["MyObject"]}), set=set_property('bpy.data.objects["MyObject"].some_property', object_dependencies={"objects": ["MyObject"]}) ) - The config section will be automatically discovered and registered
Common Pitfalls¶
- Object Dependencies: Always specify
object_dependenciesin getters/setters. Missing objects will cause warnings or errors. - Property Paths: Use exact Blender property paths. Test paths in Blender's Python console first.
- Type Mismatches: Ensure property types match between config and Blender (FloatProperty → float, etc.)
- Circular Dependencies: Avoid config sections that depend on each other
Related Systems¶
- Transform Pipeline: Uses config property paths
- Asset Management: Config files stored in assets/configs/
Scene Assets
See Architecture: Positioning System for an overview.
Overview¶
The Scene Assets system (also called the Positioning System) provides a table-relative coordinate system for consistent object placement. It includes custom properties on all Blender objects and origin calculation functions.
How It Works¶
Table Coordinates (tb_coords)¶
A custom FloatVectorProperty on all objects that represents position relative to the Table's top face center:
- Getter: Calculates object's position in table's local coordinate system
- Setter: Transforms table-relative coordinates to world space and sets object.location
- Coordinate System: Uses table's rotation matrix (unit axes) for consistent orientation
Table Rotation (tb_rotation)¶
A custom FloatVectorProperty for rotation around object's bottom-center pivot:
- Getter: Returns object's current rotation_euler
- Setter: Rotates object around its bottom-center pivot point
- Pivot Calculation: Finds lowest Z vertex, uses that as pivot point
Origin Calculation Functions¶
Functions in core/positioning/origins.py calculate different types of origins:
- OBJECT: Object's actual origin
- GEOM_BOUNDS: Center of bounding box
- MEAN: Mean position of all vertices
- MEDIAN: Median position of all vertices
Key Components¶
ui/properties/table_coords.py:tb_coordsproperty registrationui/properties/table_rotations.py:tb_rotationproperty registrationcore/positioning/get_table_coords.py: Coordinate transformation utilitiescore/positioning/origins.py: Origin calculation functions
Adding a New Origin Type¶
- Create a function in
core/positioning/origins.py: - Add to
PLACEMENT_LOCAL_ORIGINS_TBdictionary: - The new origin will automatically appear in orientation config enums
Common Pitfalls¶
- Table Object Missing:
tb_coordsgetter/setter check for Table object, but operations may fail if Table doesn't exist - Coordinate System: Table coordinates use table's rotation, not world axes. Objects rotate with table.
- Bottom-Center Calculation:
tb_rotationuses lowest Z vertex, which may not be accurate for all object types
Related Systems¶
- Orientations Config: Uses origin functions for positioning
- Morphospace Development: Generated objects use
tb_coordsfor placement
Transform Pipeline
See Architecture: Transform Pipeline for an overview.
Overview¶
The Transform Pipeline applies statistical randomization to scene properties for data augmentation. It uses a registry-based system for sampler functions and maintains undo history.
How It Works¶
Transform Registry¶
Sampler functions are registered using the @register_transform('name') decorator:
- Functions must have type hints on all parameters
- Registry stores factory functions that return configured samplers
- Samplers can be called with parameters to generate random values
Transform Class¶
Each Transform object:
- Stores a property path (e.g., "world.color")
- Stores a sampler name and parameters
- Caches original values in a stack for undo operations
- Can be called to apply the transform or undone to revert
Pipeline Class¶
The TransformPipeline manages multiple transforms:
- Stores transforms in a list
- Executes transforms in sequence when run() is called
- Undoes transforms in reverse order when undo() is called
- Serializes to YAML for persistence
Key Components¶
core/transforms/registry.py: Transform registry and sampler functionscore/transforms/transform_registry_decorator.py:@register_transformdecoratorcore/transforms/transforms.py:Transformclasscore/transforms/pipeline.py:TransformPipelineclasscore/config/config_templates/transforms.py: Config integration
Adding a New Sampler Function¶
- Create a function with type hints in
core/transforms/registry.py: - The sampler will automatically appear in the transform registry
- Use it in transforms:
Common Pitfalls¶
- Type Hints Required: All parameters must have type hints for the decorator to work
- Property Path Validation: Transforms validate property paths exist in config before creation
- Undo Stack: Each transform call adds to the undo stack. Multiple calls = multiple undo steps.
- Serialization: Pipeline state is stored as YAML string in config. Large pipelines may cause performance issues.
Related Systems¶
- Configuration System: Transforms access config properties
- Config Validator: Validates property paths
Morphospace Development
See Architecture: Morphospace System for an overview.
Overview¶
The Morphospace System dynamically loads and executes morphospace modules to generate 3D specimens. Morphospaces are discovered from the filesystem and loaded on-demand.
How It Works¶
Discovery¶
list_morphospaces() scans assets/morphospace_modules/ for directories containing __init__.py:
- Each directory is a morphospace module
- Module name = directory name
- Modules are listed in the UI dropdown
Module Loading¶
When a morphospace is selected:
1. Module path is constructed: assets/morphospace_modules/{name}/
2. __init__.py is loaded using importlib
3. sample function is extracted from the module
4. Function signature is inspected to determine parameters
Parameter Mapping¶
Dataset columns are mapped to function parameters:
- Column names are lowercased and spaces replaced with underscores
- Parameters matching function signature are passed
- name parameter is always the species name
Object Generation¶
The sample() function:
- Receives species name and parameter values
- Generates a 3D object (typically using a morphospace library)
- Returns an object with a to_blender() method
- Object is added to Blender scene with species name
Key Components¶
core/morphospaces/list_morphospaces.py: Discovery functionui/operators/generate_morphospace_sample_operator.py: Module loading and executionassets/morphospace_modules/: Morphospace module storage
Creating a New Morphospace Module¶
- Create a directory in
assets/morphospace_modules/(e.g.,MyMorphospace/) - Create
__init__.pywith asample()function: - The morphospace will automatically appear in the UI dropdown
- Ensure the object returned has a
to_blender()method
Expected Interface¶
Morphospace modules must provide:
- sample(name: str, **params) -> object: Function that generates a specimen
- Returned object: Must have to_blender() method that adds object to Blender scene
Common Pitfalls¶
- Module Import Errors: Ensure all dependencies are available in Blender's Python environment
- Parameter Mismatches: Dataset columns must match function parameter names (case-insensitive, spaces→underscores)
- Object Naming: Generated objects should use the
nameparameter for consistent identification - Blender Integration: Objects must be properly added to scene using
to_blender()method
Related Systems¶
- Dataset Management: Provides data to morphospaces
- Positioning System: Generated objects use
tb_coordsfor placement
Code Structure
See Architecture for system overview and data flow.
Directory Layout¶
TraitBlender/
├── __init__.py # Main addon registration
├── core/ # Core systems
│ ├── config/ # Configuration system
│ │ ├── register_config.py # Base classes and decorator
│ │ └── config_templates/ # Individual config sections
│ ├── datasets/ # Dataset management
│ ├── helpers/ # Utility functions
│ ├── morphospaces/ # Morphospace discovery
│ ├── positioning/ # Table coordinates and origins
│ └── transforms/ # Transform pipeline
├── ui/ # User interface
│ ├── operators/ # Blender operators
│ ├── panels/ # UI panels
│ └── properties/ # Property groups
├── assets/ # Static assets
│ ├── configs/ # Default configs
│ ├── morphospace_modules/ # Morphospace modules
│ ├── objects/ # 3D objects and materials
│ └── scenes/ # Scene files
└── docs/ # Documentation
Import Dependencies¶
- Core systems are independent (config, transforms, datasets, etc.)
- UI depends on core systems
- Helpers are used by multiple systems
- No circular dependencies between major systems
Registration Order¶
Registration occurs in this order (see __init__.py):
1. Config discovery and registration
2. Dataset property group registration
3. UI components (operators, panels, properties)
4. Asset initialization
Module Boundaries¶
- Core: Business logic, no Blender UI dependencies
- UI: Blender-specific UI code, depends on core
- Assets: Static files, loaded at runtime
- Docs: Documentation, no code dependencies
Extension Points¶
See Architecture: Extension Points for high-level overview.
Adding New Features¶
- Config Section: Add file to
core/config/config_templates/ - Transform Sampler: Add function to
core/transforms/registry.py - Morphospace: Add directory to
assets/morphospace_modules/ - Operator: Add file to
ui/operators/, register inui/operators/__init__.py - Panel: Add file to
ui/panels/, register inui/panels/__init__.py - Origin Type: Add function to
core/positioning/origins.py
Related Systems¶
- Architecture: System overview and interactions
- Configuration System: Config structure
- Transform Pipeline: Transform structure
Contributing
Development Workflow¶
- Setup: Install TraitBlender in development mode
- Modify: Make changes to code
- Test: Test in Blender with sample data
- Document: Update relevant documentation
- Commit: Follow commit message conventions
Common Patterns¶
Property Dependency Pattern¶
Always specify object dependencies in config properties:
get=get_property('bpy.data.objects["Camera"].location',
object_dependencies={"objects": ["Camera"]})
Error Handling Pattern¶
Use warnings for missing objects, errors for invalid operations:
if not _check_object_dependencies(object_dependencies):
warnings.warn("Missing objects, using default")
return default_value
Serialization Pattern¶
Both configs and transforms use to_dict()/from_dict():
Troubleshooting¶
Config Properties Not Updating¶
- Check object dependencies exist
- Verify property paths are correct (test in Blender console)
- Check for type mismatches
Transforms Not Working¶
- Verify property path exists in config
- Check sampler function parameters match
- Ensure sampler is registered correctly
Morphospaces Not Appearing¶
- Check directory has
__init__.py - Verify module name matches directory name
- Check for import errors in module
Table Coordinates Not Working¶
- Ensure Table object exists in scene
- Check object has mesh data (for bottom-center calculation)
- Verify table coordinates are set after object creation
Testing Strategies¶
- Unit Tests: Test individual functions (getters/setters, transforms)
- Integration Tests: Test system interactions (config → transform → render)
- Manual Testing: Test in Blender with real data
Related Systems¶
- Architecture: Understanding system interactions
- Code Structure: File organization
- Configuration System: Config patterns
- Transform Pipeline: Transform patterns