Skip to content

Commit

Permalink
more readme and new_from_pydata
Browse files Browse the repository at this point in the history
  • Loading branch information
BradyAJohnston committed Dec 19, 2024
1 parent 8d8f8d7 commit 4589889
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 19 deletions.
108 changes: 104 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,109 @@
# `BlenderObject` class (bob)


# databpy

[![codecov](https://codecov.io/gh/BradyAJohnston/databpy/graph/badge.svg?token=KFuu67hzAz)](https://codecov.io/gh/BradyAJohnston/databpy)
![PyPI - Version](https://img.shields.io/pypi/v/databpy.png) ![example
workflow](https://github.com/bradyajohnston/databpy/actions/workflows/tests.yml/badge.svg)
![example
workflow](https://github.com/bradyajohnston/databpy/actions/workflows/ci-cd.yml/badge.svg)

A set of data-oriented wrappers around the python API of Blender.

## Installation
Uses `uv` for project & package management.

```bash
uv pip install -r pyproject.toml --all-extras
```
Available on PyPI, install with pip:

``` bash
pip install databpy
```

## Usage

The main use cases are to create objects, store and retrieve attributes
from them. The functions are named around nodes in Geometry Nodes
`Store Named Attribute` and `Named Attribute`

``` python
import databpy as db

db.store_named_attribute() # store a named attribute on a mesh object
db.named_attribute() # retrieve a named attribute from a mesh object
```

Mostly oriented around creating mesh objects, assigning and getting back
attributes from them. Currently designed around storing and retrieving
`numpy` data types:

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

# Create a mesh object

random_verts = np.random.rand(10, 3)

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

obj.name
```

'RandomMesh.001'

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

array([[0.17452642, 0.45645952, 0.19546226],
[0.81541622, 0.39680132, 0.87869591],
[0.92496204, 0.16035524, 0.97199863],
[0.34075448, 0.5464862 , 0.02298789],
[0.0192579 , 0.35214618, 0.56019056],
[0.49805972, 0.46989357, 0.15057611],
[0.98557359, 0.7825923 , 0.40234712],
[0.1231764 , 0.33543932, 0.66668808],
[0.85444725, 0.76267177, 0.93717819],
[0.37655553, 0.65764296, 0.72231734]])

This is a convenience class that wraps around the `bpy.types.Object`,
and provides access to all of the useful functions. We can wrap an
existing Object or return one when creating a new object.

This just gives us access to the `named_attribute()` and
`store_named_attribute()` functions on the object class, but also
provides a more intuitive way to access the object’s attributes.

``` python
bob = db.BlenderObject(obj) # wraps the existing object
bob = db.create_bob(random_verts) # creates a new object and returns it already wrapped

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

array([[0.17452642, 0.45645952, 0.19546226],
[0.81541622, 0.39680132, 0.87869591],
[0.92496204, 0.16035524, 0.97199863],
[0.34075448, 0.5464862 , 0.02298789],
[0.0192579 , 0.35214618, 0.56019056],
[0.49805972, 0.46989357, 0.15057611],
[0.98557359, 0.7825923 , 0.40234712],
[0.1231764 , 0.33543932, 0.66668808],
[0.85444725, 0.76267177, 0.93717819],
[0.37655553, 0.65764296, 0.72231734]])

We can clear all of the data from the object and initialise a new mesh
underneath:

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

array([[ 1.30187321, -0.8981759 , -0.24894828],
[ 0.0452443 , 0.66260809, 0.02360807],
[-0.04097848, -0.16771786, 3.13003659],
[-0.85789645, -0.51301759, 0.31468081],
[ 0.86041909, -0.50534254, 0.36779848]])
72 changes: 72 additions & 0 deletions README.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
format: gfm
jupyter: python3
---

# databpy
[![codecov](https://codecov.io/gh/BradyAJohnston/databpy/graph/badge.svg?token=KFuu67hzAz)](https://codecov.io/gh/BradyAJohnston/databpy)
![PyPI - Version](https://img.shields.io/pypi/v/databpy)
![example workflow](https://github.com/bradyajohnston/databpy/actions/workflows/tests.yml/badge.svg)
![example workflow](https://github.com/bradyajohnston/databpy/actions/workflows/ci-cd.yml/badge.svg)

A set of data-oriented wrappers around the python API of Blender.

## Installation
Available on PyPI, install with pip:
```bash
pip install databpy
```

## Usage

The main use cases are to create objects, store and retrieve attributes from them. The functions are named around nodes in Geometry Nodes `Store Named Attribute` and `Named Attribute`

```python
import databpy as db

db.store_named_attribute() # store a named attribute on a mesh object
db.named_attribute() # retrieve a named attribute from a mesh object
```

Mostly oriented around creating mesh objects, assigning and getting back attributes from them. Currently designed around storing and retrieving `numpy` data types:

```{python}
import numpy as np
import databpy as db
# Create a mesh object
random_verts = np.random.rand(10, 3)
obj = db.create_object(random_verts, name="RandomMesh")
obj.name
```

```{python}
db.named_attribute(obj, 'position')
```

## `BlenderObject` class (bob)

This is a convenience class that wraps around the `bpy.types.Object`, and provides access to all of the useful functions. We can wrap an existing Object or return one when creating a new object.

This just gives us access to the `named_attribute()` and `store_named_attribute()` functions on the object class, but also provides a more intuitive way to access the object's attributes.

```{python}
bob = db.BlenderObject(obj) # wraps the existing object
bob = db.create_bob(random_verts) # creates a new object and returns it already wrapped
# these two are identical
bob.named_attribute('position')
bob.position
```

We can clear all of the data from the object and initialise a new mesh underneath:

```{python}
bob.new_from_pydata(np.random.randn(5, 3))
bob.position
```
14 changes: 1 addition & 13 deletions databpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
)
from .vdb import import_vdb
import bpy
from .addon import register, unregister
from .utils import centre, lerp
from .attribute import (
named_attribute,
Expand All @@ -19,16 +20,3 @@
Domains,
DomainType,
)


def register():
bpy.types.Object.uuid = bpy.props.StringProperty(
name="UUID",
description="Unique identifier for the object",
default="",
options={"HIDDEN"},
)


def unregister():
del bpy.types.Object.uuid
14 changes: 14 additions & 0 deletions databpy/addon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import bpy


def register():
bpy.types.Object.uuid = bpy.props.StringProperty(
name="UUID",
description="Unique identifier for the object",
default="",
options={"HIDDEN"},
)


def unregister():
del bpy.types.Object.uuid
44 changes: 43 additions & 1 deletion databpy/object.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
Domains,
DomainType,
)
from .addon import register

from uuid import uuid1
from . import attribute as attr
Expand Down Expand Up @@ -41,6 +42,11 @@ def __getitem__(self, key: str) -> Object:
return bpy.data.objects[key]


def _check_obj_is_mesh(obj: Object) -> None:
if not isinstance(obj.data, bpy.types.Mesh):
raise ValueError("Object must be a mesh")


bdo = ObjectDatabase()


Expand Down Expand Up @@ -191,7 +197,13 @@ def object(self, value: Object) -> None:
if not isinstance(value, Object):
raise ValueError(f"{value} must be a bpy.types.Object")

value.uuid = self.uuid
_check_obj_is_mesh(value)

try:
value.uuid = self.uuid
except AttributeError:
register()
value.uuid = self.uuid
self._object_name = value.name

@property
Expand Down Expand Up @@ -224,6 +236,36 @@ def name(self, value: str) -> None:
obj.name = value
self._object_name = obj.name

def new_from_pydata(
self,
vertices: npt.ArrayLike | None = None,
edges: npt.ArrayLike | None = None,
faces: npt.ArrayLike | None = None,
) -> Object:
"""
Create a new Blender object from vertex, edge and face data.
Parameters
----------
vertices : np.ndarray
The vertices of the object.
edges : np.ndarray
The edges of the object.
faces : np.ndarray
The faces of the object.
Returns
-------
Object
The new Blender object.
"""
vertices, edges, faces = [
[] if x is None else x for x in (vertices, edges, faces)
]
self.object.data.clear_geometry()
self.object.data.from_pydata(vertices, edges, faces)
return self.object

def store_named_attribute(
self,
data: np.ndarray,
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.0.1"
version = "0.0.2"
description = "A data-oriented wrapper library for the Blender Python API"
readme = "README.md"
dependencies = ["numpy>=1.26.0,<2.0"]
Expand Down

0 comments on commit 4589889

Please sign in to comment.