.. Comment: this file is automatically generated by `update_example_docs.py`.
   It should not be modified manually.

3D GUI elements
==========================================


``add_3d_gui_container()`` allows standard GUI elements to be incorporated directly into a
3D scene. In this example, we click on coordinate frames to show actions that can be
performed on them.



.. code-block:: python
        :linenos:


        import time
        from typing import Optional

        import numpy as np

        import viser
        import viser.transforms as tf

        server = viser.ViserServer()
        server.gui.configure_theme(dark_mode=True)
        num_frames = 20


        @server.on_client_connect
        def _(client: viser.ClientHandle) -> None:
            """For each client that connects, we create a set of random frames + a click handler for each frame.

            When a frame is clicked, we display a 3D gui node.
            """

            rng = np.random.default_rng(0)

            displayed_3d_container: Optional[viser.Gui3dContainerHandle] = None

            def make_frame(i: int) -> None:
                # Sample a random orientation + position.
                wxyz = rng.normal(size=4)
                wxyz /= np.linalg.norm(wxyz)
                position = rng.uniform(-3.0, 3.0, size=(3,))

                # Create a coordinate frame and label.
                frame = client.scene.add_frame(f"/frame_{i}", wxyz=wxyz, position=position)

                # Move the camera when we click a frame.
                @frame.on_click
                def _(_):
                    nonlocal displayed_3d_container

                    # Close previously opened GUI.
                    if displayed_3d_container is not None:
                        displayed_3d_container.remove()

                    displayed_3d_container = client.scene.add_3d_gui_container(
                        f"/frame_{i}/gui"
                    )
                    with displayed_3d_container:
                        go_to = client.gui.add_button("Go to")
                        randomize_orientation = client.gui.add_button("Randomize orientation")
                        close = client.gui.add_button("Close GUI")

                    @go_to.on_click
                    def _(_) -> None:
                        T_world_current = tf.SE3.from_rotation_and_translation(
                            tf.SO3(client.camera.wxyz), client.camera.position
                        )
                        T_world_target = tf.SE3.from_rotation_and_translation(
                            tf.SO3(frame.wxyz), frame.position
                        ) @ tf.SE3.from_translation(np.array([0.0, 0.0, -0.5]))

                        T_current_target = T_world_current.inverse() @ T_world_target

                        for j in range(20):
                            T_world_set = T_world_current @ tf.SE3.exp(
                                T_current_target.log() * j / 19.0
                            )

                            # Important bit: we atomically set both the orientation and the position
                            # of the camera.
                            with client.atomic():
                                client.camera.wxyz = T_world_set.rotation().wxyz
                                client.camera.position = T_world_set.translation()
                            time.sleep(1.0 / 60.0)

                        # Mouse interactions should orbit around the frame origin.
                        client.camera.look_at = frame.position

                    @randomize_orientation.on_click
                    def _(_) -> None:
                        wxyz = rng.normal(size=4)
                        wxyz /= np.linalg.norm(wxyz)
                        frame.wxyz = wxyz

                    @close.on_click
                    def _(_) -> None:
                        nonlocal displayed_3d_container
                        if displayed_3d_container is None:
                            return
                        displayed_3d_container.remove()
                        displayed_3d_container = None

            for i in range(num_frames):
                make_frame(i)


        while True:
            time.sleep(1.0)
