# Path: robel/simulation/renderer.py
# class Renderer(abc.ABC):
#     """Base interface for rendering simulations."""
# 
#     def __init__(self, sim):
#         """Initializes a new renderer.
# 
#         Args:
#             sim: A handle to the simulation.
#         """
#         self._sim = sim
#         self._camera_settings = {}
# 
#     @abc.abstractmethod
#     def render_to_window(self):
#         """Renders the simulation to a window."""
# 
#     @abc.abstractmethod
#     def refresh_window(self):
#         """Refreshes the rendered window if one is present."""
# 
#     @abc.abstractmethod
#     def render_offscreen(self,
#                          width: int,
#                          height: int,
#                          mode: RenderMode = RenderMode.RGB,
#                          camera_id: int = -1) -> np.ndarray:
#         """Renders the camera view as a numpy array of pixels.
# 
#         Args:
#             width: The viewport width (pixels).
#             height: The viewport height (pixels).
#             mode: The rendering mode.
#             camera_id: The ID of the camera to render from. By default, uses
#                 the free camera.
# 
#         Returns:
#             A numpy array of the pixels.
#         """
# 
#     def set_free_camera_settings(
#             self,
#             distance: Optional[float] = None,
#             azimuth: Optional[float] = None,
#             elevation: Optional[float] = None,
#             lookat: Sequence[float] = None,
#             center: bool = True,
#     ):
#         """Sets the free camera parameters.
# 
#         Args:
#             distance: The distance of the camera from the target.
#             azimuth: Horizontal angle of the camera, in degrees.
#             elevation: Vertical angle of the camera, in degrees.
#             lookat: The (x, y, z) position in world coordinates to target.
#             center: If True and `lookat` is not given, targets the camera at the
#                 median position of the simulation geometry.
#         """
#         settings = {}
#         if distance is not None:
#             settings['distance'] = distance
#         if azimuth is not None:
#             settings['azimuth'] = azimuth
#         if elevation is not None:
#             settings['elevation'] = elevation
#         if lookat is not None:
#             settings['lookat'] = np.array(lookat, dtype=np.float32)
#         elif center:
#             # Calculate the center of the simulation geometry.
#             settings['lookat'] = np.array(
#                 [np.median(self._sim.data.geom_xpos[:, i]) for i in range(3)],
#                 dtype=np.float32)
# 
#         self._camera_settings = settings
# 
#     def close(self):
#         """Cleans up any resources being used by the renderer."""
# 
#     def _update_camera_properties(self, camera: Any):
#         """Updates the given camera object with the current camera settings."""
#         for key, value in self._camera_settings.items():
#             if key == 'lookat':
#                 getattr(camera, key)[:] = value
#             else:
#                 setattr(camera, key, value)
# 
#     def __del__(self):
#         """Automatically clean up when out of scope."""
#         self.close()
# 
# class RenderMode(enum.Enum):
#     """Rendering modes for offscreen rendering."""
#     RGB = 0
#     DEPTH = 1
#     SEGMENTATION = 2


import sys
import numpy as np
import dm_control.mujoco as dm_mujoco
import dm_control.viewer as dm_viewer
import dm_control._render as dm_render
from robel.simulation.renderer import Renderer, RenderMode
class DMRenderer(Renderer):
    """Renders DM Control Physics objects."""

    def __init__(self, sim: dm_mujoco.Physics):
        super().__init__(sim)
        self._window = None

    def render_to_window(self):
        """Renders the Physics object to a window.

        The window continuously renders the Physics in a separate thread.

        This function is a no-op if the window was already created.
        """
        if not self._window:
            self._window = DMRenderWindow()
            self._window.load_model(self._sim)
            self._update_camera_properties(self._window.camera)

        self._window.run_frame()

    def refresh_window(self):
        """Refreshes the rendered window if one is present."""
        if self._window is None:
            return
        self._window.run_frame()

    def render_offscreen(self,
                         width: int,
                         height: int,

# Path: robel/simulation/renderer.py
# class Renderer(abc.ABC):
#     """Base interface for rendering simulations."""
# 
#     def __init__(self, sim):
#         """Initializes a new renderer.
# 
#         Args:
#             sim: A handle to the simulation.
#         """
#         self._sim = sim
#         self._camera_settings = {}
# 
#     @abc.abstractmethod
#     def render_to_window(self):
#         """Renders the simulation to a window."""
# 
#     @abc.abstractmethod
#     def refresh_window(self):
#         """Refreshes the rendered window if one is present."""
# 
#     @abc.abstractmethod
#     def render_offscreen(self,
#                          width: int,
#                          height: int,
#                          mode: RenderMode = RenderMode.RGB,
#                          camera_id: int = -1) -> np.ndarray:
#         """Renders the camera view as a numpy array of pixels.
# 
#         Args:
#             width: The viewport width (pixels).
#             height: The viewport height (pixels).
#             mode: The rendering mode.
#             camera_id: The ID of the camera to render from. By default, uses
#                 the free camera.
# 
#         Returns:
#             A numpy array of the pixels.
#         """
# 
#     def set_free_camera_settings(
#             self,
#             distance: Optional[float] = None,
#             azimuth: Optional[float] = None,
#             elevation: Optional[float] = None,
#             lookat: Sequence[float] = None,
#             center: bool = True,
#     ):
#         """Sets the free camera parameters.
# 
#         Args:
#             distance: The distance of the camera from the target.
#             azimuth: Horizontal angle of the camera, in degrees.
#             elevation: Vertical angle of the camera, in degrees.
#             lookat: The (x, y, z) position in world coordinates to target.
#             center: If True and `lookat` is not given, targets the camera at the
#                 median position of the simulation geometry.
#         """
#         settings = {}
#         if distance is not None:
#             settings['distance'] = distance
#         if azimuth is not None:
#             settings['azimuth'] = azimuth
#         if elevation is not None:
#             settings['elevation'] = elevation
#         if lookat is not None:
#             settings['lookat'] = np.array(lookat, dtype=np.float32)
#         elif center:
#             # Calculate the center of the simulation geometry.
#             settings['lookat'] = np.array(
#                 [np.median(self._sim.data.geom_xpos[:, i]) for i in range(3)],
#                 dtype=np.float32)
# 
#         self._camera_settings = settings
# 
#     def close(self):
#         """Cleans up any resources being used by the renderer."""
# 
#     def _update_camera_properties(self, camera: Any):
#         """Updates the given camera object with the current camera settings."""
#         for key, value in self._camera_settings.items():
#             if key == 'lookat':
#                 getattr(camera, key)[:] = value
#             else:
#                 setattr(camera, key, value)
# 
#     def __del__(self):
#         """Automatically clean up when out of scope."""
#         self.close()
# 
# class RenderMode(enum.Enum):
#     """Rendering modes for offscreen rendering."""
#     RGB = 0
#     DEPTH = 1
#     SEGMENTATION = 2


# Path: robel/simulation/dm_renderer.py
import sys
import numpy as np
import dm_control.mujoco as dm_mujoco
import dm_control.viewer as dm_viewer
import dm_control._render as dm_render
from robel.simulation.renderer import Renderer, RenderMode

class DMRenderer(Renderer):
    """Renders DM Control Physics objects."""

    def __init__(self, sim: dm_mujoco.Physics):
        super().__init__(sim)
        self._window = None

    def render_to_window(self):
        """Renders the Physics object to a window.

        The window continuously renders the Physics in a separate thread.

        This function is a no-op if the window was already created.
        """
        if not self._window:
            self._window = DMRenderWindow()
            self._window.load_model(self._sim)
            self._update_camera_properties(self._window.camera)

        self._window.run_frame()

    def refresh_window(self):
        """Refreshes the rendered window if one is present."""
        if self._window is None:
            return
        self._window.run_frame()

    def render_offscreen(self,
                         width: int,
                         height: int,

                         mode: RenderMode = RenderMode.RGB,