honeybee-core 1.61.18__tar.gz → 1.64.7__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.61.18 → honeybee_core-1.64.7}/.github/workflows/ci.yaml +1 -1
- {honeybee_core-1.61.18/honeybee_core.egg-info → honeybee_core-1.64.7}/PKG-INFO +21 -7
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/deploy.sh +1 -1
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/dev-requirements.txt +5 -4
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/aperture.py +79 -6
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/cli/edit.py +2 -1
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/cli/validate.py +14 -7
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/door.py +70 -5
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/face.py +118 -12
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/model.py +729 -116
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/properties.py +52 -16
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/room.py +483 -67
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/search.py +8 -2
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/shade.py +31 -3
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/shademesh.py +17 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/typing.py +25 -11
- {honeybee_core-1.61.18 → honeybee_core-1.64.7/honeybee_core.egg-info}/PKG-INFO +21 -7
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee_core.egg-info/SOURCES.txt +1 -1
- honeybee_core-1.64.7/honeybee_core.egg-info/requires.txt +5 -0
- honeybee_core-1.64.7/requirements.txt +3 -0
- honeybee_core-1.64.7/setup.cfg +4 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/setup.py +6 -2
- honeybee_core-1.64.7/tests/json/room_for_envelope_edges.hbjson +1 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/model_test.py +138 -3
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/room_test.py +99 -0
- honeybee_core-1.61.18/honeybee_core.egg-info/requires.txt +0 -5
- honeybee_core-1.61.18/requirements.txt +0 -3
- honeybee_core-1.61.18/setup.cfg +0 -13
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/.github/workflows/dependency-release.yaml +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/.gitignore +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/.releaserc.json +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/CODE_OF_CONDUCT.md +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/CONTRIBUTING.md +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/LICENSE +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/MANIFEST.in +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/README.md +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/docs/_build/.nojekyll +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/docs/_build/README.md +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/docs/_build/docs/README.md +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/docs/_static/custom.css +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/docs/_templates/layout.html +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/docs/cli/index.rst +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/docs/conf.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/docs/index.rst +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/__init__.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/__main__.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/_base.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/_basewithshade.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/_lockable.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/altnumber.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/boundarycondition.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/checkdup.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/cli/__init__.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/cli/compare.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/cli/create.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/cli/lib.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/cli/setconfig.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/colorobj.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/config.json +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/config.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/dictutil.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/extensionutil.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/facetype.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/logutil.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/orientation.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/units.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/writer/__init__.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/writer/aperture.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/writer/door.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/writer/face.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/writer/model.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/writer/room.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/writer/shade.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee/writer/shademesh.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee_core.egg-info/dependency_links.txt +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee_core.egg-info/entry_points.txt +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/honeybee_core.egg-info/top_level.txt +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/__init__.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/aperture_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/boundary_condition_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/cli_compare_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/cli_create_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/cli_edit_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/cli_validate_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/colorobj_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/config_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/dictutil_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/door_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/face_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/facetype_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/ShoeBox.json +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/bad_geometry_model.hbjson +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/colliding_room_volumes.hbjson +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/compare_model_1.hbjson +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/compare_model_2.hbjson +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/complex_polyfaces.json +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/minor_geometry/existing_model.hbjson +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/minor_geometry/updated_model.hbjson +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/mismatched_area_adj.hbjson +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/model_with_adiabatic.hbjson +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/model_with_holes.hbjson +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/model_without_adjacency.hbjson +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/nonascii_face.json +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/polygons_for_gap_boundary.json +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/room_for_window_offset.hbjson +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/json/single_family_home.hbjson +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/lockable_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/orientation_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/search_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/shade_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/shademesh_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/stl/cube_ascii.stl +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/stl/cube_binary.stl +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/typing_test.py +0 -0
- {honeybee_core-1.61.18 → honeybee_core-1.64.7}/tests/units_test.py +0 -0
|
@@ -55,7 +55,7 @@ jobs:
|
|
|
55
55
|
run: |
|
|
56
56
|
nextRelease="`npx semantic-release@^23.1.1 --dryRun | grep -oP 'Published release \K.*? ' || true`"
|
|
57
57
|
npx semantic-release@^23.1.1
|
|
58
|
-
echo "
|
|
58
|
+
echo "tag=$nextRelease" >> $GITHUB_OUTPUT
|
|
59
59
|
env:
|
|
60
60
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
61
61
|
PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
|
@@ -1,22 +1,36 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: honeybee-core
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.64.7
|
|
4
4
|
Summary: A library to create 3D building geometry for various types of environmental simulation.
|
|
5
5
|
Home-page: https://github.com/ladybug-tools/honeybee-core
|
|
6
6
|
Author: Ladybug Tools
|
|
7
7
|
Author-email: info@ladybug.tools
|
|
8
8
|
License: AGPL-3.0
|
|
9
9
|
Classifier: Programming Language :: Python :: 2.7
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.7
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
16
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
13
|
-
Classifier:
|
|
17
|
+
Classifier: Programming Language :: Python :: Implementation :: IronPython
|
|
14
18
|
Classifier: Operating System :: OS Independent
|
|
15
19
|
Description-Content-Type: text/markdown
|
|
16
20
|
License-File: LICENSE
|
|
17
|
-
Requires-Dist: ladybug-core==0.44.
|
|
18
|
-
Requires-Dist: ladybug-geometry-polyskel==1.7.
|
|
19
|
-
Requires-Dist: honeybee-schema==1.59.
|
|
21
|
+
Requires-Dist: ladybug-core==0.44.28
|
|
22
|
+
Requires-Dist: ladybug-geometry-polyskel==1.7.35
|
|
23
|
+
Requires-Dist: honeybee-schema==1.59.1; python_version >= "3.7"
|
|
24
|
+
Dynamic: author
|
|
25
|
+
Dynamic: author-email
|
|
26
|
+
Dynamic: classifier
|
|
27
|
+
Dynamic: description
|
|
28
|
+
Dynamic: description-content-type
|
|
29
|
+
Dynamic: home-page
|
|
30
|
+
Dynamic: license
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
Dynamic: requires-dist
|
|
33
|
+
Dynamic: summary
|
|
20
34
|
|
|
21
35
|

|
|
22
36
|
|
|
@@ -4,10 +4,10 @@ sphinx-bootstrap-theme==0.8.1
|
|
|
4
4
|
sphinxcontrib-fulltoc==1.2.0
|
|
5
5
|
sphinxcontrib-websupport==2.0.0;python_version>='3.6'
|
|
6
6
|
sphinx-click==6.0.0;python_version>='3.6'
|
|
7
|
-
twine==
|
|
8
|
-
wheel==0.
|
|
9
|
-
setuptools==
|
|
10
|
-
|
|
7
|
+
twine==6.1.0;python_version>='3.6'
|
|
8
|
+
wheel==0.45.1;python_version>='3.6'
|
|
9
|
+
setuptools==80.9.0;python_version>='3.6'
|
|
10
|
+
build==1.3.0;python_version>='3.6'
|
|
11
11
|
pytest==4.6.9;python_version<'3.0'
|
|
12
12
|
Sphinx==1.8.5;python_version<'3.0'
|
|
13
13
|
sphinxcontrib-websupport==1.1.2;python_version<'3.0'
|
|
@@ -15,4 +15,5 @@ sphinx-click==4.4.0;python_version<'3.0'
|
|
|
15
15
|
twine==1.13.0;python_version<'3.0'
|
|
16
16
|
wheel==0.38.1;python_version<'3.0'
|
|
17
17
|
setuptools==44.1.0;python_version<'3.0'
|
|
18
|
+
build==0.1.0;python_version<'3.0'
|
|
18
19
|
importlib-metadata==2.0.0;python_version<'3.0'
|
|
@@ -2,16 +2,18 @@
|
|
|
2
2
|
"""Honeybee Aperture."""
|
|
3
3
|
from __future__ import division
|
|
4
4
|
import math
|
|
5
|
+
import re
|
|
5
6
|
|
|
6
|
-
from ladybug_geometry.geometry2d
|
|
7
|
-
from ladybug_geometry.geometry3d
|
|
8
|
-
from ladybug_geometry.geometry3d.face import Face3D
|
|
7
|
+
from ladybug_geometry.geometry2d import Vector2D
|
|
8
|
+
from ladybug_geometry.geometry3d import Point3D, Face3D
|
|
9
9
|
from ladybug.color import Color
|
|
10
10
|
|
|
11
11
|
from ._basewithshade import _BaseWithShade
|
|
12
12
|
from .typing import clean_string
|
|
13
|
+
from .search import get_attr_nested
|
|
13
14
|
from .properties import ApertureProperties
|
|
14
15
|
from .boundarycondition import boundary_conditions, Outdoors, Surface
|
|
16
|
+
from .facetype import RoofCeiling
|
|
15
17
|
from .shade import Shade
|
|
16
18
|
import honeybee.writer.aperture as writer
|
|
17
19
|
|
|
@@ -276,6 +278,23 @@ class Aperture(_BaseWithShade):
|
|
|
276
278
|
"""
|
|
277
279
|
return isinstance(self.boundary_condition, Outdoors)
|
|
278
280
|
|
|
281
|
+
@property
|
|
282
|
+
def gbxml_type(self):
|
|
283
|
+
"""Get text for the type of object this is in gbXML schema.
|
|
284
|
+
|
|
285
|
+
This will always be one of the following.
|
|
286
|
+
|
|
287
|
+
* FixedWindow
|
|
288
|
+
* OperableWindow
|
|
289
|
+
* FixedSkylight
|
|
290
|
+
* OperableSkylight
|
|
291
|
+
"""
|
|
292
|
+
base_type = 'Window'
|
|
293
|
+
if self.has_parent and isinstance(self.parent.type, RoofCeiling):
|
|
294
|
+
base_type = 'Skylight'
|
|
295
|
+
win_type = 'Fixed' if not self.is_operable else 'Operable'
|
|
296
|
+
return win_type + base_type
|
|
297
|
+
|
|
279
298
|
@property
|
|
280
299
|
def type_color(self):
|
|
281
300
|
"""Get a Color to be used in visualizations by type."""
|
|
@@ -298,16 +317,23 @@ class Aperture(_BaseWithShade):
|
|
|
298
317
|
return math.degrees(
|
|
299
318
|
north_vector.angle_clockwise(Vector2D(self.normal.x, self.normal.y)))
|
|
300
319
|
|
|
301
|
-
def cardinal_direction(self, north_vector=Vector2D(0, 1)):
|
|
320
|
+
def cardinal_direction(self, north_vector=Vector2D(0, 1), angle_tolerance=1):
|
|
302
321
|
"""Get text description for the cardinal direction that the aperture is pointing.
|
|
303
322
|
|
|
304
323
|
Will be one of the following: ('North', 'NorthEast', 'East', 'SouthEast',
|
|
305
|
-
'South', 'SouthWest', 'West', 'NorthWest').
|
|
324
|
+
'South', 'SouthWest', 'West', 'NorthWest', 'Up', 'Down').
|
|
306
325
|
|
|
307
326
|
Args:
|
|
308
327
|
north_vector: A ladybug_geometry Vector2D for the north direction.
|
|
309
328
|
Default is the Y-axis (0, 1).
|
|
310
|
-
|
|
329
|
+
angle_tolerance: The angle tolerance in degrees used to determine if
|
|
330
|
+
the geometry is perfectly Up or Down. (Default: 1).
|
|
331
|
+
"""
|
|
332
|
+
tilt = self.tilt
|
|
333
|
+
if tilt < angle_tolerance:
|
|
334
|
+
return 'Up'
|
|
335
|
+
elif tilt > 180 - angle_tolerance:
|
|
336
|
+
return 'Down'
|
|
311
337
|
orient = self.horizontal_orientation(north_vector)
|
|
312
338
|
orient_text = ('North', 'NorthEast', 'East', 'SouthEast', 'South',
|
|
313
339
|
'SouthWest', 'West', 'NorthWest')
|
|
@@ -317,6 +343,31 @@ class Aperture(_BaseWithShade):
|
|
|
317
343
|
return orient_text[i]
|
|
318
344
|
return orient_text[0]
|
|
319
345
|
|
|
346
|
+
def cardinal_abbrev(self, north_vector=Vector2D(0, 1), angle_tolerance=1):
|
|
347
|
+
"""Get text abbreviation for the cardinal direction that the aperture is pointing.
|
|
348
|
+
|
|
349
|
+
Will be one of the following: ('N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW',
|
|
350
|
+
'Up', 'Down').
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
north_vector: A ladybug_geometry Vector2D for the north direction.
|
|
354
|
+
Default is the Y-axis (0, 1).
|
|
355
|
+
angle_tolerance: The angle tolerance in degrees used to determine if
|
|
356
|
+
the aperture is perfectly Up or Down. (Default: 1).
|
|
357
|
+
"""
|
|
358
|
+
tilt = self.tilt
|
|
359
|
+
if tilt < angle_tolerance:
|
|
360
|
+
return 'Up'
|
|
361
|
+
elif tilt > 180 - angle_tolerance:
|
|
362
|
+
return 'Down'
|
|
363
|
+
orient = self.horizontal_orientation(north_vector)
|
|
364
|
+
orient_text = ('N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW')
|
|
365
|
+
angles = (22.5, 67.5, 112.5, 157.5, 202.5, 247.5, 292.5, 337.5)
|
|
366
|
+
for i, ang in enumerate(angles):
|
|
367
|
+
if orient < ang:
|
|
368
|
+
return orient_text[i]
|
|
369
|
+
return orient_text[0]
|
|
370
|
+
|
|
320
371
|
def add_prefix(self, prefix):
|
|
321
372
|
"""Change the identifier of this object and child objects by inserting a prefix.
|
|
322
373
|
|
|
@@ -340,6 +391,28 @@ class Aperture(_BaseWithShade):
|
|
|
340
391
|
in self._boundary_condition._boundary_condition_objects)
|
|
341
392
|
self._boundary_condition = Surface(new_bc_objs, True)
|
|
342
393
|
|
|
394
|
+
def rename_by_attribute(
|
|
395
|
+
self,
|
|
396
|
+
format_str='{parent.parent.display_name} - {gbxml_type} - {cardinal_direction}'
|
|
397
|
+
):
|
|
398
|
+
"""Set the display name of this Aperture using a format string with attributes.
|
|
399
|
+
|
|
400
|
+
Args:
|
|
401
|
+
format_str: Text string for the pattern with which the Aperture will be
|
|
402
|
+
renamed. Any property on this class may be used (eg. gbxml_str)
|
|
403
|
+
and each property should be put in curly brackets. Nested
|
|
404
|
+
properties can be specified by using "." to denote nesting levels
|
|
405
|
+
(eg. properties.energy.construction.display_name). Functions that
|
|
406
|
+
return string outputs can also be passed here as long as these
|
|
407
|
+
functions defaults specified for all arguments.
|
|
408
|
+
"""
|
|
409
|
+
matches = re.findall(r'{([^}]*)}', format_str)
|
|
410
|
+
attributes = [get_attr_nested(self, m, decimal_count=2) for m in matches]
|
|
411
|
+
for attr_name, attr_val in zip(matches, attributes):
|
|
412
|
+
format_str = format_str.replace('{{{}}}'.format(attr_name), attr_val)
|
|
413
|
+
self.display_name = format_str
|
|
414
|
+
return format_str
|
|
415
|
+
|
|
343
416
|
def set_adjacency(self, other_aperture):
|
|
344
417
|
"""Set this aperture to be adjacent to another.
|
|
345
418
|
|
|
@@ -104,7 +104,8 @@ def solve_adjacency(model_file, no_merge, no_intersect, no_overwrite,
|
|
|
104
104
|
air_boundary = not wall
|
|
105
105
|
adiabatic = not surface
|
|
106
106
|
parsed_model.solve_adjacency(
|
|
107
|
-
merge_coplanar, intersect, overwrite,
|
|
107
|
+
merge_coplanar, intersect, overwrite,
|
|
108
|
+
air_boundary=air_boundary, adiabatic=adiabatic)
|
|
108
109
|
|
|
109
110
|
# write the new model out to the file or stdout
|
|
110
111
|
output_file.write(json.dumps(parsed_model.to_dict()))
|
|
@@ -21,10 +21,13 @@ def validate():
|
|
|
21
21
|
'--extension', '-e', help='Text for the name of the extension to be checked. '
|
|
22
22
|
'The value input is case-insensitive such that "radiance" and "Radiance" will '
|
|
23
23
|
'both result in the model being checked for validity with honeybee-radiance. '
|
|
24
|
-
'This value can also be set to "
|
|
25
|
-
'extensions.
|
|
26
|
-
'
|
|
27
|
-
|
|
24
|
+
'This value can also be set to "Generic" in order to run checks for all installed '
|
|
25
|
+
'extensions. Using "Generic" will run all except the most limiting of checks '
|
|
26
|
+
'(eg. the DOE2 lack of support for courtyards) with the goal of producing a model '
|
|
27
|
+
'that can be exported to multiple engines (albeit with a little extra '
|
|
28
|
+
'postprocessing for particularly limited engines). Some common honeybee extension '
|
|
29
|
+
'names that can be input here include: Radiance, EnergyPlus, DOE2, IES, IDAICE',
|
|
30
|
+
type=str, default='Generic', show_default=True)
|
|
28
31
|
@click.option(
|
|
29
32
|
'--plain-text/--json', ' /-j', help='Flag to note whether the output validation '
|
|
30
33
|
'report should be formatted as a JSON object instead of plain text. If set to JSON, '
|
|
@@ -82,7 +85,7 @@ def validate_model_cli(model_file, extension, plain_text, room_overlaps, output_
|
|
|
82
85
|
sys.exit(0)
|
|
83
86
|
|
|
84
87
|
|
|
85
|
-
def validate_model(model_file, extension='
|
|
88
|
+
def validate_model(model_file, extension='Generic', json=False, output_file=None,
|
|
86
89
|
plain_text=True):
|
|
87
90
|
"""Validate all properties of a Model file against the Honeybee schema.
|
|
88
91
|
|
|
@@ -95,8 +98,12 @@ def validate_model(model_file, extension='All', json=False, output_file=None,
|
|
|
95
98
|
The value input here is case-insensitive such that "radiance"
|
|
96
99
|
and "Radiance" will both result in the model being checked for
|
|
97
100
|
validity with honeybee-radiance. This value can also be set to
|
|
98
|
-
"
|
|
99
|
-
|
|
101
|
+
"Generic" in order to run checks for all installed extensions.
|
|
102
|
+
Using "Generic" will run all except the most limiting of checks
|
|
103
|
+
(eg. DOE2's lack of support for courtyards) with the goal of
|
|
104
|
+
producing a model that is export-able to multiple engines (albeit
|
|
105
|
+
with a little extra postprocessing for particularly limited engines).
|
|
106
|
+
Some common dragonfly extension names that can be input here if they
|
|
100
107
|
are installed include:
|
|
101
108
|
|
|
102
109
|
* Radiance
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
"""Honeybee Door."""
|
|
3
3
|
from __future__ import division
|
|
4
4
|
import math
|
|
5
|
+
import re
|
|
5
6
|
|
|
6
|
-
from ladybug_geometry.geometry2d
|
|
7
|
-
from ladybug_geometry.geometry3d
|
|
8
|
-
from ladybug_geometry.geometry3d.face import Face3D
|
|
7
|
+
from ladybug_geometry.geometry2d import Vector2D
|
|
8
|
+
from ladybug_geometry.geometry3d import Point3D, Face3D
|
|
9
9
|
from ladybug.color import Color
|
|
10
10
|
|
|
11
11
|
from ._basewithshade import _BaseWithShade
|
|
12
12
|
from .typing import clean_string
|
|
13
|
+
from .search import get_attr_nested
|
|
13
14
|
from .properties import DoorProperties
|
|
14
15
|
from .boundarycondition import boundary_conditions, Outdoors, Surface
|
|
15
16
|
from .shade import Shade
|
|
@@ -278,6 +279,16 @@ class Door(_BaseWithShade):
|
|
|
278
279
|
"""
|
|
279
280
|
return isinstance(self.boundary_condition, Outdoors)
|
|
280
281
|
|
|
282
|
+
@property
|
|
283
|
+
def gbxml_type(self):
|
|
284
|
+
"""Get text for the type of object this is in gbXML schema."""
|
|
285
|
+
return 'NonSlidingDoor'
|
|
286
|
+
|
|
287
|
+
@property
|
|
288
|
+
def energyplus_type(self):
|
|
289
|
+
"""Get text for the type of object this is in IDF schema."""
|
|
290
|
+
return 'GlassDoor' if self.is_glass else 'Door'
|
|
291
|
+
|
|
281
292
|
@property
|
|
282
293
|
def type_color(self):
|
|
283
294
|
"""Get a Color to be used in visualizations by type."""
|
|
@@ -300,16 +311,23 @@ class Door(_BaseWithShade):
|
|
|
300
311
|
return math.degrees(
|
|
301
312
|
north_vector.angle_clockwise(Vector2D(self.normal.x, self.normal.y)))
|
|
302
313
|
|
|
303
|
-
def cardinal_direction(self, north_vector=Vector2D(0, 1)):
|
|
314
|
+
def cardinal_direction(self, north_vector=Vector2D(0, 1), angle_tolerance=1):
|
|
304
315
|
"""Get text description for the cardinal direction that the door is pointing.
|
|
305
316
|
|
|
306
317
|
Will be one of the following: ('North', 'NorthEast', 'East', 'SouthEast',
|
|
307
|
-
'South', 'SouthWest', 'West', 'NorthWest').
|
|
318
|
+
'South', 'SouthWest', 'West', 'NorthWest', 'Up', 'Down').
|
|
308
319
|
|
|
309
320
|
Args:
|
|
310
321
|
north_vector: A ladybug_geometry Vector2D for the north direction.
|
|
311
322
|
Default is the Y-axis (0, 1).
|
|
323
|
+
angle_tolerance: The angle tolerance in degrees used to determine if
|
|
324
|
+
the geometry is perfectly Up or Down. (Default: 1).
|
|
312
325
|
"""
|
|
326
|
+
tilt = self.tilt
|
|
327
|
+
if tilt < angle_tolerance:
|
|
328
|
+
return 'Up'
|
|
329
|
+
elif tilt > 180 - angle_tolerance:
|
|
330
|
+
return 'Down'
|
|
313
331
|
orient = self.horizontal_orientation(north_vector)
|
|
314
332
|
orient_text = ('North', 'NorthEast', 'East', 'SouthEast', 'South',
|
|
315
333
|
'SouthWest', 'West', 'NorthWest')
|
|
@@ -319,6 +337,31 @@ class Door(_BaseWithShade):
|
|
|
319
337
|
return orient_text[i]
|
|
320
338
|
return orient_text[0]
|
|
321
339
|
|
|
340
|
+
def cardinal_abbrev(self, north_vector=Vector2D(0, 1), angle_tolerance=1):
|
|
341
|
+
"""Get text abbreviation for the cardinal direction that the door is pointing.
|
|
342
|
+
|
|
343
|
+
Will be one of the following: ('N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW',
|
|
344
|
+
'Up', 'Down').
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
north_vector: A ladybug_geometry Vector2D for the north direction.
|
|
348
|
+
Default is the Y-axis (0, 1).
|
|
349
|
+
angle_tolerance: The angle tolerance in degrees used to determine if
|
|
350
|
+
the door is perfectly Up or Down. (Default: 1).
|
|
351
|
+
"""
|
|
352
|
+
tilt = self.tilt
|
|
353
|
+
if tilt < angle_tolerance:
|
|
354
|
+
return 'Up'
|
|
355
|
+
elif tilt > 180 - angle_tolerance:
|
|
356
|
+
return 'Down'
|
|
357
|
+
orient = self.horizontal_orientation(north_vector)
|
|
358
|
+
orient_text = ('N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW')
|
|
359
|
+
angles = (22.5, 67.5, 112.5, 157.5, 202.5, 247.5, 292.5, 337.5)
|
|
360
|
+
for i, ang in enumerate(angles):
|
|
361
|
+
if orient < ang:
|
|
362
|
+
return orient_text[i]
|
|
363
|
+
return orient_text[0]
|
|
364
|
+
|
|
322
365
|
def add_prefix(self, prefix):
|
|
323
366
|
"""Change the identifier of this object and child objects by inserting a prefix.
|
|
324
367
|
|
|
@@ -342,6 +385,28 @@ class Door(_BaseWithShade):
|
|
|
342
385
|
in self._boundary_condition._boundary_condition_objects)
|
|
343
386
|
self._boundary_condition = Surface(new_bc_objs, True)
|
|
344
387
|
|
|
388
|
+
def rename_by_attribute(
|
|
389
|
+
self,
|
|
390
|
+
format_str='{parent.parent.display_name} - {energyplus_type} - {cardinal_direction}'
|
|
391
|
+
):
|
|
392
|
+
"""Set the display name of this Door using a format string with attributes.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
format_str: Text string for the pattern with which the Door will be
|
|
396
|
+
renamed. Any property on this class may be used (eg. energyplus_type)
|
|
397
|
+
and each property should be put in curly brackets. Nested
|
|
398
|
+
properties can be specified by using "." to denote nesting levels
|
|
399
|
+
(eg. properties.energy.construction.display_name). Functions that
|
|
400
|
+
return string outputs can also be passed here as long as these
|
|
401
|
+
functions defaults specified for all arguments.
|
|
402
|
+
"""
|
|
403
|
+
matches = re.findall(r'{([^}]*)}', format_str)
|
|
404
|
+
attributes = [get_attr_nested(self, m, decimal_count=2) for m in matches]
|
|
405
|
+
for attr_name, attr_val in zip(matches, attributes):
|
|
406
|
+
format_str = format_str.replace('{{{}}}'.format(attr_name), attr_val)
|
|
407
|
+
self.display_name = format_str
|
|
408
|
+
return format_str
|
|
409
|
+
|
|
345
410
|
def set_adjacency(self, other_door):
|
|
346
411
|
"""Set this door to be adjacent to another (and vice versa).
|
|
347
412
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"""Honeybee Face."""
|
|
3
3
|
from __future__ import division
|
|
4
4
|
import math
|
|
5
|
+
import re
|
|
5
6
|
|
|
6
7
|
from ladybug_geometry.geometry2d import Vector2D, Point2D, Polygon2D, Mesh2D
|
|
7
8
|
from ladybug_geometry.geometry3d import Vector3D, Point3D, Plane, Face3D
|
|
@@ -9,8 +10,10 @@ from ladybug.color import Color
|
|
|
9
10
|
|
|
10
11
|
from ._basewithshade import _BaseWithShade
|
|
11
12
|
from .typing import clean_string, invalid_dict_error
|
|
13
|
+
from .search import get_attr_nested
|
|
12
14
|
from .properties import FaceProperties
|
|
13
|
-
from .facetype import face_types, get_type_from_normal, AirBoundary,
|
|
15
|
+
from .facetype import face_types, get_type_from_normal, AirBoundary, Wall, \
|
|
16
|
+
Floor, RoofCeiling
|
|
14
17
|
from .boundarycondition import boundary_conditions, get_bc_from_position, \
|
|
15
18
|
_BoundaryCondition, Outdoors, Surface, Ground
|
|
16
19
|
from .shade import Shade
|
|
@@ -130,13 +133,6 @@ class Face(_BaseWithShade):
|
|
|
130
133
|
assert data['type'] == 'Face', 'Expected Face dictionary. ' \
|
|
131
134
|
'Got {}.'.format(data['type'])
|
|
132
135
|
|
|
133
|
-
# remove any invalid holes from the geometry
|
|
134
|
-
geo_dict = data['geometry']
|
|
135
|
-
if 'holes' in geo_dict and geo_dict['holes'] is not None:
|
|
136
|
-
for i, hole_list in enumerate(geo_dict['holes']):
|
|
137
|
-
if len(hole_list) < 3:
|
|
138
|
-
geo_dict['holes'].pop(i)
|
|
139
|
-
|
|
140
136
|
# first serialize it with an outdoor boundary condition
|
|
141
137
|
face_type = face_types.by_name(data['face_type'])
|
|
142
138
|
face = cls(data['identifier'], Face3D.from_dict(data['geometry']),
|
|
@@ -398,6 +394,52 @@ class Face(_BaseWithShade):
|
|
|
398
394
|
"""
|
|
399
395
|
return isinstance(self.boundary_condition, Outdoors)
|
|
400
396
|
|
|
397
|
+
@property
|
|
398
|
+
def gbxml_type(self):
|
|
399
|
+
"""Get text for the type of object this is in gbXML schema.
|
|
400
|
+
|
|
401
|
+
This will always be one of the following.
|
|
402
|
+
|
|
403
|
+
* InteriorWall
|
|
404
|
+
* ExteriorWall
|
|
405
|
+
* UndergroundWall
|
|
406
|
+
* Roof
|
|
407
|
+
* Ceiling
|
|
408
|
+
* UndergroundCeiling
|
|
409
|
+
* InteriorFloor
|
|
410
|
+
* ExposedFloor
|
|
411
|
+
* UndergroundSlab
|
|
412
|
+
* SlabOnGrade
|
|
413
|
+
* Air
|
|
414
|
+
"""
|
|
415
|
+
if isinstance(self.type, AirBoundary):
|
|
416
|
+
return 'Air'
|
|
417
|
+
elif isinstance(self.type, Wall):
|
|
418
|
+
bc_type = 'Interior'
|
|
419
|
+
if isinstance(self.boundary_condition, Outdoors):
|
|
420
|
+
bc_type = 'Exterior'
|
|
421
|
+
elif isinstance(self.boundary_condition, Ground):
|
|
422
|
+
bc_type = 'Underground'
|
|
423
|
+
return bc_type + 'Wall'
|
|
424
|
+
elif isinstance(self.type, Floor):
|
|
425
|
+
if isinstance(self.boundary_condition, Ground):
|
|
426
|
+
if self.has_parent:
|
|
427
|
+
for f in self.parent.faces:
|
|
428
|
+
if isinstance(f.type, Wall) and \
|
|
429
|
+
isinstance(f.boundary_condition, Outdoors):
|
|
430
|
+
return 'SlabOnGrade'
|
|
431
|
+
return 'UndergroundSlab'
|
|
432
|
+
elif isinstance(self.boundary_condition, Outdoors):
|
|
433
|
+
return 'ExposedFloor'
|
|
434
|
+
else:
|
|
435
|
+
return 'InteriorFloor'
|
|
436
|
+
else:
|
|
437
|
+
if isinstance(self.boundary_condition, Outdoors):
|
|
438
|
+
return 'Roof'
|
|
439
|
+
elif isinstance(self.boundary_condition, Ground):
|
|
440
|
+
return 'UndergroundCeiling'
|
|
441
|
+
return 'Ceiling'
|
|
442
|
+
|
|
401
443
|
@property
|
|
402
444
|
def type_color(self):
|
|
403
445
|
"""Get a Color to be used in visualizations by type."""
|
|
@@ -425,16 +467,23 @@ class Face(_BaseWithShade):
|
|
|
425
467
|
return math.degrees(
|
|
426
468
|
north_vector.angle_clockwise(Vector2D(self.normal.x, self.normal.y)))
|
|
427
469
|
|
|
428
|
-
def cardinal_direction(self, north_vector=Vector2D(0, 1)):
|
|
470
|
+
def cardinal_direction(self, north_vector=Vector2D(0, 1), angle_tolerance=1):
|
|
429
471
|
"""Get text description for the cardinal direction that the face is pointing.
|
|
430
472
|
|
|
431
473
|
Will be one of the following: ('North', 'NorthEast', 'East', 'SouthEast',
|
|
432
|
-
'South', 'SouthWest', 'West', 'NorthWest').
|
|
474
|
+
'South', 'SouthWest', 'West', 'NorthWest', 'Up', 'Down').
|
|
433
475
|
|
|
434
476
|
Args:
|
|
435
477
|
north_vector: A ladybug_geometry Vector2D for the north direction.
|
|
436
478
|
Default is the Y-axis (0, 1).
|
|
479
|
+
angle_tolerance: The angle tolerance in degrees used to determine if
|
|
480
|
+
the Face is perfectly Up or Down. (Default: 1).
|
|
437
481
|
"""
|
|
482
|
+
tilt = self.tilt
|
|
483
|
+
if tilt < angle_tolerance:
|
|
484
|
+
return 'Up'
|
|
485
|
+
elif tilt > 180 - angle_tolerance:
|
|
486
|
+
return 'Down'
|
|
438
487
|
orient = self.horizontal_orientation(north_vector)
|
|
439
488
|
orient_text = ('North', 'NorthEast', 'East', 'SouthEast', 'South',
|
|
440
489
|
'SouthWest', 'West', 'NorthWest')
|
|
@@ -444,6 +493,31 @@ class Face(_BaseWithShade):
|
|
|
444
493
|
return orient_text[i]
|
|
445
494
|
return orient_text[0]
|
|
446
495
|
|
|
496
|
+
def cardinal_abbrev(self, north_vector=Vector2D(0, 1), angle_tolerance=1):
|
|
497
|
+
"""Get text abbreviation for the cardinal direction that the face is pointing.
|
|
498
|
+
|
|
499
|
+
Will be one of the following: ('N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW',
|
|
500
|
+
'Up', 'Down').
|
|
501
|
+
|
|
502
|
+
Args:
|
|
503
|
+
north_vector: A ladybug_geometry Vector2D for the north direction.
|
|
504
|
+
Default is the Y-axis (0, 1).
|
|
505
|
+
angle_tolerance: The angle tolerance in degrees used to determine if
|
|
506
|
+
the Face is perfectly Up or Down. (Default: 1).
|
|
507
|
+
"""
|
|
508
|
+
tilt = self.tilt
|
|
509
|
+
if tilt < angle_tolerance:
|
|
510
|
+
return 'Up'
|
|
511
|
+
elif tilt > 180 - angle_tolerance:
|
|
512
|
+
return 'Down'
|
|
513
|
+
orient = self.horizontal_orientation(north_vector)
|
|
514
|
+
orient_text = ('N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW')
|
|
515
|
+
angles = (22.5, 67.5, 112.5, 157.5, 202.5, 247.5, 292.5, 337.5)
|
|
516
|
+
for i, ang in enumerate(angles):
|
|
517
|
+
if orient < ang:
|
|
518
|
+
return orient_text[i]
|
|
519
|
+
return orient_text[0]
|
|
520
|
+
|
|
447
521
|
def add_prefix(self, prefix):
|
|
448
522
|
"""Change the identifier of this object and child objects by inserting a prefix.
|
|
449
523
|
|
|
@@ -471,6 +545,28 @@ class Face(_BaseWithShade):
|
|
|
471
545
|
in self._boundary_condition._boundary_condition_objects)
|
|
472
546
|
self._boundary_condition = Surface(new_bc_objs, False)
|
|
473
547
|
|
|
548
|
+
def rename_by_attribute(
|
|
549
|
+
self,
|
|
550
|
+
format_str='{parent.display_name} - {gbxml_type} - {cardinal_direction}'
|
|
551
|
+
):
|
|
552
|
+
"""Set the display name of this Face using a format string with Face attributes.
|
|
553
|
+
|
|
554
|
+
Args:
|
|
555
|
+
format_str: Text string for the pattern with which the Face will be
|
|
556
|
+
renamed. Any property on this class may be used (eg. gbxml_str)
|
|
557
|
+
and each property should be put in curly brackets. Nested
|
|
558
|
+
properties can be specified by using "." to denote nesting levels
|
|
559
|
+
(eg. properties.energy.construction.display_name). Functions that
|
|
560
|
+
return string outputs can also be passed here as long as these
|
|
561
|
+
functions defaults specified for all arguments.
|
|
562
|
+
"""
|
|
563
|
+
matches = re.findall(r'{([^}]*)}', format_str)
|
|
564
|
+
attributes = [get_attr_nested(self, m, decimal_count=2) for m in matches]
|
|
565
|
+
for attr_name, attr_val in zip(matches, attributes):
|
|
566
|
+
format_str = format_str.replace('{{{}}}'.format(attr_name), attr_val)
|
|
567
|
+
self.display_name = format_str
|
|
568
|
+
return format_str
|
|
569
|
+
|
|
474
570
|
def remove_sub_faces(self):
|
|
475
571
|
"""Remove all apertures and doors from the face."""
|
|
476
572
|
self.remove_apertures()
|
|
@@ -740,6 +836,8 @@ class Face(_BaseWithShade):
|
|
|
740
836
|
if len(joined_bounds) == 1: # can be represented with a single Face3D
|
|
741
837
|
verts3d = tuple(ref_plane.xy_to_xyz(_v) for _v in joined_bounds[0])
|
|
742
838
|
non_rect_geos = [Face3D(verts3d, plane=ref_plane)]
|
|
839
|
+
elif len(joined_bounds) == 0: # everything was invalid
|
|
840
|
+
non_rect_geos = []
|
|
743
841
|
else: # need to separate holes from distinct Face3Ds
|
|
744
842
|
bound_faces = []
|
|
745
843
|
for poly in joined_bounds:
|
|
@@ -1749,16 +1847,24 @@ class Face(_BaseWithShade):
|
|
|
1749
1847
|
at which point the vertex is considered colinear. Default: 0.01,
|
|
1750
1848
|
suitable for objects in meters.
|
|
1751
1849
|
"""
|
|
1850
|
+
# set up lists to track sub-faces to remove
|
|
1851
|
+
del_ap_i, del_dr_i = [], []
|
|
1852
|
+
# remove degenerate apertures
|
|
1752
1853
|
for i, ap in enumerate(self._apertures):
|
|
1753
1854
|
try:
|
|
1754
1855
|
ap.remove_colinear_vertices(tolerance)
|
|
1755
1856
|
except ValueError:
|
|
1756
|
-
|
|
1857
|
+
del_ap_i.append(i)
|
|
1858
|
+
for del_i in reversed(del_ap_i):
|
|
1859
|
+
self._apertures.pop(del_i)
|
|
1860
|
+
# remove degenerate doors
|
|
1757
1861
|
for i, dr in enumerate(self._doors):
|
|
1758
1862
|
try:
|
|
1759
1863
|
dr.remove_colinear_vertices(tolerance)
|
|
1760
1864
|
except ValueError:
|
|
1761
|
-
|
|
1865
|
+
del_dr_i.append(i)
|
|
1866
|
+
for del_i in reversed(del_dr_i):
|
|
1867
|
+
self._doors.pop(del_i)
|
|
1762
1868
|
|
|
1763
1869
|
def is_geo_equivalent(self, face, tolerance=0.01):
|
|
1764
1870
|
"""Get a boolean for whether this object is geometrically equivalent to another.
|