Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding box actor #958

Merged
merged 6 commits into from
Jan 9, 2025
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
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
Loading