Source code for honeybee_idaice.shade
from typing import List, Union
from honeybee.model import Shade, ShadeMesh
from ladybug_geometry.geometry3d import Point3D, Face3D, Polyface3D, Mesh3D
def _vertices_to_idm(vertices: List[Point3D], dec_places: int = 3) -> str:
"""Get a string for vertices in IDM format."""
vertices = ' '.join(
f'({round(v.x, dec_places)} {round(v.y, dec_places)} {round(v.z, dec_places)})'
for v in vertices
)
return vertices
def _shade_geometry_to_idm(
geometry: Union[Face3D, Polyface3D, Mesh3D],
name: str, decimal_places: int = 3
):
"""Create an IDM shade block from a Ladybug geometry.
Here is an example:
((AGGREGATE-VTK :N "shade1" :T PICT3D)
(:PAR :N FILE :V "")
(:PAR :N POS :V #(0 0 0.0))
(:PAR :N SHADOWING :V :TRUE)
((AGGREGATE :N "geom1" :T GEOM3D)
(:PAR :N NPOINTS :V 4)
(:PAR :N POINTS :DIM (4 3) :V #2A((-0.874 -0.59 -0.941) (1.054 -0.059 -0.941) (-1.054 0.06 0.941) (0.874 0.59 0.941)))
(:PAR :N CELLTYPE :V 1)
(:PAR :N NCELLS :V 2)
(:PAR :N NVERTICES :DIM (2) :V #(3 3))
(:PAR :N TOTNVERTS :V 6)
(:PAR :N VERTICES :DIM (6) :V #(0 1 2 2 1 3))
(:PAR :N PROPERTY :V #(1.0 1.0 1.0 0.7 1.0 1.0 1.0 0.5 1.0 1.0 1.0 0.0 1.0 1.0 1.0 0.0 0.0))))
"""
if isinstance(geometry, Face3D):
mesh_3d = geometry.triangulated_mesh3d
elif isinstance(geometry, Mesh3D):
mesh_3d = geometry
else:
# it is a Polyface3D
meshes = [face.triangulated_mesh3d for face in geometry.faces]
mesh_3d = Mesh3D.join_meshes(meshes=meshes)
vertices = mesh_3d.vertices
faces = mesh_3d.faces
vertices_count = len(vertices)
face_count = len(faces)
face_length = [len(face) for face in faces]
total_vertices = sum(face_length)
faces_count = ' '.join(str(f) for f in face_length)
joined_faces = ' '.join(' '.join(str(f) for f in ff) for ff in faces)
shd_verts = _vertices_to_idm(vertices, decimal_places)
shade = f' ((AGGREGATE-VTK :N "{name}" :T PICT3D)\n' \
' (:PAR :N FILE :V "")\n' \
' (:PAR :N SHADOWING :V :TRUE)\n' \
' ((AGGREGATE :N "geom1" :T GEOM3D)\n' \
f' (:PAR :N NPOINTS :V {vertices_count})\n' \
f' (:PAR :N POINTS :DIM ({vertices_count} 3) :V #2A({shd_verts}))\n' \
' (:PAR :N CELLTYPE :V 1)\n' \
f' (:PAR :N NCELLS :V {face_count})\n' \
f' (:PAR :N NVERTICES :DIM ({face_count}) :V #({faces_count}))\n' \
f' (:PAR :N TOTNVERTS :V {total_vertices})\n' \
f' (:PAR :N VERTICES :DIM ({total_vertices}) :V #({joined_faces}))\n' \
' (:PAR :N PROPERTY :V #(1.0 1.0 1.0 0.699999988079071 1.0 1.0 1.0 0.5 1.0 1.0 1.0 0.0 1.0 1.0 1.0 0.0 0.0)))\n' \
f' )'
return shade
def _shade_group_to_idm(
shades: List[Shade], tolerance: float, decimal_places: int = 3
) -> str:
"""Convert a group of shades into a IDM string.
The files in the shade group should create a closed volume. The translator uses
the name of the first shade as the name of the group.
"""
group_geometry = Polyface3D.from_faces(
[shade.geometry for shade in shades], tolerance=tolerance
)
shade = shades[0]
# remove new lines from the name
name = '_'.join(
(' '.join(shade.display_name.split()), shade.identifier.replace('Shade_', ''))
)
return _shade_geometry_to_idm(group_geometry, name, decimal_places)
def _shade_to_idm(shade: Shade, decimal_places: int = 3):
shade_geo = shade.geometry
name = '_'.join(
(' '.join(shade.display_name.split()), shade.identifier.replace('Shade_', ''))
)
return _shade_geometry_to_idm(shade_geo, name, decimal_places)
[docs]
def shades_to_idm(shades: List[Shade], tolerance: float, decimal_places: int = 3):
"""Convert a list of Shades to a IDM string.
Args:
shades: A list of Honeybee Shade objects.
tolerance: The maximum difference between X, Y, and Z values at which point
vertices are considered distinct from one another.
decimal_places: An integer for the number of decimal places to which
coordinate values will be rounded. (Default: 3).
Returns:
A formatted string that represents this shade in IDM format.
"""
if not shades:
return ''
shade_groups = {}
no_groups = []
for shade in shades:
try:
group_id = shade.user_data['__group_id__']
except (TypeError, KeyError):
no_groups.append(shade)
continue
else:
if group_id not in shade_groups:
shade_groups[group_id] = [shade]
else:
shade_groups[group_id].append(shade)
filtered_groups = {}
for k, v in shade_groups.items():
if len(v) == 1:
no_groups.extend(v)
else:
filtered_groups[k] = v
single_shades = '\n'.join(
[_shade_to_idm(shade, decimal_places) for shade in no_groups]
)
group_shades = '\n'.join(
[_shade_group_to_idm(shades, tolerance, decimal_places)
for shades in filtered_groups.values()]
)
return f'{single_shades}\n{group_shades}\n'
[docs]
def shade_meshes_to_idm(shades: List[ShadeMesh], tolerance: float, decimal_places: int = 3):
"""Convert a list of Shades to a IDM string.
Args:
shades: A list of Honeybee ShadeMeshes.
tolerance: The maximum difference between X, Y, and Z values at which point
vertices are considered distinct from one another.
decimal_places: An integer for the number of decimal places to which
coordinate values will be rounded. (Default: 3).
Returns:
A formatted string that represents this shade in IDM format.
"""
if not shades:
return ''
shade_idms = []
for shade in shades:
shade.triangulate_and_remove_degenerate_faces(tolerance)
name = '_'.join(
(
' '.join(shade.display_name.split()),
shade.identifier.replace('Shade_', '')
)
)
shade_idms.append(
_shade_geometry_to_idm(shade.geometry, name, decimal_places)
)
return '\n'.join(shade_idms)