honeybee-core 1.62.12__tar.gz → 1.63.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {honeybee_core-1.62.12/honeybee_core.egg-info → honeybee_core-1.63.0}/PKG-INFO +1 -1
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/aperture.py +0 -7
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/door.py +0 -7
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/face.py +0 -7
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/model.py +123 -8
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/shade.py +2 -9
- {honeybee_core-1.62.12 → honeybee_core-1.63.0/honeybee_core.egg-info}/PKG-INFO +1 -1
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/model_test.py +72 -3
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/.github/workflows/ci.yaml +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/.github/workflows/dependency-release.yaml +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/.gitignore +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/.releaserc.json +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/CODE_OF_CONDUCT.md +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/CONTRIBUTING.md +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/LICENSE +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/MANIFEST.in +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/README.md +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/deploy.sh +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/dev-requirements.txt +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/docs/_build/.nojekyll +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/docs/_build/README.md +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/docs/_build/docs/README.md +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/docs/_static/custom.css +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/docs/_templates/layout.html +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/docs/cli/index.rst +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/docs/conf.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/docs/index.rst +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/__init__.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/__main__.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/_base.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/_basewithshade.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/_lockable.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/altnumber.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/boundarycondition.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/checkdup.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/cli/__init__.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/cli/compare.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/cli/create.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/cli/edit.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/cli/lib.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/cli/setconfig.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/cli/validate.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/colorobj.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/config.json +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/config.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/dictutil.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/extensionutil.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/facetype.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/logutil.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/orientation.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/properties.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/room.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/search.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/shademesh.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/typing.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/units.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/writer/__init__.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/writer/aperture.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/writer/door.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/writer/face.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/writer/model.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/writer/room.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/writer/shade.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee/writer/shademesh.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee_core.egg-info/SOURCES.txt +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee_core.egg-info/dependency_links.txt +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee_core.egg-info/entry_points.txt +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee_core.egg-info/requires.txt +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/honeybee_core.egg-info/top_level.txt +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/requirements.txt +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/setup.cfg +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/setup.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/__init__.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/aperture_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/boundary_condition_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/cli_compare_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/cli_create_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/cli_edit_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/cli_validate_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/colorobj_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/config_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/dictutil_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/door_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/face_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/facetype_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/ShoeBox.json +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/bad_geometry_model.hbjson +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/colliding_room_volumes.hbjson +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/compare_model_1.hbjson +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/compare_model_2.hbjson +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/complex_polyfaces.json +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/minor_geometry/existing_model.hbjson +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/minor_geometry/updated_model.hbjson +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/mismatched_area_adj.hbjson +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/model_with_adiabatic.hbjson +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/model_with_holes.hbjson +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/model_without_adjacency.hbjson +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/nonascii_face.json +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/polygons_for_gap_boundary.json +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/room_for_window_offset.hbjson +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/single_family_home.hbjson +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/lockable_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/orientation_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/room_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/search_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/shade_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/shademesh_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/stl/cube_ascii.stl +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/stl/cube_binary.stl +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/typing_test.py +0 -0
- {honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/units_test.py +0 -0
|
@@ -94,13 +94,6 @@ class Aperture(_BaseWithShade):
|
|
|
94
94
|
assert data['type'] == 'Aperture', 'Expected Aperture dictionary. ' \
|
|
95
95
|
'Got {}.'.format(data['type'])
|
|
96
96
|
|
|
97
|
-
# remove any invalid holes from the geometry
|
|
98
|
-
geo_dict = data['geometry']
|
|
99
|
-
if 'holes' in geo_dict and geo_dict['holes'] is not None:
|
|
100
|
-
for i, hole_list in enumerate(geo_dict['holes']):
|
|
101
|
-
if len(hole_list) < 3:
|
|
102
|
-
geo_dict['holes'].pop(i)
|
|
103
|
-
|
|
104
97
|
# serialize the aperture
|
|
105
98
|
is_operable = data['is_operable'] if 'is_operable' in data else False
|
|
106
99
|
if data['boundary_condition']['type'] == 'Outdoors':
|
|
@@ -96,13 +96,6 @@ class Door(_BaseWithShade):
|
|
|
96
96
|
assert data['type'] == 'Door', 'Expected Door dictionary. ' \
|
|
97
97
|
'Got {}.'.format(data['type'])
|
|
98
98
|
|
|
99
|
-
# remove any invalid holes from the geometry
|
|
100
|
-
geo_dict = data['geometry']
|
|
101
|
-
if 'holes' in geo_dict and geo_dict['holes'] is not None:
|
|
102
|
-
for i, hole_list in enumerate(geo_dict['holes']):
|
|
103
|
-
if len(hole_list) < 3:
|
|
104
|
-
geo_dict['holes'].pop(i)
|
|
105
|
-
|
|
106
99
|
# serialize the door
|
|
107
100
|
is_glass = data['is_glass'] if 'is_glass' in data else False
|
|
108
101
|
if data['boundary_condition']['type'] == 'Outdoors':
|
|
@@ -133,13 +133,6 @@ class Face(_BaseWithShade):
|
|
|
133
133
|
assert data['type'] == 'Face', 'Expected Face dictionary. ' \
|
|
134
134
|
'Got {}.'.format(data['type'])
|
|
135
135
|
|
|
136
|
-
# remove any invalid holes from the geometry
|
|
137
|
-
geo_dict = data['geometry']
|
|
138
|
-
if 'holes' in geo_dict and geo_dict['holes'] is not None:
|
|
139
|
-
for i, hole_list in enumerate(geo_dict['holes']):
|
|
140
|
-
if len(hole_list) < 3:
|
|
141
|
-
geo_dict['holes'].pop(i)
|
|
142
|
-
|
|
143
136
|
# first serialize it with an outdoor boundary condition
|
|
144
137
|
face_type = face_types.by_name(data['face_type'])
|
|
145
138
|
face = cls(data['identifier'], Face3D.from_dict(data['geometry']),
|
|
@@ -161,11 +161,16 @@ class Model(_Base):
|
|
|
161
161
|
self._properties = ModelProperties(self)
|
|
162
162
|
|
|
163
163
|
@classmethod
|
|
164
|
-
def from_dict(cls, data):
|
|
164
|
+
def from_dict(cls, data, cleanup_irrational=False):
|
|
165
165
|
"""Initialize a Model from a dictionary.
|
|
166
166
|
|
|
167
167
|
Args:
|
|
168
168
|
data: A dictionary representation of a Model object.
|
|
169
|
+
cleanup_irrational: Boolean to note whether common types of irrational
|
|
170
|
+
objects should be cleaned or removed from the dictionary before
|
|
171
|
+
serializing the model to Python. Typical cases that are removed
|
|
172
|
+
this way include Face3Ds with fewer than 3 vertices, Rooms that
|
|
173
|
+
have no Face geometry, etc. (Default: False).
|
|
169
174
|
"""
|
|
170
175
|
# check the type of dictionary
|
|
171
176
|
assert data['type'] == 'Model', 'Expected Model dictionary. ' \
|
|
@@ -179,6 +184,10 @@ class Model(_Base):
|
|
|
179
184
|
angle_tol = 1.0 if 'angle_tolerance' not in data or \
|
|
180
185
|
data['angle_tolerance'] is None else data['angle_tolerance']
|
|
181
186
|
|
|
187
|
+
# clean the irrational objects out if requested
|
|
188
|
+
if cleanup_irrational:
|
|
189
|
+
cls.clean_irrational_geometry(data)
|
|
190
|
+
|
|
182
191
|
# import all of the geometry
|
|
183
192
|
rooms = None # import rooms
|
|
184
193
|
if 'rooms' in data and data['rooms'] is not None:
|
|
@@ -244,11 +253,16 @@ class Model(_Base):
|
|
|
244
253
|
return model
|
|
245
254
|
|
|
246
255
|
@classmethod
|
|
247
|
-
def from_file(cls, hb_file):
|
|
256
|
+
def from_file(cls, hb_file, cleanup_irrational=False):
|
|
248
257
|
"""Initialize a Model from a HBJSON or HBpkl file, auto-sensing the type.
|
|
249
258
|
|
|
250
259
|
Args:
|
|
251
260
|
hb_file: Path to either a HBJSON or HBpkl file.
|
|
261
|
+
cleanup_irrational: Boolean to note whether common types of irrational
|
|
262
|
+
objects should be cleaned or removed from the dictionary before
|
|
263
|
+
serializing the model to Python. Typical cases that are removed
|
|
264
|
+
this way include Face3Ds with fewer than 3 vertices, Rooms that
|
|
265
|
+
have no Face geometry, etc. (Default: False).
|
|
252
266
|
"""
|
|
253
267
|
# sense the file type from the first character to avoid maxing memory with JSON
|
|
254
268
|
# this is needed since queenbee overwrites all file extensions
|
|
@@ -258,15 +272,20 @@ class Model(_Base):
|
|
|
258
272
|
is_json = True if first_char == '{' or second_char == '{' else False
|
|
259
273
|
# load the file using either HBJSON pathway or HBpkl
|
|
260
274
|
if is_json:
|
|
261
|
-
return cls.from_hbjson(hb_file)
|
|
262
|
-
return cls.from_hbpkl(hb_file)
|
|
275
|
+
return cls.from_hbjson(hb_file, cleanup_irrational)
|
|
276
|
+
return cls.from_hbpkl(hb_file, cleanup_irrational)
|
|
263
277
|
|
|
264
278
|
@classmethod
|
|
265
|
-
def from_hbjson(cls, hbjson_file):
|
|
279
|
+
def from_hbjson(cls, hbjson_file, cleanup_irrational=False):
|
|
266
280
|
"""Initialize a Model from a HBJSON file.
|
|
267
281
|
|
|
268
282
|
Args:
|
|
269
283
|
hbjson_file: Path to HBJSON file.
|
|
284
|
+
cleanup_irrational: Boolean to note whether common types of irrational
|
|
285
|
+
objects should be cleaned or removed from the dictionary before
|
|
286
|
+
serializing the model to Python. Typical cases that are removed
|
|
287
|
+
this way include Face3Ds with fewer than 3 vertices, Rooms that
|
|
288
|
+
have no Face geometry, etc. (Default: False).
|
|
270
289
|
"""
|
|
271
290
|
assert os.path.isfile(hbjson_file), 'Failed to find %s' % hbjson_file
|
|
272
291
|
with io.open(hbjson_file, encoding='utf-8') as inf:
|
|
@@ -276,19 +295,24 @@ class Model(_Base):
|
|
|
276
295
|
if second_char == '{':
|
|
277
296
|
inf.read(1)
|
|
278
297
|
data = json.load(inf)
|
|
279
|
-
return cls.from_dict(data)
|
|
298
|
+
return cls.from_dict(data, cleanup_irrational)
|
|
280
299
|
|
|
281
300
|
@classmethod
|
|
282
|
-
def from_hbpkl(cls, hbpkl_file):
|
|
301
|
+
def from_hbpkl(cls, hbpkl_file, cleanup_irrational=False):
|
|
283
302
|
"""Initialize a Model from a HBpkl file.
|
|
284
303
|
|
|
285
304
|
Args:
|
|
286
305
|
hbpkl_file: Path to HBpkl file.
|
|
306
|
+
cleanup_irrational: Boolean to note whether common types of irrational
|
|
307
|
+
objects should be cleaned or removed from the dictionary before
|
|
308
|
+
serializing the model to Python. Typical cases that are removed
|
|
309
|
+
this way include Face3Ds with fewer than 3 vertices, Rooms that
|
|
310
|
+
have no Face geometry, etc. (Default: False).
|
|
287
311
|
"""
|
|
288
312
|
assert os.path.isfile(hbpkl_file), 'Failed to find %s' % hbpkl_file
|
|
289
313
|
with open(hbpkl_file, 'rb') as inf:
|
|
290
314
|
data = pickle.load(inf)
|
|
291
|
-
return cls.from_dict(data)
|
|
315
|
+
return cls.from_dict(data, cleanup_irrational)
|
|
292
316
|
|
|
293
317
|
@classmethod
|
|
294
318
|
def from_stl(cls, file_path, geometry_to_faces=False, units='Meters',
|
|
@@ -3685,6 +3709,97 @@ class Model(_Base):
|
|
|
3685
3709
|
out_dict['valid'] = False
|
|
3686
3710
|
return json.dumps(out_dict, indent=4)
|
|
3687
3711
|
|
|
3712
|
+
@staticmethod
|
|
3713
|
+
def clean_irrational_geometry(model_dict):
|
|
3714
|
+
"""Remove irrational geometry objects from a honeybee Model dictionary.
|
|
3715
|
+
|
|
3716
|
+
This can be useful to run prior to serializing the Model object from a
|
|
3717
|
+
dictionary if it was produced from a source other than the Python
|
|
3718
|
+
core libraries, in which case the dictionary is necessarily rational
|
|
3719
|
+
and serializable. This is because not all honeybee-schema bindings
|
|
3720
|
+
enforce fundamental definitions of geometry types upon initialization
|
|
3721
|
+
of the geometry objects, leading to exceptions when an attempt is made
|
|
3722
|
+
to serialize them to Python. Furthermore, it is possible that the honeybee
|
|
3723
|
+
Model dictionary did not originate from any schema bindings at all, in
|
|
3724
|
+
which case it is highly recommended that this method be run.
|
|
3725
|
+
|
|
3726
|
+
Typical irrational geometry cases that are removed by this method include.
|
|
3727
|
+
|
|
3728
|
+
* Apertures/Doors with less than 3 vertices or holes less than 3 vertices.
|
|
3729
|
+
* Faces with less than 3 vertices or holes with less than 3 vertices.
|
|
3730
|
+
* Rooms that have no Face geometry.
|
|
3731
|
+
* Shade Face3Ds with less than 3 vertices or holes with less than 3 vertices.
|
|
3732
|
+
* ShadeMesh Mesh3Ds with no faces or faces with less than 3 vertices.
|
|
3733
|
+
"""
|
|
3734
|
+
# clean all of the Room geometry in the model dictionary
|
|
3735
|
+
if 'rooms' in model_dict and model_dict['rooms'] is not None:
|
|
3736
|
+
for ri in range(len(model_dict['rooms']) - 1, -1, -1):
|
|
3737
|
+
r_dict = model_dict['rooms'][ri]
|
|
3738
|
+
# clean all of the Face geometry
|
|
3739
|
+
Model._clean_irrational_geo_with_shade(r_dict['faces'])
|
|
3740
|
+
for f_dict in r_dict['faces']:
|
|
3741
|
+
# clean all of the Aperture/Door geometry
|
|
3742
|
+
if 'apertures' in f_dict and f_dict['apertures'] is not None:
|
|
3743
|
+
Model._clean_irrational_geo_with_shade(f_dict['apertures'])
|
|
3744
|
+
if 'doors' in f_dict and f_dict['doors'] is not None:
|
|
3745
|
+
Model._clean_irrational_geo_with_shade(f_dict['doors'])
|
|
3746
|
+
# clean the assigned shade geometry
|
|
3747
|
+
if 'outdoor_shades' in r_dict and r_dict['outdoor_shades'] is not None:
|
|
3748
|
+
Model._clean_irrational_face3ds(r_dict['outdoor_shades'])
|
|
3749
|
+
if 'indoor_shades' in r_dict and r_dict['indoor_shades'] is not None:
|
|
3750
|
+
Model._clean_irrational_face3ds(r_dict['indoor_shades'])
|
|
3751
|
+
if len(r_dict['faces']) == 0: # the entire Room is irrational
|
|
3752
|
+
model_dict['rooms'].pop(ri)
|
|
3753
|
+
# clean all of the orphaned geometry in the model dictionary
|
|
3754
|
+
if 'orphaned_faces' in model_dict and model_dict['orphaned_faces'] is not None:
|
|
3755
|
+
Model._clean_irrational_geo_with_shade(model_dict['orphaned_faces'])
|
|
3756
|
+
if 'orphaned_apertures' in model_dict and \
|
|
3757
|
+
model_dict['orphaned_apertures'] is not None:
|
|
3758
|
+
Model._clean_irrational_geo_with_shade(model_dict['orphaned_apertures'])
|
|
3759
|
+
if 'orphaned_doors' in model_dict and model_dict['orphaned_doors'] is not None:
|
|
3760
|
+
Model._clean_irrational_geo_with_shade(model_dict['orphaned_doors'])
|
|
3761
|
+
if 'orphaned_shades' in model_dict and model_dict['orphaned_shades'] is not None:
|
|
3762
|
+
Model._clean_irrational_face3ds(model_dict['orphaned_shades'])
|
|
3763
|
+
# clean all of the shade mesh geometry in the model dictionary
|
|
3764
|
+
if 'shade_meshes' in model_dict and model_dict['shade_meshes'] is not None:
|
|
3765
|
+
for smi in range(len(model_dict['shade_meshes']) - 1, -1, -1):
|
|
3766
|
+
sm_dict = model_dict['shade_meshes'][smi]['geometry']
|
|
3767
|
+
for mfi in range(len(sm_dict['faces']) - 1, -1, -1):
|
|
3768
|
+
mf = sm_dict['faces'][mfi]
|
|
3769
|
+
if len(mf) not in (3, 4):
|
|
3770
|
+
sm_dict['faces'].pop(mfi)
|
|
3771
|
+
else:
|
|
3772
|
+
for ind in mf:
|
|
3773
|
+
try:
|
|
3774
|
+
sm_dict['vertices'][ind]
|
|
3775
|
+
except IndexError:
|
|
3776
|
+
sm_dict['faces'].pop(mfi)
|
|
3777
|
+
break
|
|
3778
|
+
if len(sm_dict['faces']) == 0:
|
|
3779
|
+
model_dict['shade_meshes'].pop(smi)
|
|
3780
|
+
|
|
3781
|
+
@staticmethod
|
|
3782
|
+
def _clean_irrational_geo_with_shade(geo_obj_dicts):
|
|
3783
|
+
"""Clean irrational Face3Ds out of a list of honeybee geometry objects."""
|
|
3784
|
+
Model._clean_irrational_face3ds(geo_obj_dicts)
|
|
3785
|
+
for f_dict in geo_obj_dicts:
|
|
3786
|
+
if 'outdoor_shades' in f_dict and f_dict['outdoor_shades'] is not None:
|
|
3787
|
+
Model._clean_irrational_face3ds(f_dict['outdoor_shades'])
|
|
3788
|
+
if 'indoor_shades' in f_dict and f_dict['indoor_shades'] is not None:
|
|
3789
|
+
Model._clean_irrational_face3ds(f_dict['indoor_shades'])
|
|
3790
|
+
|
|
3791
|
+
@staticmethod
|
|
3792
|
+
def _clean_irrational_face3ds(geo_obj_dicts):
|
|
3793
|
+
"""Clean irrational Face3Ds out of a list of honeybee geometry objects."""
|
|
3794
|
+
for fi in range(len(geo_obj_dicts) - 1, -1, -1):
|
|
3795
|
+
f_dict = geo_obj_dicts[fi]
|
|
3796
|
+
face_3d = f_dict['geometry']
|
|
3797
|
+
if len(face_3d['boundary']) < 3:
|
|
3798
|
+
geo_obj_dicts.pop(fi)
|
|
3799
|
+
continue
|
|
3800
|
+
elif 'holes' in face_3d:
|
|
3801
|
+
face_3d['holes'] = [h for h in face_3d['holes'] if len(h) >= 3]
|
|
3802
|
+
|
|
3688
3803
|
@staticmethod
|
|
3689
3804
|
def conversion_factor_to_meters(units):
|
|
3690
3805
|
"""Get the conversion factor to meters based on input units.
|
|
@@ -86,16 +86,9 @@ class Shade(_Base):
|
|
|
86
86
|
assert data['type'] == 'Shade', 'Expected Shade dictionary. ' \
|
|
87
87
|
'Got {}.'.format(data['type'])
|
|
88
88
|
|
|
89
|
-
# remove any invalid holes from the geometry
|
|
90
|
-
geo_dict = data['geometry']
|
|
91
|
-
if 'holes' in geo_dict and geo_dict['holes'] is not None:
|
|
92
|
-
for i, hole_list in enumerate(geo_dict['holes']):
|
|
93
|
-
if len(hole_list) < 3:
|
|
94
|
-
geo_dict['holes'].pop(i)
|
|
95
|
-
|
|
96
89
|
# serialize the dictionary to an object
|
|
97
|
-
|
|
98
|
-
shade = cls(data['identifier'], Face3D.from_dict(
|
|
90
|
+
is_det = data['is_detached'] if 'is_detached' in data else False
|
|
91
|
+
shade = cls(data['identifier'], Face3D.from_dict(data['geometry']), is_det)
|
|
99
92
|
if 'display_name' in data and data['display_name'] is not None:
|
|
100
93
|
shade.display_name = data['display_name']
|
|
101
94
|
if 'user_data' in data and data['user_data'] is not None:
|
|
@@ -379,7 +379,7 @@ def test_model_add_prefix():
|
|
|
379
379
|
def test_reset_room_ids():
|
|
380
380
|
"""Test the reset_room_ids method."""
|
|
381
381
|
model_json = './tests/json/model_with_adiabatic.hbjson'
|
|
382
|
-
parsed_model = Model.from_hbjson(model_json)
|
|
382
|
+
parsed_model = Model.from_hbjson(model_json, cleanup_irrational=True)
|
|
383
383
|
|
|
384
384
|
new_model = parsed_model.duplicate()
|
|
385
385
|
new_model.reset_room_ids()
|
|
@@ -390,7 +390,7 @@ def test_reset_room_ids():
|
|
|
390
390
|
def test_reset_ids():
|
|
391
391
|
"""Test the reset_ids method."""
|
|
392
392
|
model_json = './tests/json/model_with_adiabatic.hbjson'
|
|
393
|
-
parsed_model = Model.from_hbjson(model_json)
|
|
393
|
+
parsed_model = Model.from_hbjson(model_json, cleanup_irrational=True)
|
|
394
394
|
|
|
395
395
|
new_model = parsed_model.duplicate()
|
|
396
396
|
new_model.reset_ids(True)
|
|
@@ -402,7 +402,7 @@ def test_reset_ids():
|
|
|
402
402
|
def test_offset_aperture_edges():
|
|
403
403
|
"""Test the Face offset_aperture_edges method."""
|
|
404
404
|
model_json = './tests/json/room_for_window_offset.hbjson'
|
|
405
|
-
parsed_model = Model.from_hbjson(model_json)
|
|
405
|
+
parsed_model = Model.from_hbjson(model_json, cleanup_irrational=True)
|
|
406
406
|
test_room = parsed_model.rooms[0]
|
|
407
407
|
test_face = test_room[1]
|
|
408
408
|
|
|
@@ -1164,6 +1164,75 @@ def test_from_dict_method_extensions():
|
|
|
1164
1164
|
assert isinstance(parsed_model, Model)
|
|
1165
1165
|
|
|
1166
1166
|
|
|
1167
|
+
def test_cleanup_irrational():
|
|
1168
|
+
"""Test the cleanup_irrational method during serialization."""
|
|
1169
|
+
room = Room.from_box('TinyHouseZone', 5, 10, 3)
|
|
1170
|
+
south_face = room[3]
|
|
1171
|
+
south_face.apertures_by_ratio(0.4, 0.01)
|
|
1172
|
+
south_face.apertures[0].overhang(0.5, indoor=False)
|
|
1173
|
+
south_face.apertures[0].overhang(0.5, indoor=True)
|
|
1174
|
+
south_face.apertures[0].move_shades(Vector3D(0, 0, -0.5))
|
|
1175
|
+
north_face = room[1]
|
|
1176
|
+
door_verts = [Point3D(2, 10, 0.1), Point3D(1, 10, 0.1),
|
|
1177
|
+
Point3D(1, 10, 2.5), Point3D(2, 10, 2.5)]
|
|
1178
|
+
aperture_verts = [Point3D(4.5, 10, 1), Point3D(2.5, 10, 1),
|
|
1179
|
+
Point3D(2.5, 10, 2.5), Point3D(4.5, 10, 2.5)]
|
|
1180
|
+
door = Door('FrontDoor', Face3D(door_verts))
|
|
1181
|
+
north_face.add_door(door)
|
|
1182
|
+
aperture = Aperture('FrontAperture', Face3D(aperture_verts))
|
|
1183
|
+
north_face.add_aperture(aperture)
|
|
1184
|
+
|
|
1185
|
+
model = Model('TinyHouse', [room])
|
|
1186
|
+
model_dict = model.to_dict()
|
|
1187
|
+
|
|
1188
|
+
irrational_dict = {
|
|
1189
|
+
'type': 'Model',
|
|
1190
|
+
'identifier': 'irrational_model',
|
|
1191
|
+
'units': 'Meters',
|
|
1192
|
+
'tolerance': 0.001,
|
|
1193
|
+
'angle_tolerance': 1.0,
|
|
1194
|
+
'properties': model_dict['properties'],
|
|
1195
|
+
'rooms': [
|
|
1196
|
+
model_dict['rooms'][0],
|
|
1197
|
+
{
|
|
1198
|
+
'type': 'Room',
|
|
1199
|
+
'identifier': 'irrational_room',
|
|
1200
|
+
'faces': [
|
|
1201
|
+
{
|
|
1202
|
+
'type': 'Face',
|
|
1203
|
+
'identifier': 'irrational_face',
|
|
1204
|
+
'face_type': 'Wall',
|
|
1205
|
+
'geometry': {
|
|
1206
|
+
'type': 'Face3D',
|
|
1207
|
+
'boundary': [
|
|
1208
|
+
(0, 0, 0), (0, 0, 1)
|
|
1209
|
+
]
|
|
1210
|
+
},
|
|
1211
|
+
'apertures': [
|
|
1212
|
+
{
|
|
1213
|
+
'type': 'Aperture',
|
|
1214
|
+
'identifier': 'irrational_aperture',
|
|
1215
|
+
'geometry': {
|
|
1216
|
+
'type': 'Face3D',
|
|
1217
|
+
'boundary': [
|
|
1218
|
+
(0, 0, 0.2), (0, 0, 0.8)
|
|
1219
|
+
]
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
]
|
|
1223
|
+
},
|
|
1224
|
+
]
|
|
1225
|
+
}
|
|
1226
|
+
]
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
with pytest.raises(ValueError):
|
|
1230
|
+
Model.from_dict(irrational_dict, cleanup_irrational=False)
|
|
1231
|
+
|
|
1232
|
+
clean_model = Model.from_dict(irrational_dict, cleanup_irrational=True)
|
|
1233
|
+
assert len(clean_model.rooms) == 1
|
|
1234
|
+
|
|
1235
|
+
|
|
1167
1236
|
def test_comparison_report():
|
|
1168
1237
|
"""Test the comparison_report method."""
|
|
1169
1238
|
room = Room.from_box('TinyHouseZone', 5, 10, 3)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/minor_geometry/existing_model.hbjson
RENAMED
|
File without changes
|
{honeybee_core-1.62.12 → honeybee_core-1.63.0}/tests/json/minor_geometry/updated_model.hbjson
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|