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 np
4
5import viser
6import viser.transforms as tf
7
8server = viser.ViserServer()
9num_frames = 20
10
11
12@server.on_client_connect
13def _(client: viser.ClientHandle) -> None:
14 """For each client that connects, create GUI elements for adjusting the
15 near/far clipping planes."""
16
17 client.camera.far = 10.0
18
19 near_slider = client.gui.add_slider(
20 "Near", min=0.01, max=10.0, step=0.001, initial_value=client.camera.near
21 )
22 far_slider = client.gui.add_slider(
23 "Far", min=1, max=20.0, step=0.001, initial_value=client.camera.far
24 )
25
26 @near_slider.on_update
27 def _(_) -> None:
28 client.camera.near = near_slider.value
29
30 @far_slider.on_update
31 def _(_) -> None:
32 client.camera.far = far_slider.value
33
34
35@server.on_client_connect
36def _(client: viser.ClientHandle) -> None:
37 """For each client that connects, we create a set of random frames + a click handler for each frame.
38
39 When a frame is clicked, we move the camera to the corresponding frame.
40 """
41
42 rng = np.random.default_rng(0)
43
44 def make_frame(i: int) -> None:
45 # Sample a random orientation + position.
46 wxyz = rng.normal(size=4)
47 wxyz /= np.linalg.norm(wxyz)
48 position = rng.uniform(-3.0, 3.0, size=(3,))
49
50 # Create a coordinate frame and label.
51 frame = client.scene.add_frame(f"/frame_{i}", wxyz=wxyz, position=position)
52 client.scene.add_label(f"/frame_{i}/label", text=f"Frame {i}")
53
54 # Move the camera when we click a frame.
55 @frame.on_click
56 def _(_):
57 T_world_current = tf.SE3.from_rotation_and_translation(
58 tf.SO3(client.camera.wxyz), client.camera.position
59 )
60 T_world_target = tf.SE3.from_rotation_and_translation(
61 tf.SO3(frame.wxyz), frame.position
62 ) @ tf.SE3.from_translation(np.array([0.0, 0.0, -0.5]))
63
64 T_current_target = T_world_current.inverse() @ T_world_target
65
66 for j in range(20):
67 T_world_set = T_world_current @ tf.SE3.exp(
68 T_current_target.log() * j / 19.0
69 )
70
71 # We can atomically set the orientation and the position of the camera
72 # together to prevent jitter that might happen if one was set before the
73 # other.
74 with client.atomic():
75 client.camera.wxyz = T_world_set.rotation().wxyz
76 client.camera.position = T_world_set.translation()
77
78 client.flush() # Optional!
79 time.sleep(1.0 / 60.0)
80
81 # Mouse interactions should orbit around the frame origin.
82 client.camera.look_at = frame.position
83
84 for i in range(num_frames):
85 make_frame(i)
86
87
88while True:
89 time.sleep(1.0)