# coding=utf-8
"""
Honeybee test room.
This class generates prototype rooms.
"""
import math
from .hbsurface import HBSurface
from .hbzone import HBZone
from .vectormath.euclid import Point3, Vector3
from .radiance.view import View
from .radiance.analysisgrid import AnalysisGrid
[docs]class Room(HBZone):
"""Honeybee room.
Attribute:
origin: Origin of the room as a tuple (default: (0, 0, 0)).
width: Room width.
depth: Room depth.
height: Room height.
rotation_angle: Clock-wise rotation angle of the room from YAxis.
"""
def __init__(self, name=None, origin=(0, 0, 0), width=3.0, depth=6.0, height=3.2,
rotation_angle=0):
"""Init room."""
self._width = float(width) or 3.0
self._depth = float(depth) or 6.0
self._height = float(height) or 3.2
self._rotation_angle = float(rotation_angle) or 0.0
self._z_axis = Vector3(0, 0, 1)
self._x_axis = Vector3(1, 0, 0).rotate_around(
self._z_axis, math.radians(rotation_angle))
self._y_axis = Vector3(0, 1, 0).rotate_around(
self._z_axis, math.radians(rotation_angle))
name = name or 'room'
origin = Point3(*tuple(origin)) if origin else Point3(0, 0, 0)
# setting up origin will initiate recalculation of room
HBZone.__init__(self, name, origin)
@property
def origin(self):
"""origin of the room."""
return self._origin
@origin.setter
def origin(self, value):
try:
self._origin = Point3(*value)
except Exception as e:
raise ValueError("Failed to set zone origin: {}".format(e))
else:
self._recalculate_room()
@property
def width(self):
"""Room width."""
return self._width
@width.setter
def width(self, value):
self._width = float(value) or 3.0
self._recalculate_room()
@property
def depth(self):
"""Room depth."""
return self._depth
@depth.setter
def depth(self, value):
self._depth = float(value) or 3.0
self._recalculate_room()
@property
def height(self):
"""Room height."""
return self._height
@height.setter
def height(self, value):
self._height = float(value) or 3.0
self._recalculate_room()
@property
def rotation_angle(self):
"""Room rotation_angle."""
return self._rotation_angle
@rotation_angle.setter
def rotation_angle(self, value):
self._rotation_angle = float(value) or 0.0
self._recalculate_room()
[docs] def add_fenestration_surface(self, wall_name, width, height, sill_height,
window_name=None, radiance_material=None):
u"""Add rectangular fenestration surface to surface.
Args:
wall_name: Target wall name (back, right, front, left)
width: Opening width. Opening will be centered in HBSurface.
height: Opening height.
sill_height: Sill height (default: 1).
radiance_material: Optional radiance material for this fenestration.
Usage:
r = Room()
for pt in r.generate_test_points():
print(pt)
r.add_fenestration_surface('back', 2, 2, .7)
r.add_fenestration_surface('right', 4, 1.5, .5)
r.add_fenestration_surface('right', 4, 0.5, 2.2)
with open('c:/ladybug/room.rad', 'wb') as outf:
outf.write(r.to_rad_string(include_materials=True))
"""
# find the wall
try:
wall = tuple(srf for srf in self.surfaces
if srf.name == '%s_wall' % wall_name.lower())[0]
except BaseException:
raise ValueError('Cannot find {} wall'.format(wall_name))
name = window_name or '{}_glazing_{}'.format(wall_name.lower(),
len(wall.children_surfaces))
wall.add_fenestration_surface_by_size(name, width, height, sill_height,
radiance_material)
[docs] def generate_test_points(self, grid_size=1, height=0.75):
"""Generate a grid of test points in the room.
Args:
grid_size: Size of test grid.
height: Test points height.
"""
# find number of divisions in width
u_count = int(self.width / grid_size)
u_step = 1.0 / u_count
u_values = tuple((i * u_step) + (grid_size / (2.0 * self.width))
for i in range(u_count))
# find number of divisions in depth
v_count = int(self.depth / grid_size)
v_step = 1.0 / v_count
v_values = tuple((i * v_step) + (grid_size / (2.0 * self.depth))
for i in range(v_count))
z = float(height) / self.height
points = tuple(self.get_location(u, v, z)
for v in v_values
for u in u_values
)
return AnalysisGrid.from_points_and_vectors(points)
[docs] def get_location(self, u=0.5, v=0.5, z=0.5):
"""Get location as a point based on u, v, z.
u, v, z must be between 0..1.
"""
x = u * self.width * self._x_axis
y = v * self.depth * self._y_axis
z = z * self.height * self._z_axis
return self.origin + x + y + z
[docs] def generate_interior_view(self, u=0.5, v=0.5, z=0.5, angle=0,
view_up_vector=(0, 0, 1), view_type=0, view_h_size=60,
view_v_size=60, x_resolution=64, y_resolution=64,
view_shift=0, view_lift=0):
u"""Generate an inetrior view.
Args:
u: u value between 0 and 1.
v: v value between 0 and 1.
z: z value between 0 and 1.
angle: Rotation angle from back wall.
view_up_vector: Set the view up (-vu) vector (vertical direction) to
(x, y, z).cDefault: (0, 0, 1)
view_type: Set view type (-vt) to one of the choices below.
0: Perspective (v)
1: Hemispherical fisheye (h)
2: Parallel (l)
3: Cylindrical panorma (c)
4: Angular fisheye (a)
5: Planisphere [stereographic] projection (s)
For more detailed description about view types check rpict manual
page: (http://radsite.lbl.gov/radiance/man_html/rpict.1.html)
view_h_size: Set the view horizontal size (-vs). For a perspective
projection (including fisheye views), val is the horizontal field
of view (in degrees). For a parallel projection, val is the view
width in world coordinates.
view_v_size: Set the view vertical size (-vv). For a perspective
projection (including fisheye views), val is the horizontal field
of view (in degrees). For a parallel projection, val is the view
width in world coordinates.
x_resolution: Set the maximum x resolution (-x) to an integer.
y_resolution: Set the maximum y resolution (-y) to an integer.
view_shift: Set the view shift (-vs). This is the amount the actual
image will be shifted to the right of the specified view. This
option is useful for generating skewed perspectives or rendering
an image a piece at a time. A value of 1 means that the rendered
image starts just to the right of the normal view. A value of −1
would be to the left. Larger or fractional values are permitted
as well.
view_lift: Set the view lift (-vl) to a value. This is the amount the
actual image will be lifted up from the specified view.
"""
v = View(self.get_location(u, v, z),
self._y_axis.rotate_around(self._z_axis, math.radians(angle)),
view_up_vector, view_type, view_h_size, view_v_size,
x_resolution, y_resolution, view_shift, view_lift)
return v
@property
def vertices(self):
"""Return the room vertices."""
return (self.pt0, self.pt1, self.pt2, self.pt3,
self.pt4, self.pt5, self.pt6, self.pt7)
@property
def surfaces(self):
"""Return room surfaces."""
return (self.floor, self.ceiling, self.back_wall, self.right_wall,
self.front_wall, self.left_wall)
def _recalculate_room(self):
# create 8 points
self._calculate_vertices()
self._create_hb_surfaces()
def _calculate_vertices(self):
self.pt0 = self.origin
self.pt1 = self.origin + self.width * self._x_axis
self.pt2 = self.pt1 + self.depth * self._y_axis
self.pt3 = self.origin + self.depth * self._y_axis
self.pt4 = self.pt0 + self.height * self._z_axis
self.pt5 = self.pt1 + self.height * self._z_axis
self.pt6 = self.pt2 + self.height * self._z_axis
self.pt7 = self.pt3 + self.height * self._z_axis
def _create_hb_surfaces(self):
self.floor = HBSurface(
'floor', sorted_points=(self.pt0, self.pt3, self.pt2, self.pt1))
self.ceiling = HBSurface(
'ceiling', sorted_points=(self.pt4, self.pt5, self.pt6, self.pt7))
self.back_wall = HBSurface(
'back_wall', sorted_points=(self.pt0, self.pt1, self.pt5, self.pt4))
self.right_wall = HBSurface(
'right_wall', sorted_points=(self.pt1, self.pt2, self.pt6, self.pt5))
self.front_wall = HBSurface(
'front_wall', sorted_points=(self.pt2, self.pt3, self.pt7, self.pt6))
self.left_wall = HBSurface(
'left_wall', sorted_points=(self.pt3, self.pt0, self.pt4, self.pt7))