Skip to content

Commit c083b78

Browse files
chriswmackeyChris Mackey
authored andcommitted
feat(model): Add methods for Model.to_vis_set
1 parent f42f874 commit c083b78

19 files changed

+1073
-10
lines changed

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,36 @@
1-
[![Build Status](https://github.com/ladybug-tools/ladybug-display/workflows/CI/badge.svg)](https://github.com/ladybug-tools/ladybug-display/actions)
1+
[![Build Status](https://github.com/ladybug-tools/honeybee-display/workflows/CI/badge.svg)](https://github.com/ladybug-tools/honeybee-display/actions)
22

33
[![Python 3.7](https://img.shields.io/badge/python-3.7-blue.svg)](https://www.python.org/downloads/release/python-370/) [![Python 2.7](https://img.shields.io/badge/python-2.7-green.svg)](https://www.python.org/downloads/release/python-270/) [![IronPython](https://img.shields.io/badge/ironpython-2.7-red.svg)](https://github.com/IronLanguages/ironpython2/releases/tag/ipy-2.7.8/)
44

5-
# ladybug-display
5+
# honeybee-display
66

7-
A library that assigns basic display attributes to ladybug-geometry objects (color, line weight, line type, etc).
7+
Adds methods to translate honeybee objects to VisualizationSets.
88

99
## Installation
1010
```console
11-
pip install ladybug-display
11+
pip install honeybee-display
1212
```
1313

1414
## QuickStart
1515
```python
16-
import ladybug_display
16+
import honeybee_display
1717

1818
```
1919

20-
## [API Documentation](http://ladybug-tools.github.io/ladybug-display/docs)
20+
## [API Documentation](http://ladybug-tools.github.io/honeybee-display/docs)
2121

2222
## Local Development
2323
1. Clone this repo locally
2424
```console
25-
git clone git@github.com:ladybug-tools/ladybug-display
25+
git clone git@github.com:ladybug-tools/honeybee-display
2626

2727
# or
2828

29-
git clone https://github.com/ladybug-tools/ladybug-display
29+
git clone https://github.com/ladybug-tools/honeybee-display
3030
```
3131
2. Install dependencies:
3232
```console
33-
cd ladybug-display
33+
cd honeybee-display
3434
pip install -r dev-requirements.txt
3535
pip install -r requirements.txt
3636
```
@@ -42,6 +42,6 @@ python -m pytest tests/
4242

4343
4. Generate Documentation:
4444
```console
45-
sphinx-apidoc -f -e -d 4 -o ./docs ./ladybug_display
45+
sphinx-apidoc -f -e -d 4 -o ./docs ./honeybee_display
4646
sphinx-build -b html ./docs ./docs/_build/docs
4747
```

honeybee_display/_extend_honeybee.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,39 @@
11
# coding=utf-8
22
# import the core honeybee modules
3+
from honeybee.model import Model
4+
from honeybee.room import Room
5+
from honeybee.face import Face
6+
from honeybee.aperture import Aperture
7+
from honeybee.door import Door
8+
from honeybee.shade import Shade
9+
from honeybee.colorobj import ColorRoom, ColorFace
310

411
# import the extension functions
12+
from .model import model_to_vis_set, model_to_vis_set_wireframe
13+
from .room import room_to_vis_set
14+
from .face import face_to_vis_set
15+
from .aperture import aperture_to_vis_set
16+
from .door import door_to_vis_set
17+
from .shade import shade_to_vis_set
18+
from .colorobj import color_room_to_vis_set, color_face_to_vis_set
519

620
# inject the methods onto the classes
21+
Model.to_vis_set = model_to_vis_set
22+
Model.to_vis_set_wireframe = model_to_vis_set_wireframe
23+
Room.to_vis_set = room_to_vis_set
24+
Face.to_vis_set = face_to_vis_set
25+
Aperture.to_vis_set = aperture_to_vis_set
26+
Door.to_vis_set = door_to_vis_set
27+
Shade.to_vis_set = shade_to_vis_set
28+
ColorRoom.to_vis_set = color_room_to_vis_set
29+
ColorFace.to_vis_set = color_face_to_vis_set
30+
31+
# attempt to extend honeybee-energy if it is installed
32+
try:
33+
from honeybee_energy.result.colorobj import ColorRoom as EnergyColorRoom
34+
from honeybee_energy.result.colorobj import ColorFace as EnergyColorFace
35+
from .energy.colorobj import energy_color_room_to_vis_set, color_face_to_vis_set
36+
EnergyColorRoom.to_vis_set = energy_color_room_to_vis_set
37+
EnergyColorFace.to_vis_set = color_face_to_vis_set
38+
except ImportError: # honeybee-energy is not installed
39+
pass

honeybee_display/aperture.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""Method to translate a Aperture to a VisualizationSet."""
2+
from ladybug_display.geometry3d import DisplayFace3D
3+
from ladybug_display.visualization import VisualizationSet, ContextGeometry
4+
5+
6+
def aperture_to_vis_set(aperture, color_by='type'):
7+
"""Translate a Honeybee Aperture to a VisualizationSet.
8+
9+
Args:
10+
model: A Honeybee Aperture object to be converted to a VisualizationSet.
11+
color_by: Text for the property that dictates the colors of the Aperture
12+
geometry. (Default: type). Choose from the following:
13+
14+
* type
15+
* boundary_condition
16+
17+
Returns:
18+
A VisualizationSet object that represents the Aperture with a
19+
single ContextGeometry.
20+
"""
21+
# get the basic properties for geometry conversion
22+
color_by_attr = 'type_color' if color_by.lower() == 'type' else 'bc_color'
23+
d_mod = 'SurfaceWithEdges'
24+
# convert all geometry into DisplayFace3D
25+
dis_geos = []
26+
a_col = getattr(aperture, color_by_attr)
27+
dis_geos.append(DisplayFace3D(aperture.geometry, a_col, d_mod))
28+
for shd in aperture.shades:
29+
s_col = getattr(shd, color_by_attr)
30+
dis_geos.append(DisplayFace3D(shd.geometry, s_col, d_mod))
31+
# build the VisualizationSet and ContextGeometry
32+
con_geo = ContextGeometry(aperture.identifier, dis_geos)
33+
con_geo.display_name = aperture.display_name
34+
vis_set = VisualizationSet(aperture.identifier, [con_geo])
35+
vis_set.display_name = aperture.display_name
36+
return vis_set

honeybee_display/colorobj.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
"""Method to translate a Color Room/Face objects to a VisualizationSet."""
2+
import math
3+
4+
from ladybug_geometry.geometry3d import Vector3D, Plane
5+
from ladybug_display.geometry3d import DisplayLineSegment3D, DisplayText3D
6+
from ladybug_display.visualization import VisualizationSet, ContextGeometry, \
7+
AnalysisGeometry, VisualizationData
8+
9+
from honeybee.face import Face
10+
11+
12+
def color_room_to_vis_set(color_room, include_wireframe=True, text_labels=False):
13+
"""Translate a Honeybee ColorRoom to a VisualizationSet.
14+
15+
Args:
16+
color_room: A Honeybee ColorRoom object to be converted to a VisualizationSet.
17+
include_wireframe: Boolean to note whether a ContextGeometry just for
18+
the Wireframe (in LineSegment3D) should be included. (Default: True).
19+
text_labels: A boolean to note whether the attribute assigned to the
20+
ColorRoom should be expressed as a colored AnalysisGeometry (False)
21+
or a ContextGeometry as text labels (True). (Default: False).
22+
23+
Returns:
24+
A VisualizationSet object that represents the ColorRoom with an
25+
AnalysisGeometry when text_labels is False or a ContextGeometry when
26+
text_labels is True. It can also optionally include a ContextGeometry
27+
for the wireframe.
28+
"""
29+
# set up an empty visualization set
30+
vis_set = VisualizationSet('Room_{}'.format(color_room.attr_name), ())
31+
vis_set.display_name = 'Room {}'.format(
32+
color_room.attr_name_end.replace('_', ' ').title())
33+
34+
# use text labels if requested
35+
if text_labels:
36+
# set up default variables
37+
label_text = []
38+
txt_height = None if color_room.legend_parameters.is_text_height_default \
39+
else color_room.legend_parameters.text_height
40+
font = color_room.legend_parameters.font
41+
# loop through the rooms and create the text labels
42+
for room_prop, room in zip(color_room.attributes, color_room.rooms):
43+
cent_pt = room.geometry.center # base point for the text
44+
base_plane = Plane(Vector3D(0, 0, 1), cent_pt)
45+
if txt_height is None: # auto-calculate default text height
46+
txt_len = len(room_prop) if len(room_prop) > 10 else 10
47+
txt_h = (room.geometry.max.x - room.geometry.min.x) / txt_len
48+
else:
49+
txt_h = txt_height
50+
label = DisplayText3D(
51+
room_prop, base_plane, txt_h, font=font,
52+
horizontal_alignment='Center', vertical_alignment='Middle')
53+
label_text.append(label) # append everything to the list
54+
con_geo = ContextGeometry(vis_set.identifier, label_text)
55+
con_geo.display_name = vis_set.display_name
56+
vis_set.add_geometry(con_geo)
57+
else: # use a colored AnalysisGeometry
58+
# produce a range of values from the collected attributes
59+
attr_dict = {i: val for i, val in enumerate(color_room.attributes_unique)}
60+
attr_dict_rev = {val: i for i, val in attr_dict.items()}
61+
values = tuple(attr_dict_rev[r_attr] for r_attr in color_room.attributes)
62+
# produce legend parameters with an ordinal dict for the attributes
63+
l_par = color_room.legend_parameters.duplicate()
64+
l_par.segment_count = len(color_room.attributes_unique)
65+
l_par.ordinal_dictionary = attr_dict
66+
if l_par.is_title_default:
67+
l_par.title = color_room.attr_name_end.replace('_', ' ').title()
68+
# create the analysis geometry
69+
vis_data = VisualizationData(values, l_par)
70+
geo = tuple(room.geometry for room in color_room.rooms)
71+
a_geo = AnalysisGeometry(vis_set.identifier, geo, [vis_data])
72+
a_geo.display_name = vis_set.display_name
73+
vis_set.add_geometry(a_geo)
74+
75+
# loop through all of the rooms and add their wire frames
76+
if include_wireframe:
77+
vis_set.add_geometry(_room_wireframe(color_room.rooms))
78+
return vis_set
79+
80+
81+
def color_face_to_vis_set(color_face, include_wireframe=True, text_labels=False):
82+
"""Translate a Honeybee ColorFace to a VisualizationSet.
83+
84+
Args:
85+
color_face: A Honeybee ColorFace object to be converted to a VisualizationSet.
86+
include_wireframe: Boolean to note whether a ContextGeometry just for
87+
the Wireframe (in LineSegment3D) should be included. (Default: True).
88+
text_labels: A boolean to note whether the attribute assigned to the
89+
ColorFace should be expressed as a colored AnalysisGeometry (False)
90+
or a ContextGeometry as text labels (True). (Default: False).
91+
92+
Returns:
93+
A VisualizationSet object that represents the ColorFace with an
94+
AnalysisGeometry when text_labels is False or a ContextGeometry when
95+
text_labels is True. It can also optionally include a ContextGeometry
96+
for the wireframe.
97+
"""
98+
# set up an empty visualization set
99+
vis_set = VisualizationSet('Face_{}'.format(color_face.attr_name), ())
100+
vis_set.display_name = 'Face {}'.format(
101+
color_face.attr_name_end.replace('_', ' ').title())
102+
103+
# use text labels if requested
104+
if text_labels:
105+
# set up default variables
106+
label_text = []
107+
txt_height = None if color_face.legend_parameters.is_text_height_default \
108+
else color_face.legend_parameters.text_height
109+
font = color_face.legend_parameters.font
110+
# loop through the faces and create the text labels
111+
for face_prop, f_geo in zip(color_face.attributes, color_face.flat_geometry):
112+
if face_prop != 'N/A':
113+
cent_pt = f_geo.center # base point for the text
114+
base_plane = Plane(f_geo.normal, cent_pt)
115+
if base_plane.y.z < 0: # base plane pointing downwards; rotate it
116+
base_plane = base_plane.rotate(base_plane.n, math.pi, base_plane.o)
117+
if txt_height is None: # auto-calculate default text height
118+
txt_len = len(face_prop) if len(face_prop) > 10 else 10
119+
largest_dim = max(
120+
(f_geo.max.x - f_geo.min.x), (f_geo.max.y - f_geo.min.y))
121+
txt_h = largest_dim / (txt_len * 2)
122+
else:
123+
txt_h = txt_height
124+
# move base plane origin a little to avoid overlaps of adjacent labels
125+
if base_plane.n.x != 0:
126+
m_vec = base_plane.y if base_plane.n.x < 0 else -base_plane.y
127+
else:
128+
m_vec = base_plane.y if base_plane.n.z < 0 else -base_plane.y
129+
base_plane = base_plane.move(m_vec * txt_h)
130+
# create the text label
131+
label = DisplayText3D(
132+
face_prop, base_plane, txt_h, font=font,
133+
horizontal_alignment='Center', vertical_alignment='Middle')
134+
label_text.append(label) # append everything to the list
135+
con_geo = ContextGeometry(vis_set.identifier, label_text)
136+
con_geo.display_name = vis_set.display_name
137+
vis_set.add_geometry(con_geo)
138+
else: # use a colored AnalysisGeometry
139+
# produce a range of values from the collected attributes
140+
attr_dict = {i: val for i, val in enumerate(color_face.attributes_unique)}
141+
attr_dict_rev = {val: i for i, val in attr_dict.items()}
142+
values = tuple(attr_dict_rev[r_attr] for r_attr in color_face.attributes)
143+
# produce legend parameters with an ordinal dict for the attributes
144+
l_par = color_face.legend_parameters.duplicate()
145+
l_par.segment_count = len(color_face.attributes_unique)
146+
l_par.ordinal_dictionary = attr_dict
147+
if l_par.is_title_default:
148+
l_par.title = color_face.attr_name_end.replace('_', ' ').title()
149+
# create the analysis geometry
150+
vis_data = VisualizationData(values, l_par)
151+
a_geo = AnalysisGeometry(
152+
vis_set.identifier, color_face.flat_geometry, [vis_data])
153+
a_geo.display_name = vis_set.display_name
154+
vis_set.add_geometry(a_geo)
155+
156+
# loop through all of the rooms and add their wire frames
157+
if include_wireframe:
158+
vis_set.add_geometry(_face_wireframe(color_face.flat_faces))
159+
return vis_set
160+
161+
162+
def _room_wireframe(rooms):
163+
"""Process Rooms into a ContextGeometry for Wireframe."""
164+
wireframe = []
165+
for room in rooms:
166+
for face in room.faces:
167+
_process_wireframe(face, wireframe, 2)
168+
for ap in face._apertures:
169+
_process_wireframe(ap, wireframe)
170+
for dr in face._doors:
171+
_process_wireframe(dr, wireframe)
172+
for shade in room.shades:
173+
sh_geo = shade.geometry
174+
for seg in sh_geo.boundary_segments:
175+
wireframe.append(DisplayLineSegment3D(seg, line_width=1))
176+
if sh_geo.has_holes:
177+
for hole in sh_geo.holes:
178+
for seg in sh_geo.boundary_segments:
179+
wireframe.append(DisplayLineSegment3D(seg, line_width=1))
180+
con_geo = ContextGeometry('Wireframe', wireframe)
181+
return con_geo
182+
183+
184+
def _face_wireframe(faces):
185+
"""Process Faces into a ContextGeometry for Wireframe."""
186+
wireframe = []
187+
for face in faces:
188+
lw = 2 if isinstance(face, Face) else 1
189+
face3d = face.geometry
190+
for seg in face3d.boundary_segments:
191+
wireframe.append(DisplayLineSegment3D(seg, line_width=lw))
192+
if face3d.has_holes:
193+
for hole in face3d.holes:
194+
for seg in face3d.boundary_segments:
195+
wireframe.append(DisplayLineSegment3D(seg, line_width=lw))
196+
con_geo = ContextGeometry('Wireframe', wireframe)
197+
return con_geo
198+
199+
200+
def _process_wireframe(geo_obj, wireframe, line_width=1):
201+
"""Process the boundary and holes of a Honeybee geometry into DisplayLinesegment3D.
202+
"""
203+
face3d = geo_obj.geometry
204+
for seg in face3d.boundary_segments:
205+
wireframe.append(DisplayLineSegment3D(seg, line_width=line_width))
206+
if face3d.has_holes:
207+
for hole in face3d.holes:
208+
for seg in face3d.boundary_segments:
209+
wireframe.append(DisplayLineSegment3D(seg, line_width=line_width))
210+
for shade in geo_obj.shades:
211+
sh_geo = shade.geometry
212+
for seg in sh_geo.boundary_segments:
213+
wireframe.append(DisplayLineSegment3D(seg, line_width=1))
214+
if sh_geo.has_holes:
215+
for hole in sh_geo.holes:
216+
for seg in sh_geo.boundary_segments:
217+
wireframe.append(DisplayLineSegment3D(seg, line_width=1))

honeybee_display/door.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Method to translate a Door to a VisualizationSet."""
2+
from ladybug_display.geometry3d import DisplayFace3D
3+
from ladybug_display.visualization import VisualizationSet, ContextGeometry
4+
5+
6+
def door_to_vis_set(door, color_by='type'):
7+
"""Translate a Honeybee Door to a VisualizationSet.
8+
9+
Args:
10+
model: A Honeybee Door object to be converted to a VisualizationSet.
11+
color_by: Text for the property that dictates the colors of the Door
12+
geometry. (Default: type). Choose from the following:
13+
14+
* type
15+
* boundary_condition
16+
17+
Returns:
18+
A VisualizationSet object that represents the Door with a single ContextGeometry.
19+
"""
20+
# get the basic properties for geometry conversion
21+
color_by_attr = 'type_color' if color_by.lower() == 'type' else 'bc_color'
22+
d_mod = 'SurfaceWithEdges'
23+
# convert all geometry into DisplayFace3D
24+
dis_geos = []
25+
a_col = getattr(door, color_by_attr)
26+
dis_geos.append(DisplayFace3D(door.geometry, a_col, d_mod))
27+
for shd in door.shades:
28+
s_col = getattr(shd, color_by_attr)
29+
dis_geos.append(DisplayFace3D(shd.geometry, s_col, d_mod))
30+
# build the VisualizationSet and ContextGeometry
31+
con_geo = ContextGeometry(door.identifier, dis_geos)
32+
con_geo.display_name = door.display_name
33+
vis_set = VisualizationSet(door.identifier, [con_geo])
34+
vis_set.display_name = door.display_name
35+
return vis_set

honeybee_display/energy/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Extension of honeybee-energy with display attributes."""

0 commit comments

Comments
 (0)