Skip to content

Commit

Permalink
Merge pull request #39 from GazzolaLab/feat/documentation
Browse files Browse the repository at this point in the history
Finalized documentation for Pose, Rod, Stack and Simple files
  • Loading branch information
hanson-hschang authored Nov 12, 2024
2 parents afdd564 + 5fde6d4 commit 83f9a97
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 38 deletions.
3 changes: 1 addition & 2 deletions examples/pose_demo.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import bpy
import numpy as np

import bsr
Expand Down Expand Up @@ -53,7 +54,6 @@ def main(
frame_rate: int = 60,
total_time: float = 5.0,
):

# Clear all mesh objects in the new scene
bsr.clear_mesh_objects()

Expand All @@ -76,7 +76,6 @@ def main(
for frame_current, angle in bsr.frame_manager.enumerate(
angles, frame_current_init=frame_start
):

# Define path of of motion for positions of pose object
positions = [np.cos(np.radians(angle)), np.sin(np.radians(angle)), 0.0]

Expand Down
53 changes: 43 additions & 10 deletions src/bsr/geometry/composite/pose.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

class Pose(KeyFrameControlMixin):
"""
Pose class for managing visualization and rendering in Blender
This class provides a mesh interface for Pose objects.
Pose objects are created using given positions and directors.
Parameters
----------
Expand All @@ -26,7 +28,6 @@ class Pose(KeyFrameControlMixin):
directors : NDArray
The directors of the pose. Expected shape is (n_dim, n_dim).
n_dim = 3
"""

input_states = {"positions", "directors"}
Expand All @@ -38,6 +39,9 @@ def __init__(
unit_length: float = 1.0,
thickness_ratio: float = 0.1,
) -> None:
"""
Pose class constructor
"""
# create sphere and cylinder objects
self.spheres: list[Sphere] = []
self.cylinders: list[Cylinder] = []
Expand Down Expand Up @@ -75,17 +79,34 @@ def object(self) -> dict[str, bpy.types.Object]:
@classmethod
def create(cls, states: dict[str, NDArray]) -> "Pose":
"""
Create a Pose object from the given states
States must have the following keys: position(n_dim,), directors(n_dim, n_dim)
Basic factory method to create a new Pose object.
States must have the following keys: positions(n_dim,), directors(n_dim, n_dim)
Parameters
----------
states: dict[str, NDArray]
A dictionary where keys are state names and values are NDArrays.
Returns
-------
Pose
An object of Pose class containing the predefined states
"""
positions = states["positions"]
directors = states["directors"]
return cls(positions, directors)
pose = cls(positions, directors)
return pose

def _build(self, positions: NDArray, directors: NDArray) -> None:
"""
Build the pose object from the given positions and directors
Populates the positions and directors of the Spheres and Cylinders into a Pose Object
Parameters
----------
positions: NDArray
An array of shape (n_dim,) that stores the positions of the Pose object
directors: NDArray
An array of shape (n_dim, n_dim) that stores the directors of the Pose object
"""
# create the sphere object at the positions
sphere = Sphere(
Expand Down Expand Up @@ -114,7 +135,14 @@ def _build(self, positions: NDArray, directors: NDArray) -> None:

def update_states(self, positions: NDArray, directors: NDArray) -> None:
"""
Update the states of the pose object
Update the states of the Pose object
Parameters
----------
positions: NDArray
The positions of the Pose objects. Expected shape is (n_dim,)
directors: NDArray
The directors of the Pose objects. Expected shape is (n_dim, n_dim)
"""
self.spheres[0].update_states(positions)

Expand All @@ -127,7 +155,12 @@ def update_states(self, positions: NDArray, directors: NDArray) -> None:

def update_material(self, **kwargs: dict[str, Any]) -> None:
"""
Updates the material of the pose object
Updates the material of the Pose object
Parameters
----------
kwargs : dict
Keyword arguments for the material update
"""
for shperes in self.spheres:
shperes.update_material(**kwargs)
Expand All @@ -148,7 +181,7 @@ def update_keyframe(self, keyframe: int) -> None:

if TYPE_CHECKING:
data = {
"position": np.array([0.0, 0.0, 0.0]),
"positions": np.array([0.0, 0.0, 0.0]),
"directors": np.array(
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
),
Expand Down
51 changes: 40 additions & 11 deletions src/bsr/geometry/composite/rod.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,24 @@

class RodWithSphereAndCylinder(KeyFrameControlMixin):
"""
Rod class for managing visualization and rendering in Blender
This class provides a mesh interface for Rod objects.
Rod objects are created using given positions and radii.
Parameters
----------
positions : NDArray
The positions of the sphere objects. Expected shape is (n_dim, n_nodes).
The positions of the Rod objects. Expected shape is (n_dim, n_nodes).
n_dim = 3
radii : NDArray
The radii of the sphere objects. Expected shape is (n_nodes-1,).
The radii of the Rod objects. Expected shape is (n_nodes-1,).
"""

input_states = {"positions", "radii"}

def __init__(self, positions: NDArray, radii: NDArray) -> None:
"""
Rod class constructor
"""
# create sphere and cylinder objects
self.spheres: list[Sphere] = []
self.cylinders: list[Cylinder] = []
Expand Down Expand Up @@ -68,14 +71,35 @@ def object(self) -> dict[str, list[bpy.types.Object]]:
@classmethod
def create(cls, states: dict[str, NDArray]) -> "RodWithSphereAndCylinder":
"""
Create a Rod object from the given states
Basic factory method to create a new Rod object.
States must have the following keys: positions(n_dim, n_nodes), radii(n_nodes-1,)
States must have the following keys: positions(n_nodes, 3), radii(n_nodes-1,)
Parameters
----------
states: dict[str, NDArray]
A dictionary where keys are state names and values are NDArrays.
Returns
-------
RodWithSphereAndCylinder
An object of Rod class containing the predefined states
"""
rod = cls(**states)
positions = states["positions"]
radii = states["radii"]
rod = cls(positions, radii)
return rod

def _build(self, positions: NDArray, radii: NDArray) -> None:
"""
Populates the positions and radii of the Spheres and Cylinders into Rod object
Parameters
----------
positions: NDArray
An array of shape (n_dim, n_nodes) that stores the positions of Spheres and Cylinders
radii: NDArray
An array of shape (n_nodes-1,) that stores the radii of the Spheres and Cylinders
"""
_radii = np.concatenate([radii, [0]])
_radii[1:] += radii
_radii[1:-1] /= 2.0
Expand All @@ -95,14 +119,14 @@ def _build(self, positions: NDArray, radii: NDArray) -> None:

def update_states(self, positions: NDArray, radii: NDArray) -> None:
"""
Update the states of the rod object
Update the states of the Rod object
Parameters
----------
positions : NDArray
The positions of the sphere objects. Expected shape is (n_nodes, 3).
The positions of the Rod objects. Expected shape is (n_dim, n_nodes)
radii : NDArray
The radii of the sphere objects. Expected shape is (n_nodes-1,).
The radii of the Rod objects. Expected shape is (n_nodes-1,)
"""
# check shape of positions and radii
assert positions.ndim == 2, "positions must be 2D array"
Expand All @@ -125,7 +149,12 @@ def update_states(self, positions: NDArray, radii: NDArray) -> None:

def update_material(self, **kwargs: dict[str, Any]) -> None:
"""
Updates the material of the rod object
Updates the material of the Rod object
Parameters
----------
kwargs : dict
Keyword arguments for the material update
"""
for shperes in self.spheres:
shperes.update_material(**kwargs)
Expand Down
65 changes: 58 additions & 7 deletions src/bsr/geometry/composite/stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,24 @@

class BaseStack(Sequence, KeyFrameControlMixin):
"""
A stack of objects that can be manipulated together.
Internally, we use a list-like structure to store the objects.
This class provides a mesh interface for a BaseStack of objects.
BaseStacks are created using given positions and radii.
Parameters
----------
positions: NDArray
Positions of each object in the stack. Expected dimension is (n_dim, n_nodes)
n_dim = 3
radii: NDArray
Radii of each object in the stack. Expected dimension is (n_nodes-1,)
"""

DefaultType: Type

def __init__(self) -> None:
"""
Stack class constructor
"""
self._objs: list[BlenderMeshInterfaceProtocol] = []
self._mats: list[BlenderMeshInterfaceProtocol] = []

Expand All @@ -53,14 +64,14 @@ def __len__(self) -> int:
@property
def material(self) -> list[BlenderMeshInterfaceProtocol]:
"""
Returns the materials in the stack.
Returns the list of materials in the stack.
"""
return self._mats

@property
def object(self) -> list[BlenderMeshInterfaceProtocol]:
"""
Returns the objects in the stack.
Returns the list of objects in the stack.
"""
return self._objs

Expand All @@ -77,7 +88,23 @@ def create(
states: dict[str, NDArray],
) -> Self:
"""
Creates a stack of objects from the given states.
Basic factory method to create a new BaseStack of objects.
States must have the following keys: positions(n_dim, n_nodes), radii(n_nodes-1,)
Parameters
----------
states: dict[str, NDArray]
A dictionary where keys are state names and values are NDarrays.
Returns
-------
Self
An instance of the BaseStack with objects containing the states
Raises
------
AssertionError
If the states have differing lengths
"""
self = cls()
keys = states.keys()
Expand All @@ -94,7 +121,13 @@ def create(

def update_states(self, *variables: NDArray) -> None:
"""
Updates the states of the objects.
Updates the states of the BaseStack objects.
Parameters
----------
*variables: NDArray
An array including all the state updates of the object in the stack.
Expected dimension is (n_nodes - 1,)
"""
if not all([v.shape[0] == len(self) for v in variables]):
raise IndexError(
Expand All @@ -105,7 +138,12 @@ def update_states(self, *variables: NDArray) -> None:

def update_material(self, **kwargs: dict[str, NDArray]) -> None:
"""
Updates the material of the objects.
Updates the material of the BaseStack objects
Parameters
----------
kwargs : dict
Keyword arguments for the material update
"""
for material_key, material_values in kwargs.items():
assert isinstance(
Expand All @@ -120,6 +158,19 @@ def update_material(self, **kwargs: dict[str, NDArray]) -> None:


class RodStack(BaseStack):
"""
This class provides a mesh interface for a RodStack of objects (only contains Rod objects).
RodStacks are created using given positions and radii.
Parameters
----------
positions: NDArray
Positions of each Rod in the stack. Expected dimension is (n_dim, n_nodes).
n_dim = 3
radii: NDArray
Radii of each Rod in the stack. Expected dimension is (n_nodes-1,).
"""

input_states = {"positions", "radii"}
DefaultType: Type = Rod

Expand Down
Loading

0 comments on commit 83f9a97

Please sign in to comment.