Source code for layerview.visualization.world.world

"""Visualization scene."""
from __future__ import annotations

from enum import Enum, auto
from typing import Optional

from panda3d.core import (
    AmbientLight,
    DirectionalLight,
    LVecBase2i,
    LVecBase3f,
    LVector3d,
    NodePath,
    VBase4,
)
from QPanda3D.Panda3DWorld import Panda3DWorld

from layerview.visualization.nodes.build_area import BuildAreaNodeBuilder
from layerview.visualization.nodes.model import ModelManager
from layerview.visualization.point_cloud.boundaries import BoundingBox3D
from layerview.visualization.point_cloud.model import ModelInfo
from layerview.visualization.world.camera import (
    CameraController,
    FreeCameraController,
    OrbitCameraController,
)


[docs]class CameraMode(Enum): """Camera mode.""" SPHERICAL = auto() FREE = auto()
[docs]class ColoringMode(Enum): """Model coloring mode.""" CONSTANT = auto() FEEDRATE = auto() THICKNESS = auto() TEMPERATURE = auto()
[docs]class Visualization(Panda3DWorld): """3D Visualization scene.""" _BACKGROUND_COLOR: VBase4 = VBase4(0.9, 0.9, 0.9, 1) _BUILD_AREA_BOUNDARIES_DEFAULT: BoundingBox3D = BoundingBox3D.from_origin( LVector3d(200, 200, 200) ) _FOCAL_POINT_DEFAULT: LVector3d = LVector3d( _BUILD_AREA_BOUNDARIES_DEFAULT.center.xy, _BUILD_AREA_BOUNDARIES_DEFAULT.size.z * 0.1, ) def __init__( self, camera_mode: CameraMode = CameraMode.SPHERICAL, debug: bool = False ): """ Parameters ---------- camera_mode : CameraMode Initial CameraMode. debug : bool Debug flag. """ Panda3DWorld.__init__(self) self._is_debug: bool = debug # Children self._model_node_manager: Optional[ModelManager] = None self._build_area_node: Optional[NodePath] = None # Light self._dir_light_top_np: Optional[DirectionalLight] = None self._dir_light_bot_np: Optional[DirectionalLight] = None self._ambient_light_np: Optional[AmbientLight] = None self._init_lights() # Controls self.disableMouse() # Disable default camera mouse controls # Background color self.buff.setClearColorActive(True) self.buff.setClearColor(self._BACKGROUND_COLOR) # Camera self._camera_mode: CameraMode = camera_mode self._camera_controller: Optional[CameraController] = None self._focal_point: LVector3d = self._FOCAL_POINT_DEFAULT # Tasks self._setup_tasks() # Misc self.disableAllAudio() self.disableParticles() self._frame_buffer_size: Optional[LVecBase2i] = None if self._is_debug: self.messenger.toggleVerbose()
[docs] def reset(self): """Reset scene to initial state.""" self._remove_model_node() new_build_area = self._get_new_build_area( size=self._BUILD_AREA_BOUNDARIES_DEFAULT.size, size_min=self._BUILD_AREA_BOUNDARIES_DEFAULT.size * 0.9, ) self._set_build_area(new_build_area) x, y, z = self._BUILD_AREA_BOUNDARIES_DEFAULT.size new_camera_pos = LVecBase3f(x / 2, -y * 1.2, z * 1.2) # Place camera at the same pos as anchor. self.cam.setPos(0, 0, 0) self.camera.setPos(new_camera_pos) self.camera.lookAt(*self._FOCAL_POINT_DEFAULT) if self._camera_controller: self._camera_controller.deactivate() self.set_focal_point(focal_point=self._FOCAL_POINT_DEFAULT) self._set_camera_controller(self._get_new_camera_controller(activate=True))
[docs] def handle_focus_out(self): self._camera_controller.stop_movement()
# Properties @property def model_node_manager(self) -> Optional[ModelManager]: return self._model_node_manager @property def model_info(self) -> Optional[ModelInfo]: if self.model_node_manager: return self._model_node_manager.model_info return None @property def model_node(self) -> Optional[NodePath]: if self.model_node_manager: return self._model_node_manager.model_node return None @property def camera_mode(self): return self._camera_mode @property def focal_point(self) -> Optional[LVector3d]: return self._focal_point # Events
[docs] def _setup_tasks(self): """Initializes Panda3D tasks for this Visualization.""" self.addTask( self._task_update_frame_buffer_size, "task_update_frame_buffer_size" )
[docs] def _task_update_frame_buffer_size(self, task): """Panda3D task, checks for frame buffer size changes. If frame buffer size changes, self._on_resize is called. This task runs indefinitely. """ if self._frame_buffer_size != self.win.fb_size: self._frame_buffer_size = self.win.fb_size self._on_resize() return task.cont
[docs] def _on_resize(self): """Perform operations required after frame buffer resize.""" self._camera_controller.stop_movement()
# Model
[docs] def _remove_model_node(self): """Remove current model node, if it exists.""" if self.model_node: self.model_node.removeNode()
[docs] def set_model_node_manager(self, manager: Optional[ModelManager]): """Set new model. Removes current model node, if it exists. Creates new build area. Parameters ---------- manager: ModelManager """ if manager: # Create new build area self._set_build_area( self._get_new_build_area( size=manager.model_info.boundaries.point_max * 1.05, size_min=self._BUILD_AREA_BOUNDARIES_DEFAULT.size, ) ) # Remove current model self._remove_model_node() # Add new model node manager.model_node.reparentTo(self.render) self.set_focal_point( focal_point=manager.model_info.boundaries_without_priming.center ) # Create new camera controller self._set_camera_controller(self._get_new_camera_controller(activate=True)) self._model_node_manager = manager
# Camera
[docs] def set_camera_mode(self, camera_mode: CameraMode): """Set camera mode. If camera_mode is the same as current camera mode, nothing changes. Otherwise camera_mode is set as current camera mode and appriopriate CameraController is setup in place of the current one. Parameters ---------- camera_mode : CameraMode Target camera mode to set. """ if self._camera_mode != camera_mode: self._camera_mode = camera_mode self._set_camera_controller(self._get_new_camera_controller(activate=True))
[docs] def _remove_camera_controller(self): if self._camera_controller: self._camera_controller.deactivate() self._camera_controller = None
[docs] def _set_camera_controller(self, controller: CameraController): """Set active camera controller""" self._remove_camera_controller() self._camera_controller = controller
[docs] def _get_new_camera_controller( self, speed: Optional[float] = 2.0, activate: Optional[bool] = False ) -> CameraController: """Create camera controller for current camera mode. Parameters ---------- speed : Optional[float] Camera movement and rotation speed. activate : Optional[bool] If True, the new controller is activated before returning. Otherwise the returned controller is inactive. Returns ------- controller : CameraController Created camera controller. """ if self._camera_mode == CameraMode.SPHERICAL: if not self.focal_point: raise ValueError( f"Focal point must be specified when using camera mode " f"{self._camera_mode.name}." ) controller = OrbitCameraController( camera=self.cam, camera_anchor=self.camera, render=self.render, mouse_watcher_node=self.mouseWatcherNode, win=self.win, focal_point=self.focal_point, speed=speed, ) elif self._camera_mode == CameraMode.FREE: controller = FreeCameraController( camera=self.cam, camera_anchor=self.camera, render=self.render, mouse_watcher_node=self.mouseWatcherNode, win=self.win, focal_point=self.focal_point, speed=speed, ) else: raise NotImplementedError( f"Camera mode {self._camera_mode.name} is currently not supported." ) if activate: controller.activate() return controller
[docs] def focus_on_model(self): """Focus the camera on model's center point.""" self._camera_controller.look_at_focal_point()
[docs] def set_focal_point(self, focal_point: LVector3d): """Set this Visualization's focal point. Parameters ---------- focal_point : LVector3d New focal point. """ self._focal_point = focal_point
# Build Area
[docs] def _remove_build_area(self): """Remove build area node, if it exists.""" if self._build_area_node: self._build_area_node.removeNode() self._build_area_node = None
[docs] def _set_build_area(self, node: NodePath): """Set build area node. If a build area node already exists, it is removed. Parameters ---------- node : NodePath Build area NodePath. """ self._remove_build_area() self._build_area_node = node self._build_area_node.reparentTo(self.render)
[docs] def _get_new_build_area( self, size: LVector3d, size_min: Optional[LVector3d] = None ) -> NodePath: """Create a new build area node. Parameters ---------- size : LVector3d size_min : Optional[LVector3d] Returns ------- build_area_node : NodePath """ build_area_node = BuildAreaNodeBuilder.build_node( loader=self.loader, size=size, size_min=size_min ) return build_area_node
# Lighting
[docs] def _init_lights(self): """Initialize scene lighting. Creates three light sources: - ambient, - directional from top (pitch=-90), - directional from bottom (pitch=90). """ # Ambient ambient_light = AmbientLight("ambient_light") ambient_light.setColor((0.3, 0.3, 0.3, 1)) self._ambient_light_np = self.render.attachNewNode(ambient_light) self.render.setLight(self._ambient_light_np) # Directional from top dir_light_top = DirectionalLight("dir_light_top") dir_light_top.setColor((1, 1, 1, 1)) self._dir_light_top_np = self.render.attachNewNode(dir_light_top) self._dir_light_top_np.setHpr(0, -90, 0) self.render.setLight(self._dir_light_top_np) # Directional from top dir_light_bot = DirectionalLight("dir_light_bot") dir_light_bot.setColor((1, 1, 1, 1)) self._dir_light_bot_np = self.render.attachNewNode(dir_light_bot) self._dir_light_bot_np.setHpr(0, 90, 0) self.render.setLight(self._dir_light_bot_np)