Skip to content

Commit

Permalink
Merge pull request #958 from m-agour/Adding-box-actor
Browse files Browse the repository at this point in the history
Adding box actor
  • Loading branch information
skoudoro authored Jan 9, 2025
2 parents 2b13e5e + 0508366 commit cb4637e
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 43 deletions.
90 changes: 90 additions & 0 deletions fury/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,93 @@ def sphere(
obj.local.position = centers[0]
obj.prim_count = prim_count
return obj


def box(
centers,
*,
directions=(1, 0, 0),
colors=(1, 0, 0),
scales=(1, 1, 1),
opacity=None,
material="phong",
enable_picking=True,
detailed=True,
):
"""Visualize one or many boxes with different features.
Parameters
----------
centers : ndarray, shape (N, 3)
Box positions.
directions : ndarray, shape (N, 3), optional
The orientation vector of the box.
colors : ndarray (N,3) or (N, 4) or tuple (3,) or tuple (4,), optional
RGB or RGBA (for opacity) R, G, B and A should be at the range [0, 1].
scales : int or ndarray (N,3) or tuple (3,), optional
The size of the box in each dimension. If a single value is provided,
the same size will be used for all boxes.
opacity : float, optional
Takes values from 0 (fully transparent) to 1 (opaque).
If both `opacity` and RGBA are provided, the final alpha will be:
final_alpha = alpha_in_RGBA * opacity
material : str, optional
The material type for the boxes. Options are 'phong' and 'basic'.
enable_picking : bool, optional
Whether the boxes should be pickable in a 3D scene.
detailed : bool, optional
Whether to create a detailed box with 24 vertices or a simple box with
8 vertices.
Returns
-------
mesh_actor : Actor
A mesh actor containing the generated boxes, with the specified
material and properties.
Examples
--------
>>> from fury import window, actor
>>> import numpy as np
>>> scene = window.Scene()
>>> centers = np.random.rand(5, 3) * 10
>>> scales = np.random.rand(5, 3)
>>> box_actor = actor.box(centers=centers, scales=scales)
>>> scene.add(box_actor)
>>> show_manager = window.ShowManager(scene=scene, size=(600, 600))
>>> show_manager.start()
"""
vertices, faces = fp.prim_box(detailed=detailed)
res = fp.repeat_primitive(
vertices,
faces,
directions=directions,
centers=centers,
colors=colors,
scales=scales,
)
big_vertices, big_faces, big_colors, _ = res
prim_count = len(centers)
big_colors = big_colors / 255.0

if isinstance(opacity, (int, float)):
if big_colors.shape[1] == 3:
big_colors = np.hstack(
(big_colors, np.full((big_colors.shape[0], 1), opacity))
)
else:
big_colors[:, 3] *= opacity

geo = buffer_to_geometry(
indices=big_faces.astype("int32"),
positions=big_vertices.astype("float32"),
texcoords=big_vertices.astype("float32"),
colors=big_colors.astype("float32"),
)
mat = _create_mesh_material(material=material, enable_picking=enable_picking)
obj = create_mesh(geometry=geo, material=mat)
obj.local.position = centers[0]

obj.prim_count = prim_count

return obj
135 changes: 100 additions & 35 deletions fury/primitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,46 +256,111 @@ def prim_square():
return vertices, triangles


def prim_box():
"""Return vertices and triangle for a box geometry.
def prim_box(detailed=True):
"""Return vertices and triangles for a box geometry.
Parameters
----------
detailed : bool, optional
If True, returns 24 vertices (no shared vertices between orthogonal faces).
If False, returns 8 unique vertices.
Returns
-------
vertices: ndarray
8 vertices coords that composed our box
triangles: ndarray
12 triangles that composed our box
vertices : ndarray
Array of vertex coordinates.
triangles : ndarray
Array of triangle indices.
"""
vertices = np.array(
[
[-0.5, -0.5, -0.5],
[-0.5, -0.5, 0.5],
[-0.5, 0.5, -0.5],
[-0.5, 0.5, 0.5],
[0.5, -0.5, -0.5],
[0.5, -0.5, 0.5],
[0.5, 0.5, -0.5],
[0.5, 0.5, 0.5],
]
)
triangles = np.array(
[
[0, 6, 4],
[0, 2, 6],
[0, 3, 2],
[0, 1, 3],
[2, 7, 6],
[2, 3, 7],
[4, 6, 7],
[4, 7, 5],
[0, 4, 5],
[0, 5, 1],
[1, 5, 7],
[1, 7, 3],
],
dtype="i8",
)
if detailed:
vertices = (
np.array(
[
[-1, -1, -1],
[1, -1, -1],
[1, 1, -1],
[-1, 1, -1],
[-1, -1, 1],
[1, -1, 1],
[1, 1, 1],
[-1, 1, 1],
[-1, -1, -1],
[-1, 1, -1],
[-1, 1, 1],
[-1, -1, 1],
[1, -1, -1],
[1, 1, -1],
[1, 1, 1],
[1, -1, 1],
[-1, 1, -1],
[1, 1, -1],
[1, 1, 1],
[-1, 1, 1],
[-1, -1, -1],
[1, -1, -1],
[1, -1, 1],
[-1, -1, 1],
],
dtype=np.float32,
)
* 0.5
)

triangles = np.array(
[
[2, 1, 0],
[3, 2, 0],
[4, 5, 6],
[4, 6, 7],
[8, 10, 9],
[11, 10, 8],
[12, 13, 14],
[12, 14, 15],
[16, 17, 18],
[16, 18, 19],
[20, 21, 22],
[20, 22, 23],
],
dtype=np.uint32,
)

else:
vertices = (
np.array(
[
[-1, -1, -1],
[-1, -1, 1],
[-1, 1, -1],
[-1, 1, 1],
[1, -1, -1],
[1, -1, 1],
[1, 1, -1],
[1, 1, 1],
],
dtype=np.float32,
)
* 0.5
)

triangles = np.array(
[
[0, 6, 4],
[0, 2, 6],
[0, 3, 2],
[0, 1, 3],
[2, 7, 6],
[2, 3, 7],
[4, 6, 7],
[4, 7, 5],
[0, 4, 5],
[0, 5, 1],
[1, 5, 7],
[1, 7, 3],
],
dtype=np.uint32,
)

return vertices, triangles


Expand Down
19 changes: 19 additions & 0 deletions fury/tests/test_actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,22 @@ def test_sphere():
assert len(vertices) == len(colors)

npt.assert_array_almost_equal(len(faces), (2 * phi * (theta - 2)))


def test_box():
scene = window.Scene()
centers = np.array([[0, 0, 0]])
colors = np.array([[1, 0, 0]])
scales = np.array([[1, 1, 7]])

box_actor = actor.box(centers=centers, colors=colors, scales=scales)
scene.add(box_actor)

npt.assert_array_equal(box_actor.local.position, centers[0])

mean_vertex = np.mean(box_actor.geometry.positions.view, axis=0)
npt.assert_array_almost_equal(mean_vertex, centers[0])

assert box_actor.prim_count == 1

scene.remove(box_actor)
17 changes: 9 additions & 8 deletions fury/tests/test_primitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@
def test_vertices_primitives():
# Tests the default vertices of all the built in primitive shapes.
l_primitives = [
(fp.prim_square, (4, 3), -0.5, 0.5, 0),
(fp.prim_box, (8, 3), -0.5, 0.5, 0),
(fp.prim_tetrahedron, (4, 3), -0.5, 0.5, 0),
(fp.prim_star, (10, 3), -3, 3, -0.0666666666),
(fp.prim_rhombicuboctahedron, (24, 3), -0.5, 0.5, 0),
(fp.prim_frustum, (8, 3), -0.5, 0.5, 0),
(fp.prim_square, (4, 3), -0.5, 0.5, 0, {}),
(fp.prim_box, (24, 3), -0.5, 0.5, 0, {"detailed": True}),
(fp.prim_box, (8, 3), -0.5, 0.5, 0, {"detailed": False}),
(fp.prim_tetrahedron, (4, 3), -0.5, 0.5, 0, {}),
(fp.prim_star, (10, 3), -3, 3, -0.0666666666, {}),
(fp.prim_rhombicuboctahedron, (24, 3), -0.5, 0.5, 0, {}),
(fp.prim_frustum, (8, 3), -0.5, 0.5, 0, {}),
]

for func, shape, e_min, e_max, e_mean in l_primitives:
vertices, _ = func()
for func, shape, e_min, e_max, e_mean, kwargs in l_primitives:
vertices, _ = func(**kwargs)
npt.assert_equal(vertices.shape, shape)
npt.assert_almost_equal(np.mean(vertices), e_mean)
npt.assert_equal(vertices.min(), e_min)
Expand Down

0 comments on commit cb4637e

Please sign in to comment.