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

WIP: Double arrow actor with tests/primitives #786

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
68 changes: 67 additions & 1 deletion fury/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,72 @@ def axes(
return arrow_actor


def dualpoint_arrow(centers, directions, colors, heights=1,
tip_length=0.35, tip_radius=0.1, shaft_radius=0.03, scales=1, resolution=10,
vertices=None, faces=None, repeat_primitive=True):
"""Visualize one or many arrows with differents features.
Parameters
----------
centers : ndarray, shape (N, 3)
Arrow positions
directions : ndarray, shape (N, 3)
The orientation vector of the arrow.
colors : ndarray (N,3) or (N, 4) or tuple (3,) or tuple (4,)
RGB or RGBA (for opacity) R, G, B and A should be at the range [0, 1]
heights : ndarray, shape (N)
The height of the arrow.
resolution : int
The resolution of the arrow.
tip_length : float
The tip size of the arrow (default: 0.35)
tip_radius : float
the tip radius of the arrow (default: 0.1)
shaft_radius : float
The shaft radius of the arrow (default: 0.03)
vertices : ndarray, shape (N, 3)
The point cloud defining the arrow.
faces : ndarray, shape (M, 3)
If faces is None then a arrow is created based on directions, heights
and resolution. If not then a arrow is created with the provided
vertices and faces.
Returns
-------
dualpoint_arrow_actor: Actor
Examples
--------
>>> from fury import window, actor
>>> scene = window.Scene()
>>> centers = np.random.rand(5, 3)
>>> directions = np.random.rand(5, 3)
>>> heights = np.random.rand(5)
>>> dualpoint_arrow_actor = actor.dualpoint_arrow(centers, directions, (1, 1, 1), heights)
>>> scene.add(dualpoint_arrow_actor)
>>> # window.show(scene)
"""
if repeat_primitive:
vertices, faces = fp.prim_dualpoint_arrow()
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)
dualpoint_arrow_actor = get_actor_from_primitive(big_vertices, big_faces, big_colors,
prim_count=prim_count)
return dualpoint_arrow_actor

src = ArrowSource() if faces is None else None

if src is not None:
src.SetTipResolution(resolution)
src.SetShaftResolution(resolution)
src.SetTipLength(tip_length)
src.SetTipRadius(tip_radius)
src.SetShaftRadius(shaft_radius)

dualpoint_arrow_actor = repeat_sources(centers=centers, directions=directions,
colors=colors, active_scalars=heights,
source=src, vertices=vertices, faces=faces)
return dualpoint_arrow_actor

def odf_slicer(
odfs,
affine=None,
Expand Down Expand Up @@ -3794,4 +3860,4 @@ def callback(
shader_to_actor(sq_actor, 'fragment', decl_code=fs_dec_code)
shader_to_actor(sq_actor, 'fragment', impl_code=fs_impl_code, block='light')

return sq_actor
return sq_actor
1 change: 0 additions & 1 deletion fury/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,4 +560,3 @@ def manifest_standard(actor, ambient_level=0, ambient_color=(1, 1, 1),
warnings.warn('Actor does not have the attribute property. This '
'material will not be applied.')
return

89 changes: 88 additions & 1 deletion fury/primitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,93 @@ def prim_arrow(

return vertices, triangles

def prim_dualpoint_arrow(height=1.0, resolution=10, tip_length=0.35, tip_radius=0.1, shaft_radius=0.03):
"""Return vertices and triangle for arrow geometry.
Parameters
----------
height : float
The height of the arrow (default: 1.0).
resolution : int
The resolution of the arrow.
tip_length : float
The tip size of the arrow (default: 0.35)
tip_radius : float
the tip radius of the arrow (default: 0.1)
shaft_radius : float
The shaft radius of the arrow (default: 0.03)
Returns
-------
vertices: ndarray
vertices of the Arrow
triangles: ndarray
Triangles of the Arrow
"""

shaft_height = height - tip_length

all_faces = []
shaft_outer_circle_down = []
shaft_outer_circle_up = []
tip_outer_circle = []

# calculating vertices
for i in range(resolution + 1):
x = math.cos((i * 2) * math.pi / resolution)
y = math.sin((i * 2) * math.pi / resolution)

shaft_x = x * shaft_radius
shaft_y = y * shaft_radius

tip_x = x * tip_radius
tip_y = y * tip_radius

# lower shaft circle (d)
shaft_outer_circle_down.append((0.0, shaft_x, shaft_y))
# upper shaft circle (u)
shaft_outer_circle_up.append((shaft_height, shaft_x, shaft_y))
# tip outer circle
tip_outer_circle.append((shaft_height, tip_x, tip_y))

# center, center at shaft height, center at overall height
v1, v2, v3 = (.0, .0, .0), (shaft_height, .0, .0), (height, .0, .0)

all_verts = [v1, v2, v3] + shaft_outer_circle_down + shaft_outer_circle_up + tip_outer_circle

offset = len(shaft_outer_circle_down)

off_1 = 3
off_2 = off_1 + offset
off_3 = off_2 + offset

# calculating triangles
for i in range(resolution):
# down circle d[i] , 0, d[i + 1]
# all_faces.append((i + off_1 + 1, i + off_1, 0))

# cylinder triangles 1 d[i], d[i + 1], u[i + 1]
all_faces.append((i + off_2 + 1, i + off_1, i + off_1 + 1))

# cylinder triangles 2 u[i + 1], u[i], d[i]
all_faces.append((i + off_1, i + off_2 + 1, i + off_2))

# tip circle u[i] , 1, d[i + 1]
all_faces.append((i + off_3 + 1, i + off_3, 1))

# tip cone t[i], t[i + 1], 2
all_faces.append((2, i + off_3, i + off_3 + 1))

vertices_front = np.asarray(all_verts)
triangles_front = np.asarray(all_faces, dtype=int)
vertices_back = vertices_front.copy()
vertices_back = - vertices_back
triangles_back = triangles_front.copy()
addition_factor = vertices_front.shape[0]
triangles_back = np.add(triangles_back, addition_factor)

vertices = np.vstack((vertices_front, vertices_back))
triangles = np.vstack((triangles_front, triangles_back))

return vertices, triangles

def prim_cone(radius=0.5, height=1, sectors=10):
"""Return vertices and triangle of a Cone.
Expand Down Expand Up @@ -1194,4 +1281,4 @@ def prim_cone(radius=0.5, height=1, sectors=10):

triangles = np.array(triangles).reshape(-1, 3)

return vertices, triangles
return vertices, triangles
3 changes: 3 additions & 0 deletions fury/tests/test_actors.py
Original file line number Diff line number Diff line change
Expand Up @@ -1768,6 +1768,8 @@ def test_actors_primitives_count():
[actor.cone, {**args_3, 'use_primitive': True}, cen_c],
[actor.arrow, {**args_3, 'repeat_primitive': False}, cen_c],
[actor.arrow, {**args_3, 'repeat_primitive': True}, cen_c],
[actor.dualpoint_arrow, {**args_3, 'repeat_primite': False}, cen_c],
[actor.dualpoint_arrow, {**args_3, 'repeat_primitive': True}, cen_c],
[actor.dot, {'points': centers}, cen_c],
[actor.point, {'points': centers, 'colors': colors}, cen_c],
[actor.line, {'lines': lines}, lin_c],
Expand All @@ -1779,3 +1781,4 @@ def test_actors_primitives_count():
primitives_count = test_case[2]
act = act_func(**args)
npt.assert_equal(primitives_count_from_actor(act), primitives_count)

2 changes: 1 addition & 1 deletion fury/tests/test_material.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,4 @@ def test_manifest_standard():
npt.assert_array_almost_equal(actual, desired, decimal=2)
actual = ss[100, 150, :] / 1000
desired = np.array([180, 0, 180]) / 1000
npt.assert_array_almost_equal(actual, desired, decimal=2)
npt.assert_array_almost_equal(actual, desired, decimal=2)