Source code for fvdb.viz._camera_view

# Copyright Contributors to the OpenVDB Project
# SPDX-License-Identifier: Apache-2.0
#

from typing import Any

import torch

from .._Cpp import CameraView as CameraViewCpp
from ..types import NumericMaxRank1, NumericScalarNative, to_Vec3f
from ._viewer_server import _get_viewer_server_cpp


[docs] class CamerasView: """ A view for a set of camera frusta and axes in a :class:`fvdb.viz.Scene` with parameters to adjust how the cameras are rendered. Each camera is represented by its camera-to-world and projection matrices, and drawn as a wireframe frustum with orthogonal axes at the camera's origin. """ __PRIVATE__ = object() def _get_view(self) -> CameraViewCpp: """ Get the underlying C++ CameraView instance from the viewer server or raise a :class:`RuntimeError` if it is not registered. Returns: view (CameraViewCpp): The C++ CameraView instance """ server = _get_viewer_server_cpp() if not server.has_camera_view(self._name): raise RuntimeError(f"CameraView '{self._name}' is not registered with the viewer server.") return server.get_camera_view(self._name) def __init__( self, scene_name: str, name: str, camera_to_world_matrices: torch.Tensor, projection_matrices: torch.Tensor, image_sizes: torch.Tensor, axis_length: float, axis_thickness: float, frustum_line_width: float, frustum_scale: float, frustum_color: tuple[float, float, float], frustum_near_plane: float, frustum_far_plane: float, enabled: bool, _private: Any = None, ): """ Create a new :class:`CamerasView` or update an existing one within a scene with the given name. .. warning:: This constructor is private and should never be called directly. Use :meth:`fvdb.viz.Scene.add_cameras()` instead. Args: scene_name (str): The name of the scene the view belongs to. name (str): The name of the :class:`CamerasView`. camera_to_world_matrices (torch.Tensor): A tensor of shape ``(N, 4, 4)`` representing the camera-to-world matrices for N cameras. projection_matrices (torch.Tensor): A tensor of shape ``(N, 4, 4)`` representing the projection matrices for N cameras. image_sizes (torch.Tensor): A tensor of shape ``(N, 2)`` representing the image sizes (width, height) for N cameras. axis_length (float): The length of the axes drawn at each camera origin in world units. axis_thickness (float): The thickness of the axes drawn at each camera origin in pixel units. frustum_line_width (float): The line width of the frustum in pixel units. frustum_scale (float): The scale factor applied to the frustum visualization. frustum_color (tuple[float, float, float]): The color of the frustum lines as an RGB tuple with values in [0, 1]. frustum_near_plane (float): The near plane distance for the camera frusta. frustum_far_plane (float): The far plane distance for the camera frusta. enabled (bool): Whether the camera frusta and axes are shown in the viewer. _private (Any): A private object to prevent direct construction. Must be :attr:`CamerasView.__PRIVATE__`. """ if _private is not self.__PRIVATE__: raise ValueError("CameraView constructor is private. Use Scene.add_cameras() instead.") server = _get_viewer_server_cpp() self._scene_name = scene_name self._name = name view: CameraViewCpp = server.add_camera_view( scene_name=scene_name, name=name, camera_to_world_matrices=camera_to_world_matrices, projection_matrices=projection_matrices, image_sizes=image_sizes, frustum_near_plane=frustum_near_plane, frustum_far_plane=frustum_far_plane, axis_length=axis_length, axis_thickness=axis_thickness, frustum_line_width=frustum_line_width, frustum_scale=frustum_scale, frustum_color=frustum_color, visible=enabled, ) # TODO: needs adjustments on editor's side # view.axis_length = axis_length # view.axis_thickness = axis_thickness # view.frustum_line_width = frustum_line_width # view.frustum_scale = frustum_scale # view.frustum_color = frustum_color # view.visible = enabled @property def enabled(self) -> bool: """ Return whether the camera frusta and axes are shown in the scene. Returns: enabled (bool): ``True`` if the camera frusta and axes are shown in the scene, ``False`` otherwise. """ view = self._get_view() return view.visible @enabled.setter def enabled(self, enabled: bool): """ Set whether the camera frusta and axes are shown in the scene. Args: enabled (bool): ``True`` to show the camera frusta and axes, ``False`` to hide them. """ view = self._get_view() view.visible = enabled @property def axis_length(self) -> float: """ Get the length of the axes drawn at each camera origin in world units. Returns: length (float): The length of the axes. """ view = self._get_view() return view.axis_length @axis_length.setter def axis_length(self, length: NumericScalarNative): """ Set the length of the axes drawn at each camera origin in world units. Args: length (NumericScalarNative): The length of the axes. """ view = self._get_view() view.axis_length = float(length) @property def axis_thickness(self) -> float: """ Get the thickness of the axes drawn at each camera origin in pixel units. Returns: thickness (float): The thickness of the axes. """ view = self._get_view() return view.axis_thickness @axis_thickness.setter def axis_thickness(self, thickness: NumericScalarNative): """ Set the thickness of the axes drawn at each camera origin in pixel units. Args: thickness (NumericScalarNative): The thickness of the axes. """ view = self._get_view() view.axis_thickness = float(thickness) @property def frustum_line_width(self) -> float: """ Get the line width of the frustum in the camera frustum view. """ view = self._get_view() return view.frustum_line_width @frustum_line_width.setter def frustum_line_width(self, width: NumericScalarNative): """ Set the line width of the frustum in the camera frustum view. Args: width (NumericScalarNative): The line width of the frustum in world units. """ view = self._get_view() view.frustum_line_width = float(width) @property def frustum_scale(self) -> float: """ Get the scale factor applied to the frustum visualization. Each frustum will have its size multiplied by this scale factor when rendered. *E.g.* if the frustum has ``near = 0.1``, and ``far = 1.0``, then setting the frustum scale to ``2.0`` will render the frustum as if ``near = 0.2`` and ``far = 2.0``. Returns: scale (float): The scale factor applied to the frustum visualization. """ view = self._get_view() return view.frustum_scale @frustum_scale.setter def frustum_scale(self, scale: NumericScalarNative): """ Set the scale factor applied to the frustum visualization. Each frustum will have its size multiplied by this scale factor when rendered. *E.g.* if the frustum has ``near = 0.1``, and ``far = 1.0``, then setting the frustum scale to ``2.0`` will render the frustum as if ``near = 0.2`` and ``far = 2.0``. Args: scale (NumericScalarNative): The scale factor to apply to the frustum visualization. """ view = self._get_view() view.frustum_scale = float(scale) @property def frustum_color(self) -> torch.Tensor: """ Get the RGB color of the frustum lines as a tensor of shape ``(3,)`` with values in ``[0, 1]``. Returns: torch.Tensor: The RGB color of the frustum lines. """ view = self._get_view() r, g, b = view.frustum_color return torch.tensor([r, g, b], dtype=torch.float32) @frustum_color.setter def frustum_color(self, color: NumericMaxRank1): """ Set the RGB color of the frustum lines. Color values must be in ``[0, 1]``. Args: color (NumericMaxRank1): A tensor-like object of shape ``(3,)`` representing the RGB color of the frustum lines with values in ``[0, 1]``. """ view = self._get_view() color_vec3f = to_Vec3f(color).cpu().numpy().tolist() if any(c < 0.0 or c > 1.0 for c in color_vec3f): raise ValueError(f"Frustum color components must be in [0, 1], got {color_vec3f}") view.frustum_color = tuple(color_vec3f)