Skip to content

Commit 19cce2c

Browse files
committed
refactor: use enum for Viser display modes
1 parent 1c64e38 commit 19cce2c

4 files changed

Lines changed: 45 additions & 37 deletions

File tree

dimos/manipulation/visualization/viser/gui.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@
1414

1515
from __future__ import annotations
1616

17-
from typing import TypeAlias, cast
17+
from typing import TypeAlias
1818

1919
from dimos.manipulation.visualization.types import RobotInfo, TargetEvaluation
2020
from dimos.manipulation.visualization.viser.adapter import InProcessViserAdapter
2121
from dimos.manipulation.visualization.viser.config import ViserVisualizationConfig
2222
from dimos.manipulation.visualization.viser.runtime import VISER_INSTALL_HINT
2323
from dimos.manipulation.visualization.viser.scene import (
24-
ROBOT_DISPLAY_MODE_VALUES,
2524
RobotDisplayMode,
2625
ViserManipulationScene,
2726
)
@@ -72,8 +71,10 @@
7271

7372
# Fallback joint-slider range (radians) when a robot config omits joint limits.
7473
DEFAULT_JOINT_LIMITS = (-3.14, 3.14)
75-
ROBOT_DISPLAY_LABELS = tuple(mode.title() for mode in ROBOT_DISPLAY_MODE_VALUES)
76-
ROBOT_DISPLAY_MODES = {mode: mode.title() for mode in ROBOT_DISPLAY_MODE_VALUES}
74+
ROBOT_DISPLAY_LABELS = tuple(mode.value.title() for mode in RobotDisplayMode)
75+
ROBOT_DISPLAY_MODES: dict[str, RobotDisplayMode] = {
76+
mode.value: mode for mode in RobotDisplayMode
77+
}
7778
ROBOT_DISPLAY_COLLISION_WARNING = (
7879
"**Collision meshes unavailable.** Showing visual geometry with collision styling."
7980
)
@@ -237,10 +238,10 @@ def _robot_display_label(self) -> str:
237238
scene = self.scene
238239
if scene is None:
239240
return ROBOT_DISPLAY_LABELS[0]
240-
mode = str(scene.robot_display_mode).lower()
241+
mode = scene.robot_display_mode.value
241242
if mode not in ROBOT_DISPLAY_MODES:
242243
return ROBOT_DISPLAY_LABELS[0]
243-
return ROBOT_DISPLAY_MODES[cast("RobotDisplayMode", mode)]
244+
return ROBOT_DISPLAY_MODES[mode].value.title()
244245

245246
def _set_robot_display_mode(self, label: str) -> None:
246247
if self._closed or self.scene is None:
@@ -249,7 +250,7 @@ def _set_robot_display_mode(self, label: str) -> None:
249250
mode = str(label).lower()
250251
if mode not in ROBOT_DISPLAY_MODES:
251252
return
252-
scene.robot_display_mode = cast("RobotDisplayMode", mode)
253+
scene.robot_display_mode = ROBOT_DISPLAY_MODES[mode]
253254
self._sync_robot_display_dropdown()
254255
self._sync_robot_display_warning()
255256

@@ -265,9 +266,9 @@ def _sync_robot_display_warning(self) -> None:
265266
scene = self.scene
266267
if handle is None or self._closed or scene is None:
267268
return
268-
mode = str(scene.robot_display_mode).lower()
269+
mode = scene.robot_display_mode
269270
has_collision = scene.collision_geometry_available
270-
visible = mode in {"collision", "both"} and not has_collision
271+
visible = mode in {RobotDisplayMode.COLLISION, RobotDisplayMode.BOTH} and not has_collision
271272
self._set_optional_handle_attr(handle, "visible", visible)
272273

273274
def _set_scene_grid_visible(self, visible: bool) -> None:

dimos/manipulation/visualization/viser/scene.py

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
from __future__ import annotations
1616

1717
from collections.abc import Callable, Sequence
18+
from enum import StrEnum
1819
from pathlib import Path
19-
from typing import Literal, Protocol, TypeAlias, cast, get_args
20+
from typing import Protocol, TypeAlias, cast
2021

2122
from yourdfpy import URDF # type: ignore[import-untyped]
2223

@@ -73,11 +74,10 @@
7374
COLLISION_MESH_COLOR = (210, 40, 220)
7475
COLLISION_MESH_OPACITY = 0.35
7576

76-
RobotDisplayMode: TypeAlias = Literal["visual", "collision", "both"]
77-
ROBOT_DISPLAY_MODE_VALUES: tuple[RobotDisplayMode, ...] = get_args(RobotDisplayMode)
78-
_VISUAL_DISPLAY_MODE = ROBOT_DISPLAY_MODE_VALUES[0]
79-
_COLLISION_DISPLAY_MODE = ROBOT_DISPLAY_MODE_VALUES[1]
80-
_BOTH_DISPLAY_MODE = ROBOT_DISPLAY_MODE_VALUES[2]
77+
class RobotDisplayMode(StrEnum):
78+
VISUAL = "visual"
79+
COLLISION = "collision"
80+
BOTH = "both"
8181

8282
SceneHandle: TypeAlias = ViserUrdf | TransformControlsHandle | GridHandle | MeshHandle
8383

@@ -105,7 +105,7 @@ def __init__(
105105
self._preview_visible: dict[str, bool] = {}
106106
self._target_tracks_current: dict[str, bool] = {}
107107
self._collision_fallback_urdfs: dict[str, ViserUrdf] = {}
108-
self._robot_display_mode: RobotDisplayMode = "visual"
108+
self._robot_display_mode = RobotDisplayMode.VISUAL
109109
self._ensure_reference_grid()
110110

111111
@property
@@ -114,11 +114,13 @@ def robot_display_mode(self) -> RobotDisplayMode:
114114
return self._robot_display_mode
115115

116116
@robot_display_mode.setter
117-
def robot_display_mode(self, mode: RobotDisplayMode) -> None:
117+
def robot_display_mode(self, mode: RobotDisplayMode | str) -> None:
118118
"""Set the primary robot display mode and apply it immediately."""
119-
if mode not in ROBOT_DISPLAY_MODE_VALUES:
120-
raise ValueError(f"Unsupported robot display mode: {mode!r}")
121-
self._robot_display_mode = mode
119+
try:
120+
normalized_mode = RobotDisplayMode(mode)
121+
except ValueError as error:
122+
raise ValueError(f"Unsupported robot display mode: {mode!r}") from error
123+
self._robot_display_mode = normalized_mode
122124
for robot_id in self._configs_by_id:
123125
self._apply_robot_display_mode(robot_id)
124126

@@ -319,7 +321,7 @@ def close(self) -> None:
319321
self._configs_by_id.clear()
320322
self._preview_visible.clear()
321323
self._target_tracks_current.clear()
322-
self._robot_display_mode = "visual"
324+
self._robot_display_mode = RobotDisplayMode.VISUAL
323325

324326
def _ensure_robot_urdfs(self, robot_id: str, config: RobotModelConfig) -> None:
325327
if not config.model_path:
@@ -416,17 +418,21 @@ def _apply_robot_display_mode(self, robot_id: str) -> None:
416418
# Viser's public flags manage all links, including links whose mesh
417419
# handles are not exposed by the helper. A model without collision
418420
# geometry falls back to its visual representation.
419-
current.show_visual = mode in {_VISUAL_DISPLAY_MODE, _BOTH_DISPLAY_MODE}
421+
current.show_visual = mode in {RobotDisplayMode.VISUAL, RobotDisplayMode.BOTH}
420422
current.show_collision = has_collision and mode in {
421-
_COLLISION_DISPLAY_MODE,
422-
_BOTH_DISPLAY_MODE,
423+
RobotDisplayMode.COLLISION,
424+
RobotDisplayMode.BOTH,
423425
}
424426
fallback = self._collision_fallback_urdfs.get(robot_id)
425427
if fallback is not None:
426-
fallback.show_visual = mode in {_COLLISION_DISPLAY_MODE, _BOTH_DISPLAY_MODE}
428+
fallback.show_visual = mode in {
429+
RobotDisplayMode.COLLISION,
430+
RobotDisplayMode.BOTH,
431+
}
427432
fallback.show_collision = False
428433
self._set_handle_visibility(
429-
fallback, mode in {_COLLISION_DISPLAY_MODE, _BOTH_DISPLAY_MODE}
434+
fallback,
435+
mode in {RobotDisplayMode.COLLISION, RobotDisplayMode.BOTH},
430436
)
431437

432438
def prepared_urdf_path(self, config: RobotModelConfig) -> Path:

dimos/manipulation/visualization/viser/test_gui_status.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from dimos.manipulation.visualization.viser.adapter import InProcessViserAdapter
2727
from dimos.manipulation.visualization.viser.config import ViserVisualizationConfig
2828
from dimos.manipulation.visualization.viser.gui import ROBOT_DISPLAY_LABELS, ViserPanelGui
29-
from dimos.manipulation.visualization.viser.scene import ViserManipulationScene
29+
from dimos.manipulation.visualization.viser.scene import RobotDisplayMode, ViserManipulationScene
3030
from dimos.manipulation.visualization.viser.state import FeasibilityStatus
3131

3232

@@ -86,19 +86,20 @@ def add_markdown(self, value: str, *, visible: bool = True) -> DisplayMarkdown:
8686

8787

8888
class DisplayScene:
89-
def __init__(self, mode: str = "visual", has_collision: bool = True) -> None:
90-
self._robot_display_mode = mode
89+
def __init__(self, mode: RobotDisplayMode | str = "visual", has_collision: bool = True) -> None:
90+
self._robot_display_mode = RobotDisplayMode(mode)
9191
self.collision_geometry_available = has_collision
9292
self.set_modes: list[str] = []
9393

9494
@property
95-
def robot_display_mode(self) -> str:
95+
def robot_display_mode(self) -> RobotDisplayMode:
9696
return self._robot_display_mode
9797

9898
@robot_display_mode.setter
99-
def robot_display_mode(self, mode: str) -> None:
100-
self.set_modes.append(mode)
101-
self._robot_display_mode = mode
99+
def robot_display_mode(self, mode: RobotDisplayMode | str) -> None:
100+
normalized_mode = RobotDisplayMode(mode)
101+
self.set_modes.append(normalized_mode)
102+
self._robot_display_mode = normalized_mode
102103

103104
def has_reference_grid(self) -> bool:
104105
return False

dimos/manipulation/visualization/viser/test_viser_visualization.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
)
3636
from dimos.manipulation.visualization.viser.config import ViserVisualizationConfig
3737
from dimos.manipulation.visualization.viser.gui import ViserPanelGui
38-
from dimos.manipulation.visualization.viser.scene import ViserManipulationScene
38+
from dimos.manipulation.visualization.viser.scene import RobotDisplayMode, ViserManipulationScene
3939
from dimos.manipulation.visualization.viser.state import (
4040
ActionStatus,
4141
FeasibilityStatus,
@@ -1483,7 +1483,7 @@ def test_gui_moves_joint_target_immediately_and_stores_evaluated_joint_solution(
14831483
target_updates = []
14841484
target_pose_updates = []
14851485
scene = SimpleNamespace(
1486-
robot_display_mode="visual",
1486+
robot_display_mode=RobotDisplayMode.VISUAL,
14871487
collision_geometry_available=False,
14881488
has_reference_grid=lambda: False,
14891489
ensure_target_controls=lambda *args: None,
@@ -1550,7 +1550,7 @@ def test_gui_cartesian_ik_result_does_not_rewrite_active_gizmo(
15501550
target_joint_updates = []
15511551
target_pose_updates = []
15521552
scene = SimpleNamespace(
1553-
robot_display_mode="visual",
1553+
robot_display_mode=RobotDisplayMode.VISUAL,
15541554
collision_geometry_available=False,
15551555
has_reference_grid=lambda: False,
15561556
ensure_target_controls=lambda *args: None,
@@ -1600,7 +1600,7 @@ def test_gui_collision_evaluation_marks_target_infeasible_and_colors_scene(
16001600
adapter = InProcessViserAdapter(world_monitor=world_monitor, manipulation_module=module)
16011601
visual_states = []
16021602
scene = SimpleNamespace(
1603-
robot_display_mode="visual",
1603+
robot_display_mode=RobotDisplayMode.VISUAL,
16041604
collision_geometry_available=False,
16051605
has_reference_grid=lambda: False,
16061606
ensure_target_controls=lambda *args: None,

0 commit comments

Comments
 (0)