Source code for layerview.app.main_window.model

from typing import Optional, Tuple

from PyQt5.QtCore import QObject, pyqtSignal
from QPanda3D.Panda3DWorld import Panda3DWorld

from layerview.app.constants import UNIT_DEGREES_CELSIUS, UNIT_MM, UNIT_MM_PER_MIN
from layerview.app.main_window.dto import (
    ColoringGroupDTO,
    IntRangeDTO,
    LayerInfoDTO,
    ModelInfoDTO,
    StatusBarMessageDTO,
)
from layerview.app.main_window.errors import VisualizationInitError
from layerview.visualization.nodes.model import ModelManager
from layerview.visualization.point_cloud.model import ModelInfo
from layerview.visualization.world.color import ModelColorizer
from layerview.visualization.world.world import CameraMode, ColoringMode, Visualization


[docs]class Model(QObject): NOZZLE_DIAM_DEFAULT: float = 0.4 MODEL_COLOR_DEFAULT = (30 / 255, 30 / 255, 255 / 255, 1) COLOR_GRADIENT_START = (30 / 255, 30 / 255, 1, 1) COLOR_GRADIENT_END = (1, 30 / 255, 30 / 255, 1) CAMERA_MODE_DEFAULT = CameraMode.SPHERICAL # Signals # Camera update_camera_mode: pyqtSignal = pyqtSignal(CameraMode) # Coloring update_coloring_group: pyqtSignal = pyqtSignal(ColoringGroupDTO) # Visible layer range update_layer_range: pyqtSignal = pyqtSignal(IntRangeDTO) update_visible_layer_range_start: pyqtSignal = pyqtSignal(int) update_visible_layer_range_end: pyqtSignal = pyqtSignal(int) # Info update_layer_info: pyqtSignal = pyqtSignal(LayerInfoDTO) update_model_info: pyqtSignal = pyqtSignal(ModelInfoDTO) # Other update_panda_3d_world: pyqtSignal = pyqtSignal(Panda3DWorld) update_status_bar: pyqtSignal = pyqtSignal(StatusBarMessageDTO) update_model_controls_enabled_state: pyqtSignal = pyqtSignal(bool) def __init__(self): super().__init__() self.model_color_default = self.MODEL_COLOR_DEFAULT self._coloring_mode: ColoringMode = ColoringMode.CONSTANT self._visible_range_start: int = 1 self._visible_range_end: int = 1 self._layer_info_number: int = 1 self._gradient_pixmap_size: Tuple[int, int] = (1, 1) self._visualization: Optional[Visualization] = None self._model_colorizer: ModelColorizer = ModelColorizer( color_default=self.MODEL_COLOR_DEFAULT, color_gradient_start=self.COLOR_GRADIENT_START, color_gradient_end=self.COLOR_GRADIENT_END, ) # Properties @property def model_node_manager(self) -> Optional[ModelManager]: return self._visualization.model_node_manager @property def model_info(self) -> Optional[ModelInfo]: return self._visualization.model_info @property def visible_range_end(self) -> int: return self._visible_range_end @property def visible_range_start(self) -> int: return self._visible_range_start @property def layer_info_number(self) -> int: return self._layer_info_number @property def camera_mode(self) -> CameraMode: return self._visualization.camera_mode # Initialization
[docs] def reset(self): self._reset_visualization() self.set_model_node_manager(None)
[docs] def init_visualization(self): if self._visualization: raise VisualizationInitError("Visualization already initialized.") self._visualization = Visualization(camera_mode=self.CAMERA_MODE_DEFAULT) # Emit panda 3d world to view. self.update_panda_3d_world.emit(self._visualization) # Visualization can be started only after it has been # placed in a QPanda3DWidget self._reset_visualization()
[docs] def _reset_visualization(self): if self._visualization: self._visualization.reset()
# State modifiers
[docs] def set_camera_mode(self, camera_mode: CameraMode): # Propagate change to visualization if self._visualization: self._visualization.set_camera_mode(camera_mode) self.update_camera_mode.emit(self.camera_mode)
[docs] def focus_on_model(self): self._visualization.focus_on_model()
[docs] def set_coloring_mode(self, coloring_mode: ColoringMode): self._coloring_mode = coloring_mode if self.model_node_manager: self._colorize_model_layers() self.update_coloring_group.emit(self._get_coloring_group_dto())
[docs] def set_layer_range(self, layer_range: IntRangeDTO): self.update_layer_range.emit(layer_range)
[docs] def set_visible_range_start(self, layer_num: int): self._visible_range_start = layer_num if self.model_node_manager: self._show_current_layer_range_only() self.update_visible_layer_range_start.emit(self._visible_range_start)
[docs] def set_visible_range_end(self, layer_num: int): self._visible_range_end = layer_num if self.model_node_manager: self._show_current_layer_range_only() self.update_visible_layer_range_end.emit(self._visible_range_end)
[docs] def set_layer_info_number(self, layer_num: int): if self.model_info: self._layer_info_number = layer_num dto = LayerInfoDTO.from_layer_info( layer_info=self.model_info.get_layer_info(layer_num), layer_num=layer_num, z_position=self.model_info.get_layer_z(layer_num), height=self.model_info.get_layer_height(layer_num), ) else: dto = LayerInfoDTO.null_object() self.update_layer_info.emit(dto)
[docs] def set_gradient_pixmap_size(self, size: Tuple[int, int]): self._gradient_pixmap_size = size self.update_coloring_group.emit(self._get_coloring_group_dto())
[docs] def set_status_bar_message(self, message: StatusBarMessageDTO): self.update_status_bar.emit(message)
[docs] def set_model_node_manager(self, manager: Optional[ModelManager]): self._visualization.set_model_node_manager(manager=manager) if manager: # Load new manager self.update_layer_range.emit(IntRangeDTO(*manager.model_info.layer_range)) self.set_visible_range_start(manager.model_info.layer_num_min) self.set_visible_range_end(manager.model_info.layer_num_max) self.set_layer_info_number(1) self.update_coloring_group.emit(self._get_coloring_group_dto()) self._colorize_model_layers() self.update_model_info.emit( ModelInfoDTO.from_model_info(model_info=manager.model_info) ) else: # Reset state self.set_layer_range(IntRangeDTO(1, 1)) self.update_model_info.emit(ModelInfoDTO.null_object()) self.update_layer_info.emit(LayerInfoDTO.null_object()) self.update_coloring_group.emit(self._get_coloring_group_dto()) self.set_status_bar_message(StatusBarMessageDTO(text="Open G-code file"))
[docs] def handle_window_resize(self): pass
[docs] def handle_visualization_focus_out(self): self._visualization.handle_focus_out()
# Protected
[docs] def _show_current_layer_range_only(self): self.model_node_manager.show_layer_range_only( start=self._visible_range_start, end=self._visible_range_end )
[docs] def _colorize_model_layers(self): self._model_colorizer.colorize( model_node_manager=self.model_node_manager, coloring_mode=self._coloring_mode, )
[docs] def _get_coloring_group_dto(self) -> ColoringGroupDTO: model_info = self.model_info coloring_mode = self._coloring_mode color_gradient_dto: ColoringGroupDTO = ColoringGroupDTO( coloring_mode=coloring_mode ) if model_info: val_min: Optional[float] = None val_max: Optional[float] = None if coloring_mode == ColoringMode.CONSTANT: color_gradient_dto.text_center = "Constant color mode" elif coloring_mode == ColoringMode.FEEDRATE: val_min = model_info.feedrate_min val_max = model_info.feedrate_max color_gradient_dto.text_center = UNIT_MM_PER_MIN elif coloring_mode == ColoringMode.THICKNESS: val_min = model_info.layer_height_min val_max = model_info.layer_height_max color_gradient_dto.text_center = UNIT_MM elif coloring_mode == ColoringMode.TEMPERATURE: val_min = model_info.temperature_min val_max = model_info.temperature_max color_gradient_dto.text_center = UNIT_DEGREES_CELSIUS else: raise TypeError(f"Unsupported coloring mode: {coloring_mode}") # Text left and right color_gradient_dto.text_left = ( str(round(val_min, 3)) if val_min is not None else "" ) color_gradient_dto.text_right = ( str(round(val_max, 3)) if val_max is not None else "" ) # Gradient pixmap if val_min == val_max: color_gradient_dto.pixmap = self._model_colorizer.get_gradient_pixmap( size=self._gradient_pixmap_size, coloring_mode=ColoringMode.CONSTANT ) else: color_gradient_dto.pixmap = self._model_colorizer.get_gradient_pixmap( size=self._gradient_pixmap_size, coloring_mode=coloring_mode ) else: color_gradient_dto.text_center = "No model" return color_gradient_dto