Source code for honeybee_radiance.dynamic.stategeo

# coding: utf-8
"""Dynamic geometry that can be assigned to individual states."""
from ..modifier import Modifier
from ..geometry import Polygon
from ..mutil import dict_to_modifier  # imports all modifiers classes
from ..lib.modifiers import black, generic_exterior_shade

from honeybee.typing import valid_rad_string
from ladybug_geometry.geometry3d.pointvector import Point3D
from ladybug_geometry.geometry3d.face import Face3D

import math


[docs]class StateGeometry(object): """A single planar geometry that can be assigned to Radiance states. Args: identifier: Text string for a unique geometry ID. Must not contain any spaces or special characters. geometry: A ladybug-geometry Face3D. modifier: A Honeybee Radiance Modifier object for the geometry. If None, it will be the Generic Exterior Shade modifier in the lib. (Default: None). Properties: * identifier * display_name * geometry * modifier * modifier_direct * parent * has_parent * vertices * normal * center * area """ __slots__ = ('_identifier', '_display_name', '_geometry', '_modifier', '_modifier_direct', '_parent') def __init__(self, identifier, geometry, modifier=None): """Initialize StateGeometry.""" # process the identifier self._identifier = valid_rad_string(identifier, 'state geometry identifier') self._display_name = self._identifier # process the geometry assert isinstance(geometry, Face3D), \ 'Expected ladybug_geometry Face3D. Got {}'.format(type(geometry)) self._geometry = geometry # process the modifier self.modifier = modifier self._modifier_direct = None self._parent = None # _parent will be set when the Geo is added to a state
[docs] @classmethod def from_dict(cls, data): """Initialize a StateGeometry from a dictionary. Note that the dictionary must be a non-abridged version for this classmethod to work. Args: data: A dictionary representation of an StateGeometry with the format below. .. code-block:: python { 'type': 'StateGeometry', 'identifier': str, # Text for the unique object identifier 'display_name': str, # Optional text for the display name 'geometry': {}, # A ladybug_geometry Face3D dictionary 'modifier': {}, # A Honeybee Radiance Modifier dictionary 'modifier_direct': {} # A Honeybee Radiance Modifier dictionary } """ # check the type of dictionary assert data['type'] == 'StateGeometry', 'Expected StateGeometry dictionary. ' \ 'Got {}.'.format(data['type']) geo = cls(data['identifier'], Face3D.from_dict(data['geometry'])) if 'modifier' in data and data['modifier'] is not None: geo.modifier = dict_to_modifier(data['modifier']) if 'modifier_direct' in data and data['modifier_direct'] is not None: geo.modifier_direct = dict_to_modifier(data['modifier_direct']) if 'display_name' in data and data['display_name'] is not None: geo.display_name = data['display_name'] return geo
[docs] @classmethod def from_dict_abridged(cls, data, modifiers): """Create StateGeometry from an abridged dictionary. Args: data: A dictionary representation of StateGeometryAbridged with the format below. modifiers: A dictionary of modifiers with modifier identifiers as keys, which will be used to re-assign modifiers. .. code-block:: python { 'type': 'StateGeometryAbridged', 'identifier': str, # Text for the unique object identifier 'display_name': str, # Optional text for the display name 'geometry': {}, # A ladybug_geometry Face3D dictionary 'modifier': str # A Honeybee Radiance Modifier identifier 'modifier_direct': str # A Honeybee Radiance Modifier identifier } """ # check the type of dictionary assert data['type'] == 'StateGeometryAbridged', \ 'Expected StateGeometryAbridged dictionary. Got {}.'.format(data['type']) geo = cls(data['identifier'], Face3D.from_dict(data['geometry'])) if 'modifier' in data and data['modifier'] is not None: geo.modifier = modifiers[data['modifier']] if 'modifier_direct' in data and data['modifier_direct'] is not None: geo.modifier_direct = modifiers[data['modifier_direct']] if 'display_name' in data and data['display_name'] is not None: geo.display_name = data['display_name'] return geo
[docs] @classmethod def from_vertices(cls, identifier, vertices, modifier=None): """Create StateGeometry from vertices with each vertex as an iterable of 3 floats. Note that this method is not recommended for a geometry with one or more holes since the distinction between hole vertices and boundary vertices cannot be derived from a single list of vertices. Args: identifier: Text string for a unique geometry ID. Must not contain any spaces or special characters. vertices: A flattened list of 3 or more vertices as (x, y, z). modifier: A Honeybee Radiance Modifier object for the geometry. If None, it will be the Generic Exterior Shade modifier in the lib. (Default: None). """ geometry = Face3D(tuple(Point3D(*v) for v in vertices)) return cls(identifier, geometry, modifier)
@property def identifier(self): """Get a text string for the unique object identifer. This identifier remains constant as the object is mutated, copied, and serialized to different formats (eg. dict, idf, rad). As such, this property is used to reference the object across a Model. """ return self._identifier @property def display_name(self): """Get or set a string for the object name without any character restrictions. If not set, this will be equal to the identifier. """ return self._display_name @display_name.setter def display_name(self, value): try: self._display_name = str(value) except UnicodeEncodeError: # Python 2 machine lacking the character set self._display_name = value # keep it as unicode @property def geometry(self): """Get a ladybug_geometry Face3D object representing the Shade.""" return self._geometry @property def modifier(self): """Get or set the object modifier.""" if self._modifier: # set by user return self._modifier return generic_exterior_shade @modifier.setter def modifier(self, value): if value is not None: assert isinstance(value, Modifier), \ 'Expected Radiance Modifier for shade. Got {}'.format(type(value)) value.lock() # lock editing in case modifier has multiple references self._modifier = value @property def modifier_direct(self): """Get or set a modifier to be used in direct solar studies. If None, this will be a completely black material if the object's modifier is opaque and will be equal to the modifier if the object's modifier is non-opaque. """ if self._modifier_direct: # set by user return self._modifier_direct mod = self.modifier # assign a default based on whether the modifier is opaque if mod.is_void or mod.is_opaque: return black else: return mod @modifier_direct.setter def modifier_direct(self, value): if value is not None: assert isinstance(value, Modifier), \ 'Expected Radiance Modifier. Got {}'.format(type(value)) value.lock() # lock editing in case modifier has multiple references self._modifier_direct = value @property def is_opaque(self): """Boolean noting whether this geomtry has an opaque modifier.""" return True if self.modifier.is_void else self.modifier.is_opaque @property def parent(self): """Get the parent State if assigned. None if not assigned.""" return self._parent @property def has_parent(self): """Get a boolean noting whether this StateGeometry has a parent State.""" return self._parent is not None @property def vertices(self): """Get a list of vertices for the geometry (in counter-clockwise order).""" return self._geometry.vertices @property def normal(self): """Get a ladybug_geometry Vector3D for the direction the geometry is pointing. """ return self._geometry.normal @property def center(self): """Get a ladybug_geometry Point3D for the center of the geometry. Note that this is the center of the bounding rectangle around this geometry and not the area centroid. """ return self._geometry.center @property def area(self): """Get the area of the geometry.""" return self._geometry.area
[docs] def move(self, moving_vec): """Move this StateGeometry along a vector. Args: moving_vec: A ladybug_geometry Vector3D with the direction and distance to move the face. """ self._geometry = self.geometry.move(moving_vec)
[docs] def rotate(self, axis, angle, origin): """Rotate this StateGeometry by a certain angle around an axis and origin. Args: axis: A ladybug_geometry Vector3D axis representing the axis of rotation. angle: An angle for rotation in degrees. origin: A ladybug_geometry Point3D for the origin around which the object will be rotated. """ self._geometry = self.geometry.rotate(axis, math.radians(angle), origin)
[docs] def rotate_xy(self, angle, origin): """Rotate this StateGeometry counterclockwise in the XY plane by a certain angle. Args: angle: An angle in degrees. origin: A ladybug_geometry Point3D for the origin around which the object will be rotated. """ self._geometry = self.geometry.rotate_xy(math.radians(angle), origin)
[docs] def reflect(self, plane): """Reflect this StateGeometry across a plane. Args: plane: A ladybug_geometry Plane across which the object will be reflected. """ self._geometry = self.geometry.reflect(plane.n, plane.o)
[docs] def scale(self, factor, origin=None): """Scale this StateGeometry by a factor from an origin point. Args: factor: A number representing how much the object should be scaled. origin: A ladybug_geometry Point3D representing the origin from which to scale. If None, it will be scaled from the World origin (0, 0, 0). """ self._geometry = self.geometry.scale(factor, origin)
[docs] def to_dict(self, abridged=False): """Return StateGeometry as a dictionary. Args: abridged: Boolean to note whether the full dictionary describing the object should be returned (False) or just an abridged version (True). Default: False. """ # assign required properties base = {'type': 'StateGeometryAbridged'} if abridged else \ {'type': 'StateGeometry'} base['identifier'] = self.identifier base['display_name'] = self.display_name base['geometry'] = self.geometry.to_dict() # assign optional properties if self._modifier: base['modifier'] = self._modifier.identifier if abridged else \ self._modifier.to_dict() if self._modifier_direct is not None: base['modifier_direct'] = self._modifier_direct.identifier if abridged \ else self._modifier.to_dict() return base
[docs] def to_radiance(self, direct=False, minimal=False): """Generate a RAD string representation of this StateGeometry. Note that the resulting string lacks modifiers. Args: direct: Boolean to note whether to write the "direct" version of the state, which will have the modifier_direct applied. (Default: False) minimal: Boolean to note whether the radiance string should be written in a minimal format (with spaces instead of line breaks). Default: False. """ modifier = self.modifier_direct if direct else self.modifier base_poly = Polygon(self.identifier, self.vertices, modifier) return base_poly.to_radiance(minimal, False, False)
[docs] def duplicate(self): """Get a copy of this object.""" return self.__copy__()
def __copy__(self): new_geo = StateGeometry(self.identifier, self.geometry, self._modifier) new_geo._modifier_direct = self._modifier_direct new_geo._display_name = self.display_name return new_geo
[docs] def ToString(self): return self.__repr__()
def __repr__(self): return 'StateGeometry: %s' % self.display_name