Spiky Sphere#

In this tutorial, we show how to create a sphere with spikes.

import itertools

import numpy as np

import fury

Create a sphere actor. Define the center, radius and color of a sphere. The sphere actor is made of points (vertices) evenly distributed on a sphere. Let’s create a scene.

scene = fury.window.Scene()

The vertices are connected with triangles in order to specify the direction of the surface normal. prim_sphere provides a sphere with evenly distributed points

vertices, triangles = fury.primitive.prim_sphere(name="symmetric362", gen_faces=False)

To be able to visualize the vertices, let’s define a point actor with green color.

point_actor = fury.actor.point(vertices, point_radius=0.01, colors=(0, 1, 0))

Normals are the vectors that are perpendicular to the surface at each vertex. We specify the normals at the vertices to tell the system whether triangles represent curved surfaces.

normals = fury.utils.normals_from_v_f(vertices, triangles)

The normals are usually used to calculate how the light will bounce on the surface of an object. However, here we will use them to direct the spikes (represented with arrows). So, let’s create an arrow actor at the center of each vertex.

arrow_actor = fury.actor.arrow(
    centers=vertices,
    directions=normals,
    colors=(1, 0, 0),
    heights=0.2,
    resolution=10,
    vertices=None,
    faces=None,
)

To be able to visualize the surface of the primitive sphere, we use get_actor_from_primitive.

primitive_colors = np.zeros(vertices.shape)
primitive_colors[:, 2] = 180
primitive_actor = fury.utils.get_actor_from_primitive(
    vertices=vertices,
    triangles=triangles,
    colors=primitive_colors,
    normals=normals,
    backface_culling=True,
)

We add all actors (visual objects) defined above to the scene.

scene.add(point_actor)
scene.add(arrow_actor)
scene.add(primitive_actor)
scene.add(fury.actor.axes())

The ShowManager class is the interface between the scene, the window and the interactor.

showm = fury.window.ShowManager(
    scene, size=(900, 768), reset_camera=False, order_transparent=True
)
/opt/homebrew/Caskroom/miniforge/base/envs/py39/lib/python3.9/site-packages/sphinx_gallery/gen_rst.py:722: UserWarning: We'll no longer accept the way you call the __init__ function in future versions of FURY.

Here's how to call the Function __init__: __init__(self_value, scene='value', title='value', size='value', png_magnify='value', reset_camera='value', order_transparent='value', interactor_style='value', stereo='value', multi_samples='value', max_peels='value', occlusion_ratio='value')

  exec(self.code, self.fake_main.__dict__)

We want to make a small animation for fun! We can determine the duration of animation with using the counter. Use itertools to avoid global variables.

The timer will call this user defined callback every 200 milliseconds. The application will exit after the callback has been called 20 times.

def timer_callback(_obj, _event):
    cnt = next(counter)
    showm.scene.azimuth(0.05 * cnt)
    primitive_actor.GetProperty().SetOpacity(cnt / 10.0)
    showm.render()
    if cnt == 20:
        showm.exit()


showm.add_timer_callback(True, 200, timer_callback)
showm.start()
fury.window.record(showm.scene, size=(900, 768), out_path="viz_spiky.png")
viz spiky
/opt/homebrew/Caskroom/miniforge/base/envs/py39/lib/python3.9/site-packages/sphinx_gallery/gen_rst.py:722: UserWarning: We'll no longer accept the way you call the record function in future versions of FURY.

Here's how to call the Function record: record(scene='value', cam_pos='value', cam_focal='value', cam_view='value', out_path='value', path_numbering='value', n_frames='value', az_ang='value', magnification='value', size='value', reset_camera='value', screen_clip='value', stereo='value', verbose='value')

  exec(self.code, self.fake_main.__dict__)

Instead of arrows, you can choose other geometrical objects such as cones, cubes or spheres.

Total running time of the script: (0 minutes 4.335 seconds)

Gallery generated by Sphinx-Gallery