Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions databpy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .object import (
ObjectTracker,
BlenderObjectBase,
BlenderObjectAttribute,
BlenderObject,
BOB,
create_object,
Expand Down Expand Up @@ -37,6 +38,7 @@
__all__ = [
"ObjectTracker",
"BlenderObjectBase",
"BlenderObjectAttribute",
"BlenderObject",
"BOB",
"create_object",
Expand Down
170 changes: 98 additions & 72 deletions databpy/object.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,20 @@ def get_from_uuid(uuid: str) -> Object:

class BlenderObjectBase:
"""
Minimal base class for Blender objects with attribute access.
Minimal base class for Blender objects with name and object access.

This class provides core functionality for storing and accessing named attributes
on Blender objects. It can be inherited by other packages that need attribute
management without the full BlenderObject feature set.
This provides a minimal set of functionality to persistently track a an object in
Blender's database, providing access to it's name property and also the object itself.
Referencing an object in the database directly can lead to ReferenceErrors as Blender
can _without warning_ alter the database and thus the Object's place in memory.

To get around this BlenderObjectBase always looks up via the name attribute and
double checks with the `uuid` attribute to ensure the correct object is being returned.
If there is a mismatch the entite database will be searched for an object with a uuid
that matches and if none is found a LinkedObjectError will be raised.

Blender _internally_ uses it's own UUID / reference system but this is currently (and
frustratingly) not available to us via the Python API.

Attributes
----------
Expand All @@ -163,12 +172,6 @@ class BlenderObjectBase:
Unique identifier for this object instance.
name : str
Name of the Blender object.
position : AttributeArray
Position attribute of the object's vertices/points.
data : bpy.types.Mesh | bpy.types.Curves | bpy.types.PointCloud
The data block associated with this object.
attributes
Get the attributes collection of the Blender object.
"""

def __init__(self, obj: Object | str | None = None):
Expand Down Expand Up @@ -198,61 +201,6 @@ def __init__(self, obj: Object | str | None = None):
elif obj is None:
self._object_name = ""

def _ipython_key_completions_(self) -> list[str]:
"""
Return possible named attributes for IPython tab completion.

Returns
-------
list[str]
List of attribute names available on this object.
"""
return self.list_attributes()

def __getitem__(self, name: str) -> AttributeArray:
"""
Access a named attribute using dictionary-style syntax.

Parameters
----------
name : str
The name of the attribute to access.

Returns
-------
AttributeArray
An AttributeArray that wraps the named attribute data.

Raises
------
ValueError
If name is not a string.
"""
if not isinstance(name, str):
raise ValueError("Attribute name must be a string")
return AttributeArray(self.object, name)

def __setitem__(self, name: str, data: np.ndarray) -> None:
"""
Set a named attribute using dictionary-style syntax.

Parameters
----------
name : str
The name of the attribute to set.
data : np.ndarray
The data to store in the attribute.
"""
if name in self.list_attributes():
att = Attribute(self.attributes[name])
self.store_named_attribute(
data=data, name=name, domain=att.domain, atype=att.atype
)
self.store_named_attribute(data=data, name=name)

def _check_obj(self) -> None:
_check_obj_attributes(self.object)

@property
def object(self) -> Object:
"""
Expand Down Expand Up @@ -337,6 +285,29 @@ def name(self, value: str) -> None:
obj.name = value
self._object_name = obj.name


class BlenderObjectAttribute(BlenderObjectBase):
"""
Minimal base class for Blender objects with attribute access.

This class provides core functionality for storing and accessing named attributes
on Blender objects.

It is intended for use with Mesh, PointCloud and Curves type objects for easier and
"numpy-like" attribute access.

It can be inherited by other classes for easier attribute management on objects.

Attributes
----------
position : AttributeArray
Position attribute of the object's vertices/points.
data : bpy.types.Mesh | bpy.types.Curves | bpy.types.PointCloud
The data block associated with this object.
attributes
Get the attributes collection of the Blender object.
"""

def store_named_attribute(
self,
data: np.ndarray,
Expand Down Expand Up @@ -401,6 +372,18 @@ def named_attribute(self, name: str, evaluate: bool = False) -> np.ndarray:
self._check_obj()
return attr.named_attribute(self.object, name=name, evaluate=evaluate)

@property
def data(self):
"""
Get the data block of the Blender object.

Returns
-------
bpy.types.Mesh | bpy.types.Curves | bpy.types.PointCloud
The data block associated with this object (e.g., mesh data, curves data, or point cloud data).
"""
return self.object.data

@property
def attributes(self):
"""
Expand Down Expand Up @@ -504,17 +487,60 @@ def __len__(self) -> int:
f"Supported types: Mesh, Curves, PointCloud"
)

@property
def data(self):
def _ipython_key_completions_(self) -> list[str]:
"""
Get the data block of the Blender object.
Return possible named attributes for IPython tab completion.

Returns
-------
bpy.types.Mesh | bpy.types.Curves | bpy.types.PointCloud
The data block associated with this object (e.g., mesh data, curves data, or point cloud data).
list[str]
List of attribute names available on this object.
"""
return self.object.data
return self.list_attributes()

def __getitem__(self, name: str) -> AttributeArray:
"""
Access a named attribute using dictionary-style syntax.

Parameters
----------
name : str
The name of the attribute to access.

Returns
-------
AttributeArray
An AttributeArray that wraps the named attribute data.

Raises
------
ValueError
If name is not a string.
"""
if not isinstance(name, str):
raise ValueError("Attribute name must be a string")
return AttributeArray(self.object, name)

def __setitem__(self, name: str, data: np.ndarray) -> None:
"""
Set a named attribute using dictionary-style syntax.

Parameters
----------
name : str
The name of the attribute to set.
data : np.ndarray
The data to store in the attribute.
"""
if name in self.list_attributes():
att = Attribute(self.attributes[name])
self.store_named_attribute(
data=data, name=name, domain=att.domain, atype=att.atype
)
self.store_named_attribute(data=data, name=name)

def _check_obj(self) -> None:
_check_obj_attributes(self.object)

def evaluate(self) -> Object:
"""
Expand All @@ -528,7 +554,7 @@ def evaluate(self) -> Object:
return evaluate_object(self.object)


class BlenderObject(BlenderObjectBase):
class BlenderObject(BlenderObjectAttribute):
"""
A convenience class for working with Blender objects.

Expand Down
1 change: 1 addition & 0 deletions docs/_quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,6 @@ quartodoc:
- create_bob
- evaluate_object
- BlenderObject
- BlenderObjectAttribute
- BlenderObjectBase
- LinkedObjectError
1 change: 1 addition & 0 deletions docs/api/_sidebar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ website:
- api/create_bob.qmd
- api/evaluate_object.qmd
- api/BlenderObject.qmd
- api/BlenderObjectAttribute.qmd
- api/BlenderObjectBase.qmd
- api/LinkedObjectError.qmd
section: Objects
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "databpy"
version = "0.5.1"
version = "0.6.0"
description = "A data-oriented wrapper library for the Blender Python API"
readme = "README.md"
dependencies = [
Expand Down
Loading