3D GUI elements¶
Embed GUI controls directly in the 3D scene positioned relative to scene objects.
Features:
viser.SceneApi.add_3d_gui_container()for 3D-positioned GUI panelsClick interactions with coordinate frame objects
Context-sensitive controls attached to scene objects
Dynamic GUI panel visibility and positioning
Source: examples/03_interaction/05_gui_in_scene.py
Code¶
1from __future__ import annotations
2
3import time
4
5import numpy as np
6
7import viser
8import viser.transforms as tf
9
10server = viser.ViserServer()
11server.gui.configure_theme(dark_mode=True)
12num_frames = 20
13
14
15@server.on_client_connect
16def _(client: viser.ClientHandle) -> None:
17
18 rng = np.random.default_rng(0)
19
20 displayed_3d_container: viser.Gui3dContainerHandle | None = None
21
22 def make_frame(i: int) -> None:
23 # Sample a random orientation + position.
24 wxyz = rng.normal(size=4)
25 wxyz /= np.linalg.norm(wxyz)
26 position = rng.uniform(-3.0, 3.0, size=(3,))
27
28 # Create a coordinate frame and label.
29 frame = client.scene.add_frame(f"/frame_{i}", wxyz=wxyz, position=position)
30
31 # Move the camera when we click a frame.
32 @frame.on_click
33 def _(_):
34 nonlocal displayed_3d_container
35
36 # Close previously opened GUI.
37 if displayed_3d_container is not None:
38 displayed_3d_container.remove()
39
40 displayed_3d_container = client.scene.add_3d_gui_container(
41 f"/frame_{i}/gui"
42 )
43 with displayed_3d_container:
44 go_to = client.gui.add_button("Go to")
45 randomize_orientation = client.gui.add_button("Randomize orientation")
46 close = client.gui.add_button("Close GUI")
47
48 @go_to.on_click
49 def _(_) -> None:
50 T_world_current = tf.SE3.from_rotation_and_translation(
51 tf.SO3(client.camera.wxyz), client.camera.position
52 )
53 T_world_target = tf.SE3.from_rotation_and_translation(
54 tf.SO3(frame.wxyz), frame.position
55 ) @ tf.SE3.from_translation(np.array([0.0, 0.0, -0.5]))
56
57 T_current_target = T_world_current.inverse() @ T_world_target
58
59 for j in range(20):
60 T_world_set = T_world_current @ tf.SE3.exp(
61 T_current_target.log() * j / 19.0
62 )
63
64 # Important bit: we atomically set both the orientation and the position
65 # of the camera.
66 with client.atomic():
67 client.camera.wxyz = T_world_set.rotation().wxyz
68 client.camera.position = T_world_set.translation()
69 time.sleep(1.0 / 60.0)
70
71 # Mouse interactions should orbit around the frame origin.
72 client.camera.look_at = frame.position
73
74 @randomize_orientation.on_click
75 def _(_) -> None:
76 wxyz = rng.normal(size=4)
77 wxyz /= np.linalg.norm(wxyz)
78 frame.wxyz = wxyz
79
80 @close.on_click
81 def _(_) -> None:
82 nonlocal displayed_3d_container
83 if displayed_3d_container is None:
84 return
85 displayed_3d_container.remove()
86 displayed_3d_container = None
87
88 for i in range(num_frames):
89 make_frame(i)
90
91
92while True:
93 time.sleep(1.0)