Camera commands#
In addition to reads, camera parameters also support writes. These are synced to the corresponding client automatically.
1import time
2
3import numpy as onp
4import viser
5import viser.transforms as tf
6
7server = viser.ViserServer()
8num_frames = 20
9
10
11@server.on_client_connect
12def _(client: viser.ClientHandle) -> None:
13 """For each client that connects, we create a set of random frames + a click handler for each frame.
14
15 When a frame is clicked, we move the camera to the corresponding frame.
16 """
17
18 rng = onp.random.default_rng(0)
19
20 def make_frame(i: int) -> None:
21 # Sample a random orientation + position.
22 wxyz = rng.normal(size=4)
23 wxyz /= onp.linalg.norm(wxyz)
24 position = rng.uniform(-3.0, 3.0, size=(3,))
25
26 # Create a coordinate frame and label.
27 frame = client.add_frame(f"/frame_{i}", wxyz=wxyz, position=position)
28 client.add_label(f"/frame_{i}/label", text=f"Frame {i}")
29
30 # Move the camera when we click a frame.
31 @frame.on_click
32 def _(_):
33 T_world_current = tf.SE3.from_rotation_and_translation(
34 tf.SO3(client.camera.wxyz), client.camera.position
35 )
36 T_world_target = tf.SE3.from_rotation_and_translation(
37 tf.SO3(frame.wxyz), frame.position
38 ) @ tf.SE3.from_translation(onp.array([0.0, 0.0, -0.5]))
39
40 T_current_target = T_world_current.inverse() @ T_world_target
41
42 for j in range(20):
43 T_world_set = T_world_current @ tf.SE3.exp(
44 T_current_target.log() * j / 19.0
45 )
46
47 # We can atomically set the orientation and the position of the camera
48 # together to prevent jitter that might happen if one was set before the
49 # other.
50 with client.atomic():
51 client.camera.wxyz = T_world_set.rotation().wxyz
52 client.camera.position = T_world_set.translation()
53
54 client.flush() # Optional!
55 time.sleep(1.0 / 60.0)
56
57 # Mouse interactions should orbit around the frame origin.
58 client.camera.look_at = frame.position
59
60 for i in range(num_frames):
61 make_frame(i)
62
63
64while True:
65 time.sleep(1.0)