Metadata-Version: 2.3
Name: databpy
Version: 0.11.0
Summary: A data-oriented wrapper library for the Blender Python API
Keywords: blender,python,numpy
Requires-Dist: numpy
Requires-Dist: bpy>=5.1 ; extra == 'bpy'
Maintainer: Brady Johnston
Maintainer-email: Brady Johnston <brady.johnston@me.com>
Requires-Python: ~=3.13.0
Project-URL: Documentation, https://bradyajohnston.github.io/databpy
Project-URL: Homepage, https://bradyajohnston.github.io/databpy
Project-URL: Repository, https://github.com/bradyajohnston/databpy
Provides-Extra: bpy
Description-Content-Type: text/markdown

# databpy


[![codecov](https://codecov.io/gh/BradyAJohnston/databpy/graph/badge.svg?token=KFuu67hzAz)](https://codecov.io/gh/BradyAJohnston/databpy)
[![pypi](https://img.shields.io/pypi/v/databpy.png)](https://pypi.org/project/databpy/)
![deployment](https://github.com/bradyajohnston/databpy/actions/workflows/ci-cd.yml/badge.svg)
[![CodSpeed](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://app.codspeed.io/BradyAJohnston/databpy?utm_source=badge)

| Blender | Tests |
|----|----|
| 5.1 | [![Blender 5.1](https://github.com/BradyAJohnston/databpy/actions/workflows/tests-5.1.yml/badge.svg)](https://github.com/BradyAJohnston/databpy/actions/workflows/tests-5.1.yml) |
| 5.2 | [![Blender 5.2](https://github.com/BradyAJohnston/databpy/actions/workflows/tests-5.2.yml/badge.svg)](https://github.com/BradyAJohnston/databpy/actions/workflows/tests-5.2.yml) |
| 5.3 (beta) | [![Blender 5.3](https://github.com/BradyAJohnston/databpy/actions/workflows/tests-5.3.yml/badge.svg)](https://github.com/BradyAJohnston/databpy/actions/workflows/tests-5.3.yml) |
| 5.4 (alpha) | [![Blender 5.4](https://github.com/BradyAJohnston/databpy/actions/workflows/tests-5.4.yml/badge.svg)](https://github.com/BradyAJohnston/databpy/actions/workflows/tests-5.4.yml) |

![](docs/img/notdavid.png)

Data-oriented wrappers around Blender’s Python API, which read and write
the attributes of mesh, curves and point cloud objects as NumPy arrays.

databpy started inside [Molecular
Nodes](https://github.com/BradyAJohnston/MolecularNodes) and was split
into a separate Python package for use in other projects.

## Installation

databpy supports Blender 5.1 and later, which use Python 3.13. Install
it from PyPI with pip:

``` bash
pip install databpy
```

> [!CAUTION]
>
> `bpy` (Blender as a Python module) is an optional dependency, so
> installing `databpy` inside Blender doesn’t install a second copy of
> `bpy`. To use databpy outside Blender, install `bpy` as well with
> either of these commands:
>
> ``` bash
> # install with the bpy dependency
> pip install "databpy[bpy]"
>
> # install both packages
> pip install databpy bpy
> ```

## Usage

databpy creates objects, and stores and retrieves their attributes.
[`store_named_attribute()`](https://bradyajohnston.github.io/databpy/api/store_named_attribute.html)
and
[`named_attribute()`](https://bradyajohnston.github.io/databpy/api/named_attribute.html)
are named after the `Store Named Attribute` and `Named Attribute` nodes
in Geometry Nodes, and work on mesh, curves and point cloud objects.

To store an attribute on a new object:

``` python
import numpy as np
import databpy as db

coords = np.array([[0, 0, 0], [0, 5, 0], [5, 0, 0], [5, 5, 0]])

obj = db.create_object(coords, name="Box")
db.store_named_attribute(obj, np.array([10, 20, 31, 42]), "vals")
```

![image](https://github.com/user-attachments/assets/2af6046a-8d73-4881-af63-8ed175fe2136.png)

Attributes are stored and retrieved as NumPy arrays:

``` python
import numpy as np
import databpy as db

np.random.seed(6)

# Create a mesh object
random_verts = np.random.rand(10, 3)

obj = db.create_object(random_verts, name="RandomMesh")

obj.name
```

    'RandomMesh'

Read an attribute from the object’s mesh:

``` python
db.named_attribute(obj, "position")
```

    array([[0.8928602 , 0.3319798 , 0.8212291 ],
           [0.04169663, 0.10765668, 0.59505206],
           [0.52981734, 0.41880742, 0.33540785],
           [0.62251943, 0.43814144, 0.7358821 ],
           [0.5180364 , 0.5788586 , 0.6453551 ],
           [0.99022424, 0.8198582 , 0.41320094],
           [0.8762677 , 0.82375944, 0.05447451],
           [0.7186372 , 0.8021706 , 0.7364066 ],
           [0.7091318 , 0.5409368 , 0.12482417],
           [0.9576473 , 0.4032563 , 0.21695116]], dtype=float32)

### `BlenderObject` class (bob)

`BlenderObject` wraps a `bpy.types.Object` and provides databpy’s
functions as methods. Wrap an existing object, or create a new one with
`create_bob()`.

Attributes can be read with `named_attribute()`, or as properties and
items such as `bob.position` and `bob["position"]`.

``` python
# wrap an existing object or create a new one
bob = db.BlenderObject(obj)
bob = db.create_bob(random_verts)

# these two are identical
bob.named_attribute("position")
bob.position
```

    AttributeArray(name='position', object='NewObject', mesh='NewObject', domain=POINT, type=FLOAT_VECTOR, shape=(10, 3), dtype=float32)
    array([[0.8928602 , 0.3319798 , 0.8212291 ],
           [0.04169663, 0.10765668, 0.59505206],
           [0.52981734, 0.41880742, 0.33540785],
           [0.62251943, 0.43814144, 0.7358821 ],
           [0.5180364 , 0.5788586 , 0.6453551 ],
           [0.99022424, 0.8198582 , 0.41320094],
           [0.8762677 , 0.82375944, 0.05447451],
           [0.7186372 , 0.8021706 , 0.7364066 ],
           [0.7091318 , 0.5409368 , 0.12482417],
           [0.9576473 , 0.4032563 , 0.21695116]], dtype=float32)

`new_from_pydata()` clears the geometry of the object’s mesh and fills
it with new vertices, edges and faces:

``` python
bob.new_from_pydata(np.random.randn(5, 3))
bob.position
```

    AttributeArray(name='position', object='NewObject', mesh='NewObject', domain=POINT, type=FLOAT_VECTOR, shape=(5, 3), dtype=float32)
    array([[ 0.82465386, -1.1764315 ,  1.5644896 ],
           [ 0.7127051 , -0.1810066 ,  0.53419954],
           [-0.58661294, -1.4818532 ,  0.8572476 ],
           [ 0.94309896,  0.11444143, -0.02195668],
           [-2.1271446 , -0.83440745, -0.4655083 ]], dtype=float32)

## Example with Polars data

``` python
import polars as pl
import databpy as db
from io import StringIO

json_file = StringIO("""
{
  "Dino": [
    [55.3846, 97.1795, 0.0],
    [51.5385, 96.0256, 0.0]
  ],
  "Star": [
    [58.2136, 91.8819, 0.0],
    [58.1961, 92.215, 0.0]
  ]
}
""")

df = pl.read_json(json_file)
columns_to_explode = [col for col in df.columns if df[col].dtype == pl.List(pl.List)]
df = df.explode(columns_to_explode, empty_as_null=True)

vertices = np.zeros((len(df), 3), dtype=np.float32)
bob = db.create_bob(vertices, name="DinoStar")

for col in df.columns:
    data = np.vstack(df.get_column(col).to_numpy())
    bob.store_named_attribute(data, col)

bob.named_attribute("Dino")
```

    array([[55.3846, 97.1795,  0.    ],
           [51.5385, 96.0256,  0.    ]], dtype=float32)

``` python
bob.named_attribute("Star")
```

    array([[58.2136, 91.8819,  0.    ],
           [58.1961, 92.215 ,  0.    ]], dtype=float32)
