From ccd821a5e9ce8074f78aa663062c62ca1b60693c Mon Sep 17 00:00:00 2001 From: brentyi Date: Tue, 10 Sep 2024 16:04:50 -0700 Subject: [PATCH] Update docs, improve type checking, hotfix recursion edge case --- docs/source/examples/09_urdf_visualizer.rst | 3 +- docs/source/examples/11_colmap_visualizer.rst | 52 ++++++++++--------- .../examples/25_smpl_visualizer_skinned.rst | 13 +++++ src/viser/_scene_handles.py | 28 +++++----- 4 files changed, 59 insertions(+), 37 deletions(-) diff --git a/docs/source/examples/09_urdf_visualizer.rst b/docs/source/examples/09_urdf_visualizer.rst index d7c16e92..413c6625 100644 --- a/docs/source/examples/09_urdf_visualizer.rst +++ b/docs/source/examples/09_urdf_visualizer.rst @@ -27,8 +27,9 @@ and viser. It can also take a path to a local URDF file as input. import numpy as onp import tyro - import viser from robot_descriptions.loaders.yourdfpy import load_robot_description + + import viser from viser.extras import ViserUrdf diff --git a/docs/source/examples/11_colmap_visualizer.rst b/docs/source/examples/11_colmap_visualizer.rst index d467ca28..943f343f 100644 --- a/docs/source/examples/11_colmap_visualizer.rst +++ b/docs/source/examples/11_colmap_visualizer.rst @@ -16,6 +16,7 @@ Visualize COLMAP sparse reconstruction outputs. To get demo data, see ``./assets import random import time from pathlib import Path + from typing import List import imageio.v3 as iio import numpy as onp @@ -77,26 +78,30 @@ Visualize COLMAP sparse reconstruction outputs. To get demo data, see ``./assets step=1, initial_value=min(len(images), 100), ) - gui_point_size = server.gui.add_number("Point size", initial_value=0.05) + gui_point_size = server.gui.add_slider( + "Point size", min=0.01, max=0.1, step=0.001, initial_value=0.05 + ) + + points = onp.array([points3d[p_id].xyz for p_id in points3d]) + colors = onp.array([points3d[p_id].rgb for p_id in points3d]) + + point_mask = onp.random.choice(points.shape[0], gui_points.value, replace=False) + point_cloud = server.scene.add_point_cloud( + name="/colmap/pcd", + points=points[point_mask], + colors=colors[point_mask], + point_size=gui_point_size.value, + ) + frames: List[viser.FrameHandle] = [] - def visualize_colmap() -> None: + def visualize_frames() -> None: """Send all COLMAP elements to viser for visualization. This could be optimized a ton!""" - # Set the point cloud. - points = onp.array([points3d[p_id].xyz for p_id in points3d]) - colors = onp.array([points3d[p_id].rgb for p_id in points3d]) - points_selection = onp.random.choice( - points.shape[0], gui_points.value, replace=False - ) - points = points[points_selection] - colors = colors[points_selection] - - server.scene.add_point_cloud( - name="/colmap/pcd", - points=points, - colors=colors, - point_size=gui_point_size.value, - ) + + # Remove existing image frames. + for frame in frames: + frame.remove() + frames.clear() # Interpret the images and cameras. img_ids = [im.id for im in images.values()] @@ -131,6 +136,7 @@ Visualize COLMAP sparse reconstruction outputs. To get demo data, see ``./assets axes_length=0.1, axes_radius=0.005, ) + frames.append(frame) # For pinhole cameras, cam.params will be (fx, fy, cx, cy). if cam.model != "PINHOLE": @@ -153,8 +159,9 @@ Visualize COLMAP sparse reconstruction outputs. To get demo data, see ``./assets @gui_points.on_update def _(_) -> None: - nonlocal need_update - need_update = True + point_mask = onp.random.choice(points.shape[0], gui_points.value, replace=False) + point_cloud.points = points[point_mask] + point_cloud.colors = colors[point_mask] @gui_frames.on_update def _(_) -> None: @@ -163,15 +170,12 @@ Visualize COLMAP sparse reconstruction outputs. To get demo data, see ``./assets @gui_point_size.on_update def _(_) -> None: - nonlocal need_update - need_update = True + point_cloud.point_size = gui_point_size.value while True: if need_update: need_update = False - - server.scene.reset() - visualize_colmap() + visualize_frames() time.sleep(1e-3) diff --git a/docs/source/examples/25_smpl_visualizer_skinned.rst b/docs/source/examples/25_smpl_visualizer_skinned.rst index 16031865..b5294cb1 100644 --- a/docs/source/examples/25_smpl_visualizer_skinned.rst +++ b/docs/source/examples/25_smpl_visualizer_skinned.rst @@ -180,6 +180,9 @@ See here for download instructions: gui_rgb = server.gui.add_rgb("Color", initial_value=(90, 200, 255)) gui_wireframe = server.gui.add_checkbox("Wireframe", initial_value=False) gui_show_controls = server.gui.add_checkbox("Handles", initial_value=True) + gui_control_size = server.gui.add_slider( + "Handle size", min=0.0, max=10.0, step=0.01, initial_value=1.0 + ) gui_rgb.on_update(set_changed) gui_wireframe.on_update(set_changed) @@ -189,6 +192,16 @@ See here for download instructions: for control in transform_controls: control.visible = gui_show_controls.value + @gui_control_size.on_update + def _(_): + for control in transform_controls: + prefixed_joint_name = control.name + control.scale = ( + 0.2 + * (0.75 ** prefixed_joint_name.count("/")) + * gui_control_size.value + ) + # GUI elements: shape parameters. with tab_group.add_tab("Shape", viser.Icon.BOX): gui_reset_shape = server.gui.add_button("Reset Shape") diff --git a/src/viser/_scene_handles.py b/src/viser/_scene_handles.py index f36591a8..0787c74a 100644 --- a/src/viser/_scene_handles.py +++ b/src/viser/_scene_handles.py @@ -40,17 +40,7 @@ def colors_to_uint8(colors: onp.ndarray) -> onpt.NDArray[onp.uint8]: return colors -class _OverridablePropApi: - """Mixin that allows reading/assigning properties defined in each scene node message.""" - - _PropHints: ClassVar[Dict[str, type]] - - def __init__(self) -> None: - assert False - - def __init_subclass__(cls, PropClass: type): - cls._PropHints = get_type_hints(PropClass) - +class _OverridablePropSettersAndGetters: def __setattr__(self, name: str, value: Any) -> None: handle = cast(SceneNodeHandle, self) # Get the value of the T TypeVar. @@ -70,7 +60,7 @@ def __setattr__(self, name: str, value: Any) -> None: return object.__setattr__(self, name, value) def __getattr__(self, name: str) -> Any: - if name in self._PropClass: + if name in self._PropHints: return getattr(self._impl.props, name) else: raise AttributeError( @@ -78,6 +68,20 @@ def __getattr__(self, name: str) -> Any: ) +class _OverridablePropApi( + _OverridablePropSettersAndGetters if not TYPE_CHECKING else object +): + """Mixin that allows reading/assigning properties defined in each scene node message.""" + + _PropHints: ClassVar[Dict[str, type]] + + def __init__(self) -> None: + assert False + + def __init_subclass__(cls, PropClass: type): + cls._PropHints = get_type_hints(PropClass) + + @dataclasses.dataclass(frozen=True) class ScenePointerEvent: """Event passed to pointer callbacks for the scene (currently only clicks)."""