"""Embedded offscreen 3D mesh viewport for the Qt dashboard.""" from __future__ import annotations from pipeline.frame import FingerFrameView from viz.offscreen_mesh import OffscreenFingerMeshRenderer from ..layout import DashboardLayout, LAYOUT from .qt_pixmap import ndarray_to_pixmap from .image_panel import ImagePanel class MeshViewportWidget(ImagePanel): """Render the finger mesh offscreen in a square 1:1 viewport.""" def __init__(self, parent=None, *, layout: DashboardLayout = LAYOUT): self._layout = layout self._renderer: OffscreenFingerMeshRenderer | None = None self._mesh_map = "depth" self._threshold = 0.0 self._enable_3d = True slot = layout.top_slot("mesh") super().__init__( title=layout.mesh_panel_title, object_name="meshViewport", default_side=min(slot.width_px, slot.height_px), parent=parent, ) def configure(self, pipeline) -> None: self._mesh_map = pipeline.config.mesh_map self._threshold = pipeline.config.flow_threshold self._enable_3d = pipeline.config.enable_3d and pipeline.uv_mesh is not None if self._renderer is not None: self._renderer.close() self._renderer = None if not self._enable_3d: self.label.setText( "3D mesh disabled" if not pipeline.config.enable_3d else "3D mesh unavailable" ) self.set_source_pixmap(None) return self.label.setText("") self._init_renderer(pipeline) def _init_renderer(self, pipeline) -> None: if not self._enable_3d or pipeline.uv_mesh is None: return side = self.side_length self._renderer = OffscreenFingerMeshRenderer( pipeline.uv_mesh, width=side, height=side, threshold=self._threshold, color_mode=self._mesh_map, world_to_cam=pipeline.world_to_cam, lookat=pipeline.mesh_lookat, ) def on_side_length_changed(self, side: int) -> None: if self._renderer is not None: self._renderer.resize(side, side) def update_frame(self, frame: FingerFrameView) -> None: if not self._enable_3d or self._renderer is None: return rgb = self._renderer.render( flow=frame.flow, scalar_map=frame.depth_map, centroid=frame.centroid, centroid_found=frame.centroid_found, fnormal=frame.fnormal, ) self.set_source_pixmap(ndarray_to_pixmap(rgb, bgr=False)) def close_renderer(self) -> None: if self._renderer is not None: self._renderer.close() self._renderer = None