Skip to content

Commit

Permalink
Merge e2d9a48 into 28da196
Browse files Browse the repository at this point in the history
  • Loading branch information
devang-chauhan committed Feb 10, 2022
2 parents 28da196 + e2d9a48 commit f97e598
Show file tree
Hide file tree
Showing 17 changed files with 688 additions and 178 deletions.
33 changes: 30 additions & 3 deletions honeybee_vtk/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@ class Actor:
Args:
modeldataset: A ModelDataSet object from a honeybee-vtk Model.
data_to_show: Name of the data loaded on the model. This is used in adding legends
for the data to the scene. If not specified all the legends are added.
Defaults to None.
"""

def __init__(self, modeldataset: ModelDataSet) -> None:
def __init__(self, modeldataset: ModelDataSet, data_to_show: str = None) -> None:
self.modeldataset = modeldataset
self._name = self._modeldataset.name
self._monochrome_color = None
self._data_to_show = data_to_show

@property
def modeldataset(self) -> ModelDataSet:
Expand Down Expand Up @@ -53,8 +57,31 @@ def monochrome_color(self) -> Tuple[float, float, float]:
@property
def legend_parameters(self) -> List[LegendParameter]:
"""Legend parameters in the DataFieldInfo of ModelDataSet of this actor."""
return [info.legend_parameter for info in self._modeldataset.fields_info.values()
if info.legend_parameter]
if not self._data_to_show:
return [info.legend_parameter for info in self._modeldataset.fields_info.values()
if info.legend_parameter]
return[self._modeldataset.fields_info[self._data_to_show].legend_parameter]

@property
def bounds(self) -> List[Point3D]:
"""Bounds of the actor."""

vtk_actor = self.to_vtk()
bounds = vtk_actor.GetBounds()
pt_min = Point3D(bounds[0], bounds[2], bounds[4])
pt_max = Point3D(bounds[1], bounds[3], bounds[5])
return [pt_min, pt_max]

@property
def centroid(self) -> Point3D:
"""Centroid of the actor."""
points = self.bounds

x = sum([point.x for point in points]) / len(points)
y = sum([point.y for point in points]) / len(points)
z = sum([point.z for point in points]) / len(points)

return Point3D(x, y, z)

def get_monochrome(self, monochrome_color: Tuple[float, float, float]) -> None:
"""Get actors in monochrome color.
Expand Down
9 changes: 6 additions & 3 deletions honeybee_vtk/assistant.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import vtk
import pathlib
from typing import Tuple
from .actor import Actor
from .camera import Camera
from .types import ImageTypes

Expand All @@ -21,6 +20,7 @@ class Assistant:
actors: A dictionary of actors from a Scene object
camera: A Camera object.
legend_parameters: A list of legend parameter objects to be added to the scene
"""

def __init__(self, background_color: Tuple[int, int, int], camera: Camera,
Expand All @@ -31,7 +31,8 @@ def __init__(self, background_color: Tuple[int, int, int], camera: Camera,
self._camera = camera
self._legend_params = legend_parameters

def _create_window(self) -> None:
def _create_window(self) -> \
Tuple[vtk.vtkRenderWindowInteractor, vtk.vtkRenderWindow, vtk.vtkRenderer]:
"""Create a rendering window with a single renderer and an interactor.
The objects are embedded inside each other. interactor is the outmost layer.
Expand Down Expand Up @@ -81,7 +82,9 @@ def _create_window(self) -> None:
renderer.TwoSidedLightingOn()

renderer.SetActiveCamera(self._camera.to_vtk())
renderer.ResetCamera()

if self._camera.reset_camera:
renderer.ResetCamera()

# the order is from outside to inside
return interactor, window, renderer
Expand Down
205 changes: 135 additions & 70 deletions honeybee_vtk/camera.py
Original file line number Diff line number Diff line change
@@ -1,86 +1,155 @@
"""A VTK camera object."""
from __future__ import annotations


import vtk
import math
from pathlib import Path
from typing import Tuple, List
from typing import Tuple, List, Union, TypeVar, Type
from honeybee_radiance.view import View
from ladybug_geometry.geometry3d import Point3D, LineSegment3D


T = TypeVar('T', bound='Camera')


def _get_focal_point(focal_point: Union[Tuple[float, float, float], None],
position: Tuple[float, float, float],
direction: Tuple[float, float, float]) -> Tuple[float, float, float]:
"""Set the focal point of the camera.
Args:
focal_point: x, y, z coordinates of the focal point. If not set, the focal
point will be set by moving the point of camera position in the direction
of the camera direction.
position: x, y, z coordinates of the camera position.
direction: x, y, z components of the camera direction.
Returns:
x, y, z coordinates of the focal point.
"""
if not focal_point:
return (position[0] + direction[0], position[1] + direction[1], position[2] +
direction[2])
return focal_point


def _apply_projection(camera: Type[T], projection: str, parallel_scale: int,
clipping_range: Tuple[float, float]) -> vtk.vtkCamera:
"""Set the parallel projection camera.
Args:
camera: A VTK camera object.
projection: The projection type. 'v' or 'l'
parallel_scale: The parallel scale of the camera.
clipping_range: The clipping range of the camera.
Returns:
A VTK camera object.
"""
if projection == 'l':
camera.ParallelProjectionOn()
if parallel_scale:
camera.SetParallelScale(parallel_scale)
if clipping_range:
camera.SetClippingRange(clipping_range)
return camera
return camera


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.
This object inherits from the Honeybee_radiance.View object.
https://www.ladybug.tools/honeybee-radiance/docs/honeybee_radiance.view.html
Args:
identifier: A unique name for the camera. Defaults to 'camera'.
position: x, y, z coordinates of the camera in a 3D space. Defaults to (0, 0, 100)
which puts the camera 100 meters off of the XY plane (ground).
direction: x, y, and z components of a vector that represents the view direction
(aim) of the camera. Defaults to (0, 0, -1). Which means the camera will look
towards the XY plane (ground).
up_vector: x, y, and z component of the vector that represents where the top
of the camera is. Defaults to (0, 1, 0).
view_angle: The angular hight of the camera view in degrees. You can think of
this as the vertical view angle. Defaults to 60
projection: Choose between a perspective and parallel view type. 'v' will set
the perspective view and 'l' will set the parallel view. Defaults to 'v'.
reset_camera: A boolean that indicates whether the camera should be reset.
Resetting the camera is helpful when you want to capture an image of a
model from outside the model. This will make sure that the camera is far
away from the model and the whole model is captured. This should be set
to false in case of the intention is to take snapshots inside the model.
A use case is taking the snapshots of the grids in the model.
Defaults to True.
focal_point: x, y, and z coordinates of the point where the camera is looking at.
Defaults to None which means the camera will look towards the XY plane
(ground).
clipping_range: A range of two numbers that define the near plane and the far
plane respectively. Both of these planes are perpendicular to the camera
direction and are effective only in when the view type is parallel. The
distance from the camera to the near plane is the closest distance that
an object can be to the camera and still remain in the view. The distance
from the camera to the far plane is the farthest distance that an object
can be from the camera and still remain in the view. Defaults to None.
parallel_scale: Set the parallel scale for the camera. Note, that this parameters
works as an inverse scale. So larger numbers produce smaller images.This can
be thought of as the zoom in and zoom out control. This parameter is effective
only when the view type is parallel. Defaults to None.
"""

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:
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),
view_angle: int = 60,
projection: str = 'v',
reset_camera: bool = True,
focal_point: Union[Tuple[float, float, float], None] = None,
clipping_range: Union[Tuple[float, float], None] = None,
parallel_scale: Union[int, None] = None) -> None:

super().__init__(
identifier=identifier, position=position, direction=direction,
up_vector=up_vector, h_size=h_size, v_size=v_size, type=type)
up_vector=up_vector, h_size=view_angle, type=projection)

self._view_angle = view_angle
self._projection = projection
self._reset_camera = reset_camera
self._focal_point = focal_point
self._clipping_range = clipping_range
self._parallel_scale = parallel_scale

@property
def reset_camera(self) -> bool:
"""Get a boolean that indicates whether the camera should be reset."""
return self._reset_camera

@reset_camera.setter
def reset_camera(self, reset_camera: bool) -> None:
"""Set a boolean that indicates whether the camera should be reset. This is
helpful in case of the intention is to take snapshots inside the model.
"""
self._reset_camera = reset_camera

def to_vtk(self) -> vtk.vtkCamera:
"""Get a vtk camera object."""
camera = vtk.vtkCamera()
"""Get a vtk camera object"""

# 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 = vtk.vtkCamera()
camera.SetPosition(self.position)
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()
camera.SetViewUp(self.up_vector)

camera.SetViewAngle(self._view_angle)
camera.SetFocalPoint(_get_focal_point(self._focal_point, self.position,
self.direction))
camera = _apply_projection(camera, self._projection, self._parallel_scale,
self._clipping_range)
camera.OrthogonalizeViewUp()
return camera

@classmethod
def from_view(cls: Camera, view: View) -> Camera:
def from_view(cls: Type[T], view: View) -> T:
"""Create a Camera object from a radiance view.
Args:
Expand All @@ -89,17 +158,13 @@ def from_view(cls: Camera, view: View) -> Camera:
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)
return cls(identifier=view.identifier, position=view.position,
direction=view.direction, up_vector=view.up_vector,
view_angle=view.h_size if view.h_size > view.v_size else view.v_size,
projection=view.type)

@classmethod
def from_view_file(cls: Camera, file_path: str) -> Camera:
def from_view_file(cls: Type[T], file_path: str) -> T:
"""Create a Camera object from a radiance view file.
Args:
Expand All @@ -112,14 +177,14 @@ def from_view_file(cls: Camera, file_path: str) -> Camera:
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()))
return cls.from_view(view=View.from_file(view_file.as_posix()))
else:
raise FileNotFoundError(
'Radiance view file not found.'
)

@classmethod
def aerial_cameras(cls: Camera, bounds: List[Point3D], centroid) -> List[Camera]:
def aerial_cameras(cls: Type[T], bounds: List[Point3D], centroid: Point3D) -> List[T]:
"""Get four aerial cameras.
Args:
Expand Down
2 changes: 2 additions & 0 deletions honeybee_vtk/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import click
from .translate import translate
from .export import export
from .export_grid import export_grid
from .config import config
from honeybee.cli import main

Expand All @@ -19,5 +20,6 @@ def vtk():
vtk.add_command(translate)
vtk.add_command(export)
vtk.add_command(config)
vtk.add_command(export_grid)

main.add_command(vtk)
Loading

0 comments on commit f97e598

Please sign in to comment.