Skip to content

Commit d1f937e

Browse files
chriswmackeyChris Mackey
authored andcommitted
fix(colorobj): Improve default text labeling
1 parent 04c4e61 commit d1f937e

File tree

3 files changed

+141
-18
lines changed

3 files changed

+141
-18
lines changed

honeybee_display/colorobj.py

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
"""Method to translate a Color Room/Face objects to a VisualizationSet."""
22
import math
33

4-
from ladybug_geometry.geometry3d import Vector3D, Plane
4+
from ladybug_geometry.geometry3d import Vector3D, Point3D, Polyline3D, Plane, \
5+
Face3D, Polyface3D
56
from ladybug_display.geometry3d import DisplayLineSegment3D, DisplayText3D
67
from ladybug_display.visualization import VisualizationSet, ContextGeometry, \
78
AnalysisGeometry, VisualizationData
89

10+
from honeybee.units import conversion_factor_to_meters
11+
from honeybee.facetype import Floor
912
from honeybee.face import Face
1013

1114

12-
def color_room_to_vis_set(color_room, include_wireframe=True, text_labels=False):
15+
def color_room_to_vis_set(
16+
color_room, include_wireframe=True, text_labels=False,
17+
units=None, tolerance=0.01):
1318
"""Translate a Honeybee ColorRoom to a VisualizationSet.
1419
1520
Args:
@@ -19,6 +24,11 @@ def color_room_to_vis_set(color_room, include_wireframe=True, text_labels=False)
1924
text_labels: A boolean to note whether the attribute assigned to the
2025
ColorRoom should be expressed as a colored AnalysisGeometry (False)
2126
or a ContextGeometry as text labels (True). (Default: False).
27+
units: Optional text, which will be used to set the default maximum text
28+
height and the distance of the text to the ground. If None, some
29+
generic defaults will be used. (Default: None).
30+
tolerance: Tolerance value, which is used to compute the text
31+
label point for concave geometries. (Default: 0.01).
2232
2333
Returns:
2434
A VisualizationSet object that represents the ColorRoom with an
@@ -34,19 +44,51 @@ def color_room_to_vis_set(color_room, include_wireframe=True, text_labels=False)
3444
# use text labels if requested
3545
if text_labels:
3646
# set up default variables
47+
max_txt_h = float('inf')
48+
if units is not None:
49+
fac_to_m = conversion_factor_to_meters(units)
50+
max_txt_h = 0.25 / fac_to_m
51+
max_txt_v = 1.0 / fac_to_m
3752
label_text = []
3853
txt_height = None if color_room.legend_parameters.is_text_height_default \
3954
else color_room.legend_parameters.text_height
4055
font = color_room.legend_parameters.font
4156
# loop through the rooms and create the text labels
4257
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)
58+
# compute the center point for the text
59+
if units is not None:
60+
room_h = room.geometry.max.z - room.geometry.min.z
61+
m_vec = Vector3D(0, 0, max_txt_v) if room_h > max_txt_v * 2 \
62+
else Vector3D(0, 0, room_h / 2)
63+
floor_faces = [face.geometry for face in room.faces
64+
if isinstance(face.type, Floor)]
65+
if len(floor_faces) == 1:
66+
flr_geo = floor_faces[0]
67+
base_pt = flr_geo.center if flr_geo.is_convex else \
68+
flr_geo.pole_of_inaccessibility(tolerance)
69+
elif len(floor_faces) == 0:
70+
c_pt = room.geometry.center
71+
base_pt = Point3D(c_pt.x, c_pt.y, room.geometry.min.z)
72+
else:
73+
floor_p_face = Polyface3D.from_faces(floor_faces, tolerance)
74+
ne = floor_p_face.naked_edges
75+
floor_outline = Polyline3D.join_segments(ne, tolerance)[0]
76+
flr_geo = Face3D(floor_outline.vertices[:-1])
77+
base_pt = flr_geo.center if flr_geo.is_convex else \
78+
flr_geo.pole_of_inaccessibility(tolerance)
79+
base_pt = base_pt.move(m_vec)
80+
base_plane = Plane(Vector3D(0, 0, 1), base_pt)
81+
else:
82+
base_pt = room.geometry.center
83+
base_plane = Plane(Vector3D(0, 0, 1), base_pt)
84+
# get the text height
4585
if txt_height is None: # auto-calculate default text height
4686
txt_len = len(room_prop) if len(room_prop) > 10 else 10
4787
txt_h = (room.geometry.max.x - room.geometry.min.x) / txt_len
4888
else:
4989
txt_h = txt_height
90+
txt_h = max_txt_h if txt_h > max_txt_h else txt_h
91+
# create the text label
5092
label = DisplayText3D(
5193
room_prop, base_plane, txt_h, font=font,
5294
horizontal_alignment='Center', vertical_alignment='Middle')
@@ -78,7 +120,9 @@ def color_room_to_vis_set(color_room, include_wireframe=True, text_labels=False)
78120
return vis_set
79121

80122

81-
def color_face_to_vis_set(color_face, include_wireframe=True, text_labels=False):
123+
def color_face_to_vis_set(
124+
color_face, include_wireframe=True, text_labels=False,
125+
units=None, tolerance=0.01):
82126
"""Translate a Honeybee ColorFace to a VisualizationSet.
83127
84128
Args:
@@ -88,6 +132,11 @@ def color_face_to_vis_set(color_face, include_wireframe=True, text_labels=False)
88132
text_labels: A boolean to note whether the attribute assigned to the
89133
ColorFace should be expressed as a colored AnalysisGeometry (False)
90134
or a ContextGeometry as text labels (True). (Default: False).
135+
units: Optional text, which will be used to set the default maximum text
136+
height and the distance of the text to the ground. If None, some
137+
generic defaults will be used. (Default: None).
138+
tolerance: Tolerance value, which is used to eliminate very small
139+
text. (Default: 0.01).
91140
92141
Returns:
93142
A VisualizationSet object that represents the ColorFace with an
@@ -103,6 +152,10 @@ def color_face_to_vis_set(color_face, include_wireframe=True, text_labels=False)
103152
# use text labels if requested
104153
if text_labels:
105154
# set up default variables
155+
max_txt_h = float('inf')
156+
if units is not None:
157+
fac_to_m = conversion_factor_to_meters(units)
158+
max_txt_h = 0.25 / fac_to_m
106159
label_text = []
107160
txt_height = None if color_face.legend_parameters.is_text_height_default \
108161
else color_face.legend_parameters.text_height
@@ -116,11 +169,17 @@ def color_face_to_vis_set(color_face, include_wireframe=True, text_labels=False)
116169
base_plane = base_plane.rotate(base_plane.n, math.pi, base_plane.o)
117170
if txt_height is None: # auto-calculate default text height
118171
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)
172+
dims = [
173+
(f_geo.max.x - f_geo.min.x),
174+
(f_geo.max.y - f_geo.min.y),
175+
(f_geo.max.z - f_geo.min.z)]
176+
dims.sort()
177+
txt_h = dims[1] / (txt_len * 1.5)
122178
else:
123179
txt_h = txt_height
180+
if txt_h < tolerance:
181+
continue
182+
txt_h = max_txt_h if txt_h > max_txt_h else txt_h
124183
# move base plane origin a little to avoid overlaps of adjacent labels
125184
if base_plane.n.x != 0:
126185
m_vec = base_plane.y if base_plane.n.x < 0 else -base_plane.y

honeybee_display/energy/colorobj.py

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
"""Method to translate a Color Room/Face objects to a VisualizationSet."""
22
import math
33

4-
from ladybug_geometry.geometry3d import Vector3D, Plane
4+
from ladybug_geometry.geometry3d import Vector3D, Point3D, Polyline3D, Plane, \
5+
Face3D, Polyface3D
56
from ladybug_display.geometry3d import DisplayText3D
67
from ladybug_display.visualization import VisualizationSet, ContextGeometry, \
78
AnalysisGeometry, VisualizationData
9+
from honeybee.units import conversion_factor_to_meters
10+
from honeybee.facetype import Floor
811

912
from ..colorobj import _room_wireframe, _process_wireframe
1013

1114

12-
def energy_color_room_to_vis_set(color_room, include_wireframe=True, text_labels=False):
15+
def energy_color_room_to_vis_set(
16+
color_room, include_wireframe=True, text_labels=False,
17+
units=None, tolerance=0.01):
1318
"""Translate a Honeybee-Energy ColorRoom to a VisualizationSet.
1419
1520
Args:
@@ -20,6 +25,11 @@ def energy_color_room_to_vis_set(color_room, include_wireframe=True, text_labels
2025
text_labels: A boolean to note whether the results should be expressed
2126
as a colored AnalysisGeometry (False) or a ContextGeometry as text
2227
labels (True). (Default: False).
28+
units: Optional text, which will be used to set the default maximum text
29+
height and the distance of the text to the ground. If None, some
30+
generic defaults will be used. (Default: None).
31+
tolerance: Optional tolerance value, which is used to compute the text
32+
label point for concave geometries. (Default: 0.01).
2333
2434
Returns:
2535
A VisualizationSet object that represents the ColorRoom with an
@@ -35,16 +45,48 @@ def energy_color_room_to_vis_set(color_room, include_wireframe=True, text_labels
3545
if text_labels:
3646
txt_height, font, f_str = _process_leg_par_for_text(color_room)
3747
# loop through the rooms and create the text labels
48+
max_txt_h = float('inf')
49+
if units is not None:
50+
fac_to_m = conversion_factor_to_meters(units)
51+
max_txt_h = 0.25 / fac_to_m
52+
max_txt_v = 1.0 / fac_to_m
3853
label_text = []
3954
for room_val, room in zip(color_room.matched_values, color_room.matched_rooms):
40-
cent_pt = room.geometry.center # base point for the text
41-
base_plane = Plane(Vector3D(0, 0, 1), cent_pt)
4255
room_prop = f_str % room_val
56+
# compute the center point for the text
57+
if units is not None:
58+
room_h = room.geometry.max.z - room.geometry.min.z
59+
m_vec = Vector3D(0, 0, max_txt_v) if room_h > max_txt_v * 2 \
60+
else Vector3D(0, 0, room_h / 2)
61+
floor_faces = [face.geometry for face in room.faces
62+
if isinstance(face.type, Floor)]
63+
if len(floor_faces) == 1:
64+
flr_geo = floor_faces[0]
65+
base_pt = flr_geo.center if flr_geo.is_convex else \
66+
flr_geo.pole_of_inaccessibility(tolerance)
67+
elif len(floor_faces) == 0:
68+
c_pt = room.geometry.center
69+
base_pt = Point3D(c_pt.x, c_pt.y, room.geometry.min.z)
70+
else:
71+
floor_p_face = Polyface3D.from_faces(floor_faces, tolerance)
72+
ne = floor_p_face.naked_edges
73+
floor_outline = Polyline3D.join_segments(ne, tolerance)[0]
74+
flr_geo = Face3D(floor_outline.vertices[:-1])
75+
base_pt = flr_geo.center if flr_geo.is_convex else \
76+
flr_geo.pole_of_inaccessibility(tolerance)
77+
base_pt = base_pt.move(m_vec)
78+
base_plane = Plane(Vector3D(0, 0, 1), base_pt)
79+
else:
80+
base_pt = room.geometry.center
81+
base_plane = Plane(Vector3D(0, 0, 1), base_pt)
82+
# get the text height
4383
if txt_height is None: # auto-calculate default text height
4484
txt_len = len(room_prop) if len(room_prop) > 10 else 10
4585
txt_h = (room.geometry.max.x - room.geometry.min.x) / txt_len
4686
else:
4787
txt_h = txt_height
88+
txt_h = max_txt_h if txt_h > max_txt_h else txt_h
89+
# create the text label
4890
label = DisplayText3D(
4991
room_prop, base_plane, txt_h, font=font,
5092
horizontal_alignment='Center', vertical_alignment='Middle')
@@ -68,7 +110,9 @@ def energy_color_room_to_vis_set(color_room, include_wireframe=True, text_labels
68110
return vis_set
69111

70112

71-
def color_face_to_vis_set(color_face, include_wireframe=True, text_labels=False):
113+
def color_face_to_vis_set(
114+
color_face, include_wireframe=True, text_labels=False,
115+
units=None, tolerance=0.01):
72116
"""Translate a Honeybee ColorFace to a VisualizationSet.
73117
74118
Args:
@@ -78,6 +122,11 @@ def color_face_to_vis_set(color_face, include_wireframe=True, text_labels=False)
78122
text_labels: A boolean to note whether the attribute assigned to the
79123
ColorFace should be expressed as a colored AnalysisGeometry (False)
80124
or a ContextGeometry as text labels (True). (Default: False).
125+
units: Optional text, which will be used to set the default maximum text
126+
height and the distance of the text to the ground. If None, some
127+
generic defaults will be used. (Default: None).
128+
tolerance: Tolerance value, which is used to eliminate very small
129+
text. (Default: 0.01).
81130
82131
Returns:
83132
A VisualizationSet object that represents the ColorFace with an
@@ -91,6 +140,11 @@ def color_face_to_vis_set(color_face, include_wireframe=True, text_labels=False)
91140

92141
# use text labels if requested
93142
if text_labels:
143+
# set up default variables
144+
max_txt_h = float('inf')
145+
if units is not None:
146+
fac_to_m = conversion_factor_to_meters(units)
147+
max_txt_h = 0.25 / fac_to_m
94148
txt_height, font, f_str = _process_leg_par_for_text(color_face)
95149
# loop through the faces and create the text labels
96150
label_text = []
@@ -103,11 +157,17 @@ def color_face_to_vis_set(color_face, include_wireframe=True, text_labels=False)
103157
base_plane = base_plane.rotate(base_plane.n, math.pi, base_plane.o)
104158
if txt_height is None: # auto-calculate default text height
105159
txt_len = len(face_prop) if len(face_prop) > 10 else 10
106-
largest_dim = max(
107-
(f_geo.max.x - f_geo.min.x), (f_geo.max.y - f_geo.min.y))
108-
txt_h = largest_dim / (txt_len * 2)
160+
dims = [
161+
(f_geo.max.x - f_geo.min.x),
162+
(f_geo.max.y - f_geo.min.y),
163+
(f_geo.max.z - f_geo.min.z)]
164+
dims.sort()
165+
txt_h = dims[1] / (txt_len * 1.5)
109166
else:
110167
txt_h = txt_height
168+
if txt_h < tolerance:
169+
continue
170+
txt_h = max_txt_h if txt_h > max_txt_h else txt_h
111171
# move base plane origin a little to avoid overlaps of adjacent labels
112172
if base_plane.n.x != 0:
113173
m_vec = base_plane.y if base_plane.n.x < 0 else -base_plane.y

honeybee_display/model.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,11 @@ def model_to_vis_set(
260260
if room_attr is not None and len(room_attr) != 0 and len(model.rooms) != 0:
261261
room_attr = [room_attr] if isinstance(room_attr, str) else room_attr
262262
if room_text_labels:
263+
units, tol = model.units, model.tolerance
263264
for r_attr in room_attr:
264265
ra_col_obj = ColorRoom(model.rooms, r_attr, room_legend_par)
265-
geo_objs.append(color_room_to_vis_set(ra_col_obj, False, True)[0])
266+
geo_objs.append(
267+
color_room_to_vis_set(ra_col_obj, False, True, units, tol)[0])
266268
else:
267269
ra_col_obj = ColorRoom(model.rooms, room_attr[0], room_legend_par)
268270
geo_obj = color_room_to_vis_set(ra_col_obj, False, False)[0]
@@ -286,9 +288,11 @@ def model_to_vis_set(
286288
faces.extend(model.orphaned_doors)
287289
faces.extend(model.orphaned_shades)
288290
if face_text_labels:
291+
units, tol = model.units, model.tolerance
289292
for f_attr in face_attr:
290293
fa_col_obj = ColorFace(faces, f_attr, face_legend_par)
291-
geo_objs.append(color_face_to_vis_set(fa_col_obj, False, True)[0])
294+
geo_objs.append(
295+
color_face_to_vis_set(fa_col_obj, False, True, units, tol)[0])
292296
else:
293297
fa_col_obj = ColorFace(faces, face_attr[0], face_legend_par)
294298
geo_obj = color_face_to_vis_set(fa_col_obj, False, False)[0]

0 commit comments

Comments
 (0)