Source code for honeybee_vtk.scene

"""A VTK scene."""

from __future__ import annotations

from typing import Dict, List, Tuple, Union

import vtk

from ._helper import _validate_input
from .actor import Actor
from .assistant import Assistant
from .camera import Camera
from .headless import try_headless
from .legend_parameter import LegendParameter
from .types import ImageTypes


[docs]class Scene: """Initialize a Scene object. Vtk derives inspiration from a movie set in naming its objects. Imagine a scene being prepared at a movie set. The scene has a background. It has a few actors, there are assistants running around making sure everything is ready, and there are a few cameras setup to capture the scene from different angles. A scene in vtk is defined in the similar fashion. It has a background color, some actors i.e. geometry objects from model, a few cameras around the scene, and assistants equal the number of cameras setup in the scene. Args: background_color: A tuple of three integers that represent RGB values of the color that you'd like to set as the background color. Defaults to gray. """ def __init__(self, background_color: Tuple[int, int, int] = None) -> None: self.background_color = background_color self._actors = {} self._cameras = [] self._assistants = [] @property def background_color(self) -> Tuple[int, int, int]: """background_color for the scene.""" return self._background_color @background_color.setter def background_color(self, val: Tuple[int, int, int]) -> None: if not val: colors = vtk.vtkNamedColors() self._background_color = colors.GetColor3d("SlateGray") elif _validate_input(val, [int]): self._background_color = val else: raise ValueError( 'Background color is a tuple with three integers' ' representing R, G, and B values.' ) @property def actors(self) -> List[str]: """Name of actors in the scene.""" return self._actors.keys() @property def cameras(self) -> List[Camera]: """A list of honeybee-vtk cameras setup in the scene.""" return self._cameras @property def assistants(self) -> List[Assistant]: """A list of honeybee-vtk assistants working in the scene.""" return self._assistants @property def legend_parameters(self) -> Dict[str, LegendParameter]: """Legends in the scene that can be added to the images.""" legends_dict = {} for actor in self._actors.values(): for legend_param in actor.legend_parameters: legends_dict[legend_param.name] = legend_param return legends_dict
[docs] def legend_parameter(self, name: str) -> LegendParameter: """Get a legend parameter object by name. Args: name: A string for the name of the legend parameters you are looking for. Returns: A legend parameter object. """ if name not in self.legend_parameters.keys(): raise ValueError( 'No legend parameter found by that name in this scene. Make sure cameras' ' and actors a added to the scene. Legends in the scene are' f' {tuple(self.legend_parameters.keys())}.' ) return self.legend_parameters[name]
[docs] def add_cameras(self, val: Union[Camera, List[Camera]]) -> None: """Add a honeybee-vtk Camera objects to a Scene. Args: val: Either a list of Camera objects or a single Camera object. """ if isinstance(val, list) and _validate_input(val, [Camera]): self._cameras.extend(val) elif isinstance(val, Camera): self._cameras.append(val) else: raise ValueError( 'Either a list of Camera objects or a Camera object is expected.' f' Instead got {val}.' )
[docs] def add_actors(self, val: Union[Actor, List[Actor]]) -> None: """add honeybee-vtk Actor objects to a Scene. Args: val: Either a list of Actors objects or a single Actor object. """ if isinstance(val, list) and _validate_input(val, [Actor]): for v in val: self._actors[v.name] = v elif isinstance(val, Actor): self._actors[val.name] = val else: raise ValueError( 'Either a list of Actor objects or an Actor object is expected.' f' Instead got {val}.' )
[docs] def remove_actor(self, name: str) -> None: """Remove an actor from scene by name. Args: name: A string representing the name of the actor you would like to remove from the scene. """ valid_names = tuple(['Aperture', 'Door', 'Shade', 'Wall', 'Floor', 'RoofCeiling', 'AirBoundary', 'Grid']) if name in valid_names: try: del self._actors[name] except KeyError: raise KeyError( f'{name} is not found in the actors in this scene.') else: raise ValueError( 'Name of the actors should be from one the following' f' values {valid_names}.' )
[docs] def update_scene(self) -> None: """Update the scene. This method will use the latest cameras, actors, and visible legend parameters to create assistant object. """ if self._cameras and self._actors: if self.legend_parameters: visible_legend_params = [ legend_param for legend_param in self.legend_parameters.values() if not legend_param.hide_legend] else: visible_legend_params = [] self._assistants = [ Assistant(background_color=self._background_color, camera=camera, actors=self._actors, legend_parameters=visible_legend_params) for camera in self._cameras] else: raise ValueError( 'Add cameras and actors to the scene first.' )
@try_headless def export_images( self, folder: str, image_type: ImageTypes = ImageTypes.png, *, image_scale: int = 1, image_width: int = 0, image_height: int = 0, color_range: vtk.vtkLookupTable = None, rgba: bool = False, show: bool = False) -> List[str]: """Export all the cameras in the scene as images. Reference: https://kitware.github.io/vtk-examples/site/Python/IO/ImageWriter/ This method is able to export an image in '.png', '.jpg', '.ps', '.tiff', '.bmp', and '.pnm' formats. Args: folder: A valid path to where you'd like to write the images. image_type: An ImageType object. image_scale: An integer value as a scale factor. Defaults to 1. image_width: An integer value that sets the width of image in pixels. Defaults to 0, which will use view's x dimension. image_height: An integer value that sets the height of image in pixels. Defaults to 0, which will use view's y dimension. color_range: A vtk lookup table object which can be obtained from the color_range mehtod of the DataFieldInfo object. Defaults to None. rgba: A boolean value to set the type of buffer. A True value sets an the background color to black. A False value uses the Scene object's background color. Defaults to False. show: A boolean value to decide if the the render window should pop up. Defaults to False. Returns: A list of text strings representing the paths to the exported images. """ self.update_scene() return [assistant._export_image( folder=folder, image_type=image_type, image_scale=image_scale, image_width=image_width, image_height=image_height, color_range=color_range, rgba=rgba, show=show) for assistant in self._assistants]
[docs] def export_gltf(self, folder: str, name: str = 'Camera') -> str: """Export a scene to a glTF file. Args: folder: A valid path to where you'd like to write the gltf file. name: Name of the gltf file as a text string. Returns: A text string representing the path to the gltf file. """ self.update_scene() if self._assistants: return self._assistants[0]._export_gltf(folder=folder, name=name) else: raise ValueError( 'At least one camera needs to be setup to export an image.' )