Source code for honeybee_vtk.camera

"""A VTK camera object."""
from __future__ import annotations
import vtk
import math
from pathlib import Path
from typing import Tuple, List
from honeybee_radiance.view import View
from ladybug_geometry.geometry3d import Point3D, LineSegment3D


[docs]class Camera(View): """Create a vtk camera object. Args: identifier: A text string to be used as a name for the camera. Defaults to 'camera'. position: A tuple of three numbers that represent the x, y and z coordinates of camera in a 3D space. Defaults to (0, 0, 50). Which puts the camera 50 units off of ground (XY plane). direction: A tuple of three numbers that represent the x, y, and z components of a vector towards the aim of the camera. Defaults to (0, 0, -1). Which means the camera will look towards the ground (XY plane). up_vector: A tuple of three numbers to represent the x, y, and z component of the vector that represents where the top of the camera is. Defaults to (0, 1, 0). h_size: A number representing the horizontal view angle. Defaults to 60. v_size: A number representing the vertical view angle. Defaults to 60. type: Choose between a perspective and parallel view type. 'v' will set the perspective view and 'l' will set the parallel view. Defaults to 'v' which is the perspective view. """ def __init__( self, identifier: str = 'camera', position: Tuple[float, float, float] = (0, 0, 100), direction: Tuple[float, float, float] = (0, 0, -1), up_vector: Tuple[float, float, float] = (0, 1, 0), h_size: int = 60, v_size: int = 60, type: str = 'v') -> None: super().__init__( identifier=identifier, position=position, direction=direction, up_vector=up_vector, h_size=h_size, v_size=v_size, type=type)
[docs] def to_vtk(self) -> vtk.vtkCamera: """Get a vtk camera object.""" camera = vtk.vtkCamera() # Parallel projection if self._type.value == 'l': camera.SetParallelProjection(True) camera.ParallelProjectionOn() # The location of camera in a 3D space camera.SetPosition(self._position.value) # get a focal_point on the same axis as the camera position. fp = (self._position[0] + self._direction.value[0], self._position[1] + self._direction.value[1], self._position[2] + self._direction.value[2]) # The direction to the point where the camera is looking at camera.SetFocalPoint(fp) # calculate view normal to the camera camera.ComputeViewPlaneNormal() # Where the top of the camera is camera.SetViewUp(self._up_vector.value) # Make sure that view up is 90 degrees to the view normal camera.OrthogonalizeViewUp() # Horizontal view angle camera.SetViewAngle(self._h_size.value) camera.SetUseHorizontalViewAngle(True) camera.UseHorizontalViewAngleOn() return camera
[docs] @classmethod def from_view(cls: Camera, view: View) -> Camera: """Create a Camera object from a radiance view. Args: view: A radiance view Returns: A Camera object. """ return cls( identifier=view.identifier, position=view.position, direction=view.direction, up_vector=view.up_vector, h_size=view.h_size, v_size=view.v_size, type=view.type)
[docs] @classmethod def from_view_file(cls: Camera, file_path: str) -> Camera: """Create a Camera object from a radiance view file. Args: file_path: A valid path to a radiance view file with .vf extension. Returns: A Camera object. """ view_file = Path(file_path) if view_file.is_file() and view_file.as_posix()[-3:] == '.vf': return Camera.from_view(view=View.from_file(view_file.as_posix())) else: raise FileNotFoundError( 'Radiance view file not found.' )
[docs] @classmethod def aerial_cameras(cls: Camera, bounds: List[Point3D], centroid) -> List[Camera]: """Get four aerial cameras. Args: bounds: A list of Point3D objects representing bounds of the actors in the scene. centroid: A Point3D object representing the centroid of the actors. Returns: A list of Camera objects. """ # find the top most z-cordinate in the model cord_point = {point.z: point for point in bounds} topmost_z_cord = cord_point[sorted(cord_point, reverse=True)[0]].z # move centroid to the level of top most z-cordinate centroid_moved = Point3D(centroid.x, centroid.y, topmost_z_cord) # distance of four cameras from centroid distances = [centroid.distance_to_point(pt) for pt in bounds] farthest_distance = sorted(distances, reverse=True) camera_distance = farthest_distance[0] # generate total four points at 45 degrees and -45 degrees on left and right # side of the centroid pt0 = Point3D( centroid_moved.x + math.cos(math.radians(45))*camera_distance, centroid_moved.y + math.sin(math.radians(45))*camera_distance, centroid_moved.z) pt1 = Point3D( centroid_moved.x + math.cos(math.radians(-45))*camera_distance, centroid_moved.y + math.sin(math.radians(-45))*camera_distance, centroid_moved.z) pt2 = Point3D( centroid_moved.x + math.cos(math.radians(45))*camera_distance*-1, centroid_moved.y + math.sin(math.radians(45))*camera_distance*-1, centroid_moved.z) pt3 = Point3D( centroid_moved.x + math.cos(math.radians(-45))*camera_distance*-1, centroid_moved.y + math.sin(math.radians(-45))*camera_distance*-1, centroid_moved.z) camera_points = [pt0, pt3, pt2, pt1] # get directions (vectors) from each point to the centroid directions = [LineSegment3D.from_end_points( pt, centroid).v for pt in camera_points] # default camera identifiers names = ['45_degrees', '315_degrees', '225_degrees', '135_degrees'] # create cameras from four points. These cameras look at the centroid. default_cameras = [] for i in range(len(camera_points)): point = camera_points[i] direction = directions[i] default_cameras.append(cls(identifier=names[i], position=(point.x, point.y, point.z), direction=(direction.x, direction.y, direction.z), up_vector=(0, 0, 1))) return default_cameras