Py-SPHViewer allows users to define custom smooth camera trajectories. The tool responsible for this is called camera_tools
, initially designed by Adrien Thob and inspired by Adobe Surge Target.
This tutorial will guide you through creating a custom camera trajectory using this powerful tool.
In this example, we will create a universe with two cubes, located at \((x_1, y_1, z_1) = (0.5, 1.5, 0.5)\) and \((x_2, y_2, z_2) = (0.5, -5.5, 0.5)\) respectively:
import matplotlib.pyplot as plt
import numpy as np
import h5py
from sphviewer.tools import camera_tools
n1 = 10000
cube1 = np.random.rand(3, n1)
cube1[:, 1] -= 6
cube2 = np.random.rand(3, n1)
cube2[:, 1] += 1
cubes = np.concatenate((cube1, cube2), axis=0)
mass = np.ones(n1 + n1)
Instead of using QuickView, we will render the scene more efficiently by directly using Py-SPHViewer’s internal functions.
Here, we instantiate the Particles
and Scene
classes. The camera is automatically created when the scene is initialized:
P = sph.Particles(cubes, mass)
S = sph.Scene(P)
You can update the camera parameters using the Scene.update_camera()
method.
Before creating the camera trajectory, it’s helpful to decide what you want to visualize and when you want to see it. For example, let’s say we want a video of 1680 frames, with the following sequence:
This sequence defines the camera’s trajectory using “anchors”. camera_tools
smoothly interpolates the trajectory between these anchor points. Here’s how you define it:
cm_1 = [0.5, 1.5, 0.5]
cm_2 = [0.5, -5.5, 0.5]
targets = [cm_1, cm_2]
anchors = {}
anchors['sim_times'] = [0.0, 1.0, 'pass', 3.0, 'same', 'same', 'same']
anchors['id_frames'] = [0, 180, 750, 840, 930, 1500, 1680]
anchors['r'] = [2, 'same', 'same', 'same', 'same', 'same', 'same']
anchors['id_targets'] = [0, 1, 'same', 'pass', 0, 'same', 1]
anchors['t'] = [0, 'same', 'same', 'same', 'same', 'same', 0]
anchors['p'] = [0, 'same', 'same', 'same', 'same', 'same', 'same']
anchors['zoom'] = [1., 'same', 'same', 'same', 'same', 'same', 'same']
anchors['extent'] = [10, 'same', 'same', 'same', 'same', 'same', 30]
data = camera_tools.get_camera_trajectory(targets, anchors)
h = 0
for i in data:
i['xsize'] = 250
i['ysize'] = 250
i['roll'] = 0
S.update_camera(**i)
R = sph.Render(S)
img = R.get_image()
R.set_logscale()
plt.imsave('img/image_' + str('%04d.png' % h), img, vmin=0, vmax=6, cmap='cubehelix')
h += 1
Here, the anchors
dictionary defines the key camera parameters, such as zoom
, extent
, r
, t
, p
, and the indices of targets. These anchors are passed to the camera_tools.get_camera_trajectory()
function, which generates the camera’s parameters for each frame.
Special keywords like same
and pass
control the behavior of the camera at each anchor. same
means the current parameter will remain unchanged, while pass
interpolates the value between the previous and next anchors.
The result of the code above is:
To make the camera movement more dynamic, we can also rotate the camera, adjust its distance from the cubes, and more. Here’s an example with additional parameters:
anchors = {}
anchors['sim_times'] = [0.0, 1.0, 'pass', 3.0, 'same', 'same', 'same']
anchors['id_frames'] = np.array([0, 180, 750, 840, 930, 1500, 1680]) / 4
anchors['id_targets'] = [0, 1, 'same', 'pass', 0, 'same', 1]
anchors['extent'] = [10, 'same', 'same', 'same', 'same', 'same', 30]
anchors['r'] = [10, 2, 'same', 4, 2, 'same', 10]
anchors['t'] = [0, 'pass', 'pass', 45, 'pass', 'pass', 0]
anchors['p'] = [0, 'pass', 'pass', 'pass', 'pass', 'pass', 900]
anchors['extent'] = [10, 'pass', 'pass', 'pass', 'pass', 'pass', 30]
This results in the following animation:
These examples demonstrate the flexibility and power of camera_tools
. You can combine various camera parameters to create impressive visualizations. For instance, the video below was generated using camera_tools
: