honeybee-core 1.64.0__py3-none-any.whl → 1.64.2__py3-none-any.whl

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/model.py CHANGED
@@ -13,7 +13,9 @@ try: # check if we are in IronPython
13
13
  except ImportError: # wea are in cPython
14
14
  import pickle
15
15
 
16
- from ladybug_geometry.geometry3d import Vector3D, Point3D, Plane, Face3D, Mesh3D
16
+ from ladybug_geometry.geometry2d import Polygon2D
17
+ from ladybug_geometry.geometry3d import Vector3D, Point3D, Plane, Face3D, \
18
+ Mesh3D, Polyface3D
17
19
  from ladybug_geometry.interop.stl import STL
18
20
 
19
21
  from ._base import _Base
@@ -1533,6 +1535,71 @@ class Model(_Base):
1533
1535
  return roof_to_exterior, slab_to_exterior, exposed_floor_to_exterior_wall, \
1534
1536
  exterior_wall_to_wall, roof_ridge, exposed_floor_to_floor, underground
1535
1537
 
1538
+ def classified_sub_face_edges(
1539
+ self, mullion_thickness=None, tolerance=None, angle_tolerance=None
1540
+ ):
1541
+ """Get classified edges around this Model's Apertures and Doors.
1542
+
1543
+ The edges returned by this method will only exist along the exterior
1544
+ sub-faces.
1545
+
1546
+ Args:
1547
+ mullion_thickness: The maximum difference that apertures can be from
1548
+ one another for the edges to be considered a mullion rather than
1549
+ a frame. If None, the Model's tolerance will be used.
1550
+ tolerance: The maximum difference between point values for them to be
1551
+ considered equivalent. If None, the Model's tolerance will be used.
1552
+ angle_tolerance: The max angle difference in degrees where sub-face
1553
+ normals are no longer considered coplanar. If None, the Model
1554
+ angle_tolerance will be used. (Default: None).
1555
+
1556
+ Returns:
1557
+ A tuple with three items where each item is a list containing
1558
+ LineSegment3D surrounding different sub-face conditions.
1559
+
1560
+ - window_frames - Apertures meet their parent exterior wall or roof.
1561
+
1562
+ - window_mullions - Apertures meet one another.
1563
+
1564
+ - door_frames - Doors meet their parent exterior wall or roof.
1565
+ """
1566
+ # set up lists to be populated
1567
+ window_frames, window_mullions = [], []
1568
+ tol = tolerance if tolerance is not None else self.tolerance
1569
+ a_tol = math.radians(angle_tolerance) if angle_tolerance is not None else \
1570
+ math.radians(self.angle_tolerance)
1571
+ mul_thick = tol if mullion_thickness is None else mullion_thickness
1572
+
1573
+ # group the apertures by the plane in which they exist
1574
+ apertures = self.apertures
1575
+ coplanar_dict = {apertures[0].geometry.plane: [apertures[0]]}
1576
+ for ap in apertures[1:]:
1577
+ if isinstance(ap.boundary_condition, Outdoors):
1578
+ for pln, f_list in coplanar_dict.items():
1579
+ if ap.geometry.plane.is_coplanar_tolerance(pln, tol, a_tol):
1580
+ f_list.append(ap)
1581
+ break
1582
+ else: # the first face with this type of plane
1583
+ coplanar_dict[ap.geometry.plane] = [ap]
1584
+
1585
+ # for each group, intersect their edges and extract edges from a Polyface3D
1586
+ for plane, aps in coplanar_dict.items():
1587
+ # intersect edges that are close enough to one another within thickness
1588
+ polygons = []
1589
+ for ap in aps:
1590
+ pts_2d = [plane.xyz_to_xy(pt) for pt in ap.geometry.boundary]
1591
+ polygons.append(Polygon2D(pts_2d))
1592
+ polygons = Polygon2D.intersect_polygon_segments(polygons, mul_thick)
1593
+ faces = []
1594
+ for poly in polygons:
1595
+ faces.append(Face3D([plane.xy_to_xyz(pt) for pt in poly]))
1596
+ # create a joined Polyface3D and classify the edges
1597
+ ap_polyface = Polyface3D.from_faces(faces, mul_thick)
1598
+ window_frames.extend(ap_polyface.naked_edges)
1599
+ window_mullions.extend(ap_polyface.internal_edges)
1600
+
1601
+ return window_frames, window_mullions, self.exterior_door_edges
1602
+
1536
1603
  def add_prefix(self, prefix):
1537
1604
  """Change the identifier of this object and child objects by inserting a prefix.
1538
1605
 
@@ -1559,6 +1626,25 @@ class Model(_Base):
1559
1626
  for shade_mesh in self._shade_meshes:
1560
1627
  shade_mesh.add_prefix(prefix)
1561
1628
 
1629
+ def reset_room_ids(self):
1630
+ """Reset the identifiers of the Model Rooms to be derived from display_names.
1631
+
1632
+ In the event that duplicate Room identifiers are found, an integer will
1633
+ be automatically appended to the new Room ID to make it unique.
1634
+
1635
+ Returns:
1636
+ A dictionary that relates the old identifiers (keys) to the new
1637
+ identifiers (values). This can be used to map between old and new
1638
+ objects and update things like Surface boundary conditions.
1639
+ """
1640
+ room_dict, room_map = {}, {}
1641
+ for room in self.rooms:
1642
+ new_id = clean_and_number_string(
1643
+ room.display_name, room_dict, 'Room identifier')
1644
+ room_map[room.identifier] = new_id
1645
+ room.identifier = new_id
1646
+ return room_map
1647
+
1562
1648
  def reset_ids(self, repair_surface_bcs=True):
1563
1649
  """Reset the identifiers of all Model objects to be derived from display_names.
1564
1650
 
@@ -1613,39 +1699,7 @@ class Model(_Base):
1613
1699
  shade_mesh.display_name, sm_dict, 'ShadeMesh identifier')
1614
1700
  # reset all of the Surface boundary conditions if requested
1615
1701
  if repair_surface_bcs:
1616
- for room in self.rooms:
1617
- for face in room.faces:
1618
- if isinstance(face.boundary_condition, Surface):
1619
- old_objs = face.boundary_condition.boundary_condition_objects
1620
- try:
1621
- new_objs = (face_map[old_objs[0]], room_map[old_objs[1]])
1622
- except KeyError: # missing adjacency
1623
- try: # see if maybe the room reference is still there
1624
- new_objs = (old_objs[0], room_map[old_objs[1]])
1625
- except KeyError: # just let the invalid adjacency pass
1626
- continue
1627
- new_bc = Surface(new_objs)
1628
- face.boundary_condition = new_bc
1629
- for ap in face.apertures:
1630
- old_objs = ap.boundary_condition.boundary_condition_objects
1631
- try:
1632
- new_objs = (ap_map[old_objs[0]], face_map[old_objs[1]],
1633
- room_map[old_objs[2]])
1634
- except KeyError: # missing adjacency
1635
- new_objs = (old_objs[0], old_objs[1],
1636
- room_map[old_objs[2]])
1637
- new_bc = Surface(new_objs, True)
1638
- ap.boundary_condition = new_bc
1639
- for dr in face.doors:
1640
- old_objs = dr.boundary_condition.boundary_condition_objects
1641
- try:
1642
- new_objs = (dr_map[old_objs[0]], face_map[old_objs[1]],
1643
- room_map[old_objs[2]])
1644
- except KeyError: # missing adjacency
1645
- new_objs = (old_objs[0], old_objs[1],
1646
- room_map[old_objs[2]])
1647
- new_bc = Surface(new_objs, True)
1648
- dr.boundary_condition = new_bc
1702
+ self._repair_surface_bcs(room_map, face_map, ap_map, dr_map)
1649
1703
  # return a dictionary that maps between old and new IDs
1650
1704
  return {
1651
1705
  'rooms': room_map,
@@ -1654,24 +1708,90 @@ class Model(_Base):
1654
1708
  'doors': dr_map
1655
1709
  }
1656
1710
 
1657
- def reset_room_ids(self):
1658
- """Reset the identifiers of the Model Rooms to be derived from display_names.
1711
+ def reset_ids_to_integers(self, start_integer=0, repair_surface_bcs=True):
1712
+ """Reset the identifiers of all Model geometry objects to be a unique integer.
1659
1713
 
1660
- In the event that duplicate Room identifiers are found, an integer will
1661
- be automatically appended to the new Room ID to make it unique.
1714
+ Integers are simply incremented from the start_integer, assigning integers
1715
+ first to Rooms, then to Faces, then to Apertures/Doors and lastly to
1716
+ Shades/ShadeMeshes.
1717
+
1718
+ Args:
1719
+ start_integer: The starting integer that will be used to set a lower
1720
+ limit on the integers assigned to the geometry elements.
1721
+ repair_surface_bcs: A Boolean to note whether all Surface boundary
1722
+ conditions across the model should be updated with the new
1723
+ identifiers that were generated from the display names. (Default: True).
1662
1724
 
1663
1725
  Returns:
1664
- A dictionary that relates the old identifiers (keys) to the new
1665
- identifiers (values). This can be used to map between old and new
1666
- objects and update things like Surface boundary conditions.
1726
+ An integer for the last value assigned to the model geometry objects.
1727
+ This can be used to ensure that any future IDs assigned after running
1728
+ this method do not have IDs that collide with the model objects.
1667
1729
  """
1668
- room_dict, room_map = {}, {}
1730
+ # set up dictionaries to hold various pieces of information
1731
+ room_map, face_map, ap_map, dr_map = {}, {}, {}, {}
1732
+ # loop through the objects and change their identifiers
1669
1733
  for room in self.rooms:
1670
- new_id = clean_and_number_string(
1671
- room.display_name, room_dict, 'Room identifier')
1672
- room_map[room.identifier] = new_id
1673
- room.identifier = new_id
1674
- return room_map
1734
+ room_map[room.identifier] = str(start_integer)
1735
+ room.identifier = str(start_integer)
1736
+ start_integer += 1
1737
+ for face in self.faces:
1738
+ face_map[face.identifier] = str(start_integer)
1739
+ face.identifier = str(start_integer)
1740
+ start_integer += 1
1741
+ for ap in self.apertures:
1742
+ ap_map[ap.identifier] = str(start_integer)
1743
+ ap.identifier = str(start_integer)
1744
+ start_integer += 1
1745
+ for dr in self.doors:
1746
+ dr_map[dr.identifier] = str(start_integer)
1747
+ dr.identifier = str(start_integer)
1748
+ start_integer += 1
1749
+ for shade in self.shades:
1750
+ shade.identifier = str(start_integer)
1751
+ start_integer += 1
1752
+ for shade_mesh in self.shade_meshes:
1753
+ shade_mesh.identifier = str(start_integer)
1754
+ start_integer += 1
1755
+ # reset all of the Surface boundary conditions if requested
1756
+ if repair_surface_bcs:
1757
+ self._repair_surface_bcs(room_map, face_map, ap_map, dr_map)
1758
+ return start_integer
1759
+
1760
+ def _repair_surface_bcs(self, room_map, face_map, ap_map, dr_map):
1761
+ """Repair Surface boundary conditions across the model using dict maps."""
1762
+ for room in self.rooms:
1763
+ for face in room.faces:
1764
+ if isinstance(face.boundary_condition, Surface):
1765
+ old_objs = face.boundary_condition.boundary_condition_objects
1766
+ try:
1767
+ new_objs = (face_map[old_objs[0]], room_map[old_objs[1]])
1768
+ except KeyError: # missing adjacency
1769
+ try: # see if maybe the room reference is still there
1770
+ new_objs = (old_objs[0], room_map[old_objs[1]])
1771
+ except KeyError: # just let the invalid adjacency pass
1772
+ continue
1773
+ new_bc = Surface(new_objs)
1774
+ face.boundary_condition = new_bc
1775
+ for ap in face.apertures:
1776
+ old_objs = ap.boundary_condition.boundary_condition_objects
1777
+ try:
1778
+ new_objs = (ap_map[old_objs[0]], face_map[old_objs[1]],
1779
+ room_map[old_objs[2]])
1780
+ except KeyError: # missing adjacency
1781
+ new_objs = (old_objs[0], old_objs[1],
1782
+ room_map[old_objs[2]])
1783
+ new_bc = Surface(new_objs, True)
1784
+ ap.boundary_condition = new_bc
1785
+ for dr in face.doors:
1786
+ old_objs = dr.boundary_condition.boundary_condition_objects
1787
+ try:
1788
+ new_objs = (dr_map[old_objs[0]], face_map[old_objs[1]],
1789
+ room_map[old_objs[2]])
1790
+ except KeyError: # missing adjacency
1791
+ new_objs = (old_objs[0], old_objs[1],
1792
+ room_map[old_objs[2]])
1793
+ new_bc = Surface(new_objs, True)
1794
+ dr.boundary_condition = new_bc
1675
1795
 
1676
1796
  def solve_adjacency(
1677
1797
  self, merge_coplanar=False, intersect=False, overwrite=False,
@@ -2181,6 +2301,18 @@ class Model(_Base):
2181
2301
  extrusion_rooms.append(room.to_extrusion(tol, a_tol))
2182
2302
  self._rooms = extrusion_rooms
2183
2303
 
2304
+ def shade_meshes_to_shades(self):
2305
+ """Convert all ShadeMesh objects on the Model to planar Shades."""
2306
+ new_shades = []
2307
+ for shade_mesh in self.shade_meshes:
2308
+ try:
2309
+ shade_mesh.triangulate_and_remove_degenerate_faces(self.tolerance)
2310
+ new_shades.extend(shade_mesh.to_shades())
2311
+ except AssertionError:
2312
+ pass # completely degenerate ShadeMesh to ignore
2313
+ self._orphaned_shades.extend(new_shades)
2314
+ self._shade_meshes = []
2315
+
2184
2316
  def convert_to_units(self, units='Meters'):
2185
2317
  """Convert all of the geometry in this model to certain units.
2186
2318
 
honeybee/shademesh.py CHANGED
@@ -324,6 +324,23 @@ class ShadeMesh(_Base):
324
324
  base['user_data'] = self.user_data
325
325
  return base
326
326
 
327
+ def to_shades(self):
328
+ """Return a list of Honeybee Shade objects derived from this ShadeMesh.
329
+
330
+ Note that the resulting Shades may be degenerate or non-planar so it
331
+ may be useful to call triangulate_and_remove_degenerate_faces on this
332
+ object before converting to Shades.
333
+ """
334
+ from honeybee.shade import Shade # imported here to avoid circular import
335
+ shades = []
336
+ for i, shade_geo in enumerate(self.geometry.face_vertices):
337
+ shade_id = '{}_{}'.format(self.identifier, i)
338
+ shade = Shade(shade_id, Face3D(shade_geo), self.is_detached)
339
+ if self._display_name is not None:
340
+ shade.display_name = '{} {}'.format(self.display_name, i)
341
+ shades.append(shade)
342
+ return shades
343
+
327
344
  @staticmethod
328
345
  def _display_mesh(mesh3d, color):
329
346
  """Create a DisplayMesh3D dictionary from a Mesh3D and color."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: honeybee-core
3
- Version: 1.64.0
3
+ Version: 1.64.2
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
@@ -16,13 +16,13 @@ honeybee/extensionutil.py,sha256=DDQYhM7tFD3avRSCOjwTzLqX9ldUxl5GzY79V3_sATE,964
16
16
  honeybee/face.py,sha256=BOL2tlFLErxY27euixVWFzL1z-xtABtzvMmLIpzMqcE,111961
17
17
  honeybee/facetype.py,sha256=vCtWZKHp21RH-Yzs8zsHJHuFhJvczNh0yFl8wDe_RWY,4489
18
18
  honeybee/logutil.py,sha256=2gn-6RcWqFLvwdFzBHPqUwFqTj_R3iwHKALrl-2eL7M,2564
19
- honeybee/model.py,sha256=RgD5JsxESt1JjmFanJZ4Vu5gQAHTXLMmtP5R0M9vL2Q,195385
19
+ honeybee/model.py,sha256=hlZ8HJpq4YTlLFII5-VlcohKKzFpNl88i0DXj-y6zSA,201639
20
20
  honeybee/orientation.py,sha256=GogGblASW9OU-fobfDaQ7w5yRbEAFdJJuHwg2fadhKI,5046
21
21
  honeybee/properties.py,sha256=ok976fbUdXzLDR2XiPNf8Q5ejfOM-PArqm5CVUbFiCc,34316
22
22
  honeybee/room.py,sha256=ZrjABLErBTdTHcDRvPdt4W8-0REyPjQLtnu0rnrML0U,168284
23
23
  honeybee/search.py,sha256=KOIeQjYdj0yhRWPmF5kiFiH8Ei0WbzuiU-capnwYVeM,5060
24
24
  honeybee/shade.py,sha256=2zuW1lR5wfe-UyAosuCX-3tII6CKONq7wt8Ac_jXU6c,20698
25
- honeybee/shademesh.py,sha256=oldugnwhu-ibX9f0hfxpO-DvgM8U7S-dYwUjBSVzo4g,13273
25
+ honeybee/shademesh.py,sha256=WnpYkt04zuB8w-6OETNP7ZfzJarykEkQig6YdNkuVOw,14069
26
26
  honeybee/typing.py,sha256=E8-HrCB9cSoqhFR6zcFXhrAlQRAFw_sxHGdobB8Lre8,20051
27
27
  honeybee/units.py,sha256=_qG_G5b9hdqjpyVOpGdIYCB6k8VKYjcxSJn1St-7Xjc,3204
28
28
  honeybee/cli/__init__.py,sha256=nYyTV_HapGo-a1XZLZps9__Bqp50YJYzHZ1LzHp4TJU,3675
@@ -40,9 +40,9 @@ honeybee/writer/model.py,sha256=N7F_jksf-5TrdVecuxTaFWxnPVFLmQs7k8g27TsdB7Q,177
40
40
  honeybee/writer/room.py,sha256=kFghgStTU1SEJSLigXB0VjOWhZtgs4uXuAqdwd4yRQo,174
41
41
  honeybee/writer/shade.py,sha256=EpgX-vMc-s21TnMvNWvWTKyT8iAnxu1nFVXzjY1oyF8,177
42
42
  honeybee/writer/shademesh.py,sha256=Y41bLogJ7dwpvMe5cAWVRDRVqJEwo9e5hFJQjlt6UX8,189
43
- honeybee_core-1.64.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
44
- honeybee_core-1.64.0.dist-info/METADATA,sha256=RlL15eO292imJELxLIkC81hR94wrn3_qeGxIxqEGGiM,3729
45
- honeybee_core-1.64.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
46
- honeybee_core-1.64.0.dist-info/entry_points.txt,sha256=r3YqOm40goBroH3ccUhpwQjvTwu10JWLd0HIRHI1J8E,47
47
- honeybee_core-1.64.0.dist-info/top_level.txt,sha256=8ve7puCRLUA9XDEGc1Mcs-UX9sFjpPV8MeTaIMwQ_Tg,9
48
- honeybee_core-1.64.0.dist-info/RECORD,,
43
+ honeybee_core-1.64.2.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
44
+ honeybee_core-1.64.2.dist-info/METADATA,sha256=uik9BH2MG0is9st_FBqDrbwW4M_Ljf4YNTi7kAAlbTw,3729
45
+ honeybee_core-1.64.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
46
+ honeybee_core-1.64.2.dist-info/entry_points.txt,sha256=r3YqOm40goBroH3ccUhpwQjvTwu10JWLd0HIRHI1J8E,47
47
+ honeybee_core-1.64.2.dist-info/top_level.txt,sha256=8ve7puCRLUA9XDEGc1Mcs-UX9sFjpPV8MeTaIMwQ_Tg,9
48
+ honeybee_core-1.64.2.dist-info/RECORD,,