topologicpy 0.8.52__py3-none-any.whl → 0.8.55__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.
- topologicpy/Cell.py +1 -3
- topologicpy/EnergyModel.py +111 -10
- topologicpy/Graph.py +285 -1
- topologicpy/Helper.py +57 -43
- topologicpy/Honeybee.py +527 -2
- topologicpy/Plotly.py +4 -2
- topologicpy/Topology.py +17 -0
- topologicpy/version.py +1 -1
- {topologicpy-0.8.52.dist-info → topologicpy-0.8.55.dist-info}/METADATA +1 -1
- {topologicpy-0.8.52.dist-info → topologicpy-0.8.55.dist-info}/RECORD +13 -13
- {topologicpy-0.8.52.dist-info → topologicpy-0.8.55.dist-info}/WHEEL +0 -0
- {topologicpy-0.8.52.dist-info → topologicpy-0.8.55.dist-info}/licenses/LICENSE +0 -0
- {topologicpy-0.8.52.dist-info → topologicpy-0.8.55.dist-info}/top_level.txt +0 -0
topologicpy/Cell.py
CHANGED
@@ -1552,7 +1552,6 @@ class Cell():
|
|
1552
1552
|
cluster = Cluster.ByTopologies(pentagons)
|
1553
1553
|
|
1554
1554
|
cluster2 = Topology.Rotate(cluster, origin=Vertex.Origin(), axis=[1, 0, 0], angle=180)
|
1555
|
-
#cluster2 = Topology.Rotate(cluster2, origin=Vertex.Origin(), axis=[0, 0, 1], angle=36)
|
1556
1555
|
vertices = Topology.Vertices(cluster)
|
1557
1556
|
zList = [Vertex.Z(v) for v in vertices]
|
1558
1557
|
zList = list(set(zList))
|
@@ -1566,9 +1565,8 @@ class Cell():
|
|
1566
1565
|
dodecahedron = Topology.Translate(dodecahedron, -Vertex.X(centroid), -Vertex.Y(centroid), -Vertex.Z(centroid))
|
1567
1566
|
vertices = Topology.Vertices(dodecahedron)
|
1568
1567
|
d = Vertex.Distance(Vertex.Origin(), vertices[0])
|
1568
|
+
# Make sure the distance from the origin to the vertices is equal to the radius.
|
1569
1569
|
dodecahedron = Topology.Scale(dodecahedron, origin=Vertex.Origin(), x=radius/d, y=radius/d, z=radius/d)
|
1570
|
-
verts = Topology.Vertices(dodecahedron)
|
1571
|
-
print("Dodec: Distance", Vertex.Distance(Vertex.Origin(), verts[0]))
|
1572
1570
|
if placement == "bottom":
|
1573
1571
|
dodecahedron = Topology.Translate(dodecahedron, 0, 0, radius)
|
1574
1572
|
elif placement == "lowerleft":
|
topologicpy/EnergyModel.py
CHANGED
@@ -201,6 +201,51 @@ class EnergyModel:
|
|
201
201
|
warnings.warn("EnergyModel.ByTopology - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
|
202
202
|
return None
|
203
203
|
|
204
|
+
|
205
|
+
def load_openstudio_model(osm_path: str, allow_newer: bool = True):
|
206
|
+
"""
|
207
|
+
Load an OpenStudio .osm file and return an openstudio.model.Model.
|
208
|
+
Uses loadModelFromString to avoid SWIG path overload issues.
|
209
|
+
"""
|
210
|
+
import os
|
211
|
+
|
212
|
+
if not os.path.exists(osm_path):
|
213
|
+
raise FileNotFoundError(osm_path)
|
214
|
+
|
215
|
+
vt = openstudio.osversion.VersionTranslator()
|
216
|
+
if allow_newer:
|
217
|
+
vt.setAllowNewerVersions(True)
|
218
|
+
|
219
|
+
# 1) Robust path-agnostic route: read text and load from string
|
220
|
+
with open(osm_path, "r", encoding="utf-8") as f:
|
221
|
+
txt = f.read()
|
222
|
+
model_opt = vt.loadModelFromString(txt) # <- avoids path type mismatches
|
223
|
+
|
224
|
+
# 2) Fallback: try the filesystem-path overloads if needed
|
225
|
+
if (not model_opt) or (not model_opt.is_initialized()):
|
226
|
+
os_path = None
|
227
|
+
for maker in (
|
228
|
+
lambda s: getattr(openstudio, "path")(s),
|
229
|
+
lambda s: getattr(openstudio, "toPath")(s),
|
230
|
+
lambda s: getattr(openstudio.openstudioutilitiescore, "toPath")(s),
|
231
|
+
):
|
232
|
+
try:
|
233
|
+
os_path = maker(osm_path)
|
234
|
+
break
|
235
|
+
except Exception:
|
236
|
+
pass
|
237
|
+
if os_path is not None:
|
238
|
+
try:
|
239
|
+
model_opt = vt.loadModel(os_path)
|
240
|
+
except TypeError:
|
241
|
+
model_opt = openstudio.model.Model.load(os_path)
|
242
|
+
|
243
|
+
if (not model_opt) or (not model_opt.is_initialized()):
|
244
|
+
raise RuntimeError(f"Failed to load OpenStudio model from: {osm_path}")
|
245
|
+
|
246
|
+
model = model_opt.get()
|
247
|
+
return model
|
248
|
+
|
204
249
|
def getKeyName(d, keyName):
|
205
250
|
keys = d.Keys()
|
206
251
|
for key in keys:
|
@@ -243,21 +288,23 @@ class EnergyModel:
|
|
243
288
|
|
244
289
|
if not osModelPath:
|
245
290
|
import os
|
246
|
-
osModelPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "OSMTemplate-OfficeBuilding-3.
|
291
|
+
osModelPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "OSMTemplate-OfficeBuilding-3.10.0.osm")
|
247
292
|
if not weatherFilePath or not designDayFilePath:
|
248
293
|
import os
|
249
294
|
weatherFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.epw")
|
250
295
|
designDayFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.ddy")
|
251
|
-
|
296
|
+
|
297
|
+
#translator = openstudio.osversion.VersionTranslator()
|
252
298
|
# DEBUGGING
|
253
299
|
#osmFile = openstudio.openstudioutilitiescore.toPath(osModelPath)
|
254
300
|
#osModel = translator.loadModel(osmFile)
|
255
|
-
osModel = translator.loadModel(osModelPath)
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
301
|
+
#osModel = translator.loadModel(osModelPath)
|
302
|
+
osModel = load_openstudio_model(osModelPath, allow_newer=True)
|
303
|
+
# if osModel.isNull():
|
304
|
+
# print("EnergyModel.ByTopology - Error: The openstudio model is null. Returning None.")
|
305
|
+
# return None
|
306
|
+
# else:
|
307
|
+
# osModel = osModel.get()
|
261
308
|
# DEBUGGING
|
262
309
|
#osEPWFile = openstudio.openstudioutilitiesfiletypes.EpwFile.load(openstudio.toPath(weatherFilePath))
|
263
310
|
osEPWFile = openstudio.openstudioutilitiesfiletypes.EpwFile.load(weatherFilePath)
|
@@ -265,8 +312,8 @@ class EnergyModel:
|
|
265
312
|
osEPWFile = osEPWFile.get()
|
266
313
|
openstudio.model.WeatherFile.setWeatherFile(osModel, osEPWFile)
|
267
314
|
# DEBUGGING
|
268
|
-
|
269
|
-
ddyModel = openstudio.openstudioenergyplus.loadAndTranslateIdf(designDayFilePath)
|
315
|
+
ddyModel = openstudio.openstudioenergyplus.loadAndTranslateIdf(openstudio.toPath(designDayFilePath))
|
316
|
+
#ddyModel = openstudio.openstudioenergyplus.loadAndTranslateIdf(designDayFilePath)
|
270
317
|
if ddyModel.is_initialized():
|
271
318
|
ddyModel = ddyModel.get()
|
272
319
|
for ddy in ddyModel.getObjectsByType(openstudio.IddObjectType("OS:SizingPeriod:DesignDay")):
|
@@ -1195,3 +1242,57 @@ class EnergyModel:
|
|
1195
1242
|
return None
|
1196
1243
|
return units
|
1197
1244
|
|
1245
|
+
@staticmethod
|
1246
|
+
def Version(check: bool = True, silent: bool = False):
|
1247
|
+
"""
|
1248
|
+
Returns the OpenStudio SDK version number.
|
1249
|
+
|
1250
|
+
Parameters
|
1251
|
+
----------
|
1252
|
+
check : bool , optional
|
1253
|
+
if set to True, the version number is checked with the latest version on PyPi. Default is True.
|
1254
|
+
|
1255
|
+
silent : bool , optional
|
1256
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
1257
|
+
|
1258
|
+
Returns
|
1259
|
+
-------
|
1260
|
+
str
|
1261
|
+
The OpenStudio SDK version number.
|
1262
|
+
|
1263
|
+
"""
|
1264
|
+
from topologicpy.Helper import Helper
|
1265
|
+
try:
|
1266
|
+
import openstudio
|
1267
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
1268
|
+
except:
|
1269
|
+
if not silent:
|
1270
|
+
print("EnergyModel.Version - Information: Installing required openstudio library.")
|
1271
|
+
try:
|
1272
|
+
os.system("pip install openstudio")
|
1273
|
+
except:
|
1274
|
+
os.system("pip install openstudio --user")
|
1275
|
+
try:
|
1276
|
+
import openstudio
|
1277
|
+
openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
|
1278
|
+
if not silent:
|
1279
|
+
print("EnergyModel.Version - Information: openstudio library installed correctly.")
|
1280
|
+
except:
|
1281
|
+
if not silent:
|
1282
|
+
print("EnergyModel.Version - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
|
1283
|
+
return None
|
1284
|
+
import requests
|
1285
|
+
from packaging import version
|
1286
|
+
|
1287
|
+
result = getattr(openstudio, "openStudioVersion", None)
|
1288
|
+
if callable(result):
|
1289
|
+
result = result()
|
1290
|
+
else:
|
1291
|
+
if not silent:
|
1292
|
+
print("EnergyModel.Version - Error: Could not retrieve the openstudio SDK version number. Returning None.")
|
1293
|
+
return None
|
1294
|
+
if check == True:
|
1295
|
+
result = Helper.CheckVersion("openstudio", result, silent=silent)
|
1296
|
+
return result
|
1297
|
+
|
1298
|
+
|
topologicpy/Graph.py
CHANGED
@@ -784,7 +784,291 @@ class Graph:
|
|
784
784
|
return csv_buffer.getvalue()
|
785
785
|
except Exception as e:
|
786
786
|
return ""
|
787
|
-
|
787
|
+
|
788
|
+
@staticmethod
|
789
|
+
def AdjacencyMatrixFigure(graph,
|
790
|
+
vertexKey: str = None,
|
791
|
+
showZero: bool = False,
|
792
|
+
zeroChar: str = "·",
|
793
|
+
zeroColor: str = 'rgba(0,0,0,0)',
|
794
|
+
valueColor: str = 'rgba(0,0,0,0.05)',
|
795
|
+
diagonalHighlight: bool = True,
|
796
|
+
diagonalColor: str = 'rgba(0,0,0,0)',
|
797
|
+
title: str = None,
|
798
|
+
cellSize: int = 24,
|
799
|
+
fontFamily: str = "Arial",
|
800
|
+
fontSize: int = 12,
|
801
|
+
fontColor: str = 'rgba(0,0,0,0)',
|
802
|
+
backgroundColor: str = 'rgba(0,0,0,0)',
|
803
|
+
headerColor: str = 'rgba(0,0,0,0)',
|
804
|
+
reverse=False,
|
805
|
+
edgeKeyFwd=None,
|
806
|
+
edgeKeyBwd=None,
|
807
|
+
bidirKey=None,
|
808
|
+
bidirectional=True,
|
809
|
+
useEdgeIndex=False,
|
810
|
+
useEdgeLength=False,
|
811
|
+
mantissa: int = 6,
|
812
|
+
tolerance=0.0001,
|
813
|
+
silent: bool = False):
|
814
|
+
"""
|
815
|
+
Returns a Plotly table figure visualizing the adjacency matrix of a Graph.
|
816
|
+
|
817
|
+
Parameters
|
818
|
+
----------
|
819
|
+
graph : topologic_core.Graph
|
820
|
+
The input graph.
|
821
|
+
vertexKey : str , optional
|
822
|
+
If set, the returned list of vertices is sorted according to the dictionary values stored under this key. Default is None.
|
823
|
+
showZero : bool, optional
|
824
|
+
If True, show zeros as "0"; if False, show a subtle glyph (zero_char) or blank. Default is False.
|
825
|
+
zeroChar : str, optional
|
826
|
+
Character to display for zero entries when show_zero is False. Default is "·".
|
827
|
+
zeroColor : list or str , optional
|
828
|
+
The desired color to display for zero-valued cells. This can be any color list or plotly color string and may be specified as:
|
829
|
+
- An rgb list (e.g. [255,0,0])
|
830
|
+
- A cmyk list (e.g. [0.5, 0, 0.25, 0.2])
|
831
|
+
- A hex string (e.g. '#ff0000')
|
832
|
+
- An rgb/rgba string (e.g. 'rgb(255,0,0)')
|
833
|
+
- An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
|
834
|
+
- An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
|
835
|
+
- A named CSS color.
|
836
|
+
The default is 'rgba(0,0,0,0)' (transparent).
|
837
|
+
valueColor : list or str , optional
|
838
|
+
The desired color to display for non-zero-valued cells. This can be any color list or plotly color string and may be specified as:
|
839
|
+
- An rgb list (e.g. [255,0,0])
|
840
|
+
- A cmyk list (e.g. [0.5, 0, 0.25, 0.2])
|
841
|
+
- A hex string (e.g. '#ff0000')
|
842
|
+
- An rgb/rgba string (e.g. 'rgb(255,0,0)')
|
843
|
+
- An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
|
844
|
+
- An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
|
845
|
+
- A named CSS color.
|
846
|
+
The default is 'rgba(0,0,0,0.35)' (slight highlight).
|
847
|
+
diagonalHighlight : bool, optional
|
848
|
+
If True, lightly highlight diagonal cells. Default is True.
|
849
|
+
diagonalColor : list or str , optional
|
850
|
+
The desired diagonal highlight color. This can be any color list or plotly color string and may be specified as:
|
851
|
+
- An rgb list (e.g. [255,0,0])
|
852
|
+
- A cmyk list (e.g. [0.5, 0, 0.25, 0.2])
|
853
|
+
- A hex string (e.g. '#ff0000')
|
854
|
+
- An rgb/rgba string (e.g. 'rgb(255,0,0)')
|
855
|
+
- An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
|
856
|
+
- An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
|
857
|
+
- A named CSS color.
|
858
|
+
The default is 'rgba(0,0,0,0)' (transparent).
|
859
|
+
title : str, optional
|
860
|
+
Optional figure title.
|
861
|
+
cellSize : int, optional
|
862
|
+
Approximate pixel height of each table row. Default is 24.
|
863
|
+
fontFamily : str, optional
|
864
|
+
Font family for table text. Default is "Arial".
|
865
|
+
fontSize : int, optional
|
866
|
+
Font size for table text. Default is 12.
|
867
|
+
fontColor : list or str , optional
|
868
|
+
The desired font color. This can be any color list or plotly color string and may be specified as:
|
869
|
+
- An rgb list (e.g. [255,0,0])
|
870
|
+
- A cmyk list (e.g. [0.5, 0, 0.25, 0.2])
|
871
|
+
- A hex string (e.g. '#ff0000')
|
872
|
+
- An rgb/rgba string (e.g. 'rgb(255,0,0)')
|
873
|
+
- An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
|
874
|
+
- An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
|
875
|
+
- A named CSS color.
|
876
|
+
The default is 'rgba(0,0,0,0)' (transparent).
|
877
|
+
backgroundColor : list or str , optional
|
878
|
+
The desired background color. This can be any color list or plotly color string and may be specified as:
|
879
|
+
- An rgb list (e.g. [255,0,0])
|
880
|
+
- A cmyk list (e.g. [0.5, 0, 0.25, 0.2])
|
881
|
+
- A hex string (e.g. '#ff0000')
|
882
|
+
- An rgb/rgba string (e.g. 'rgb(255,0,0)')
|
883
|
+
- An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
|
884
|
+
- An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
|
885
|
+
- A named CSS color.
|
886
|
+
The default is 'rgba(0,0,0,0)' (transparent).
|
887
|
+
headerColor : list or str , optional
|
888
|
+
The desired header color. This can be any color list or plotly color string and may be specified as:
|
889
|
+
- An rgb list (e.g. [255,0,0])
|
890
|
+
- A cmyk list (e.g. [0.5, 0, 0.25, 0.2])
|
891
|
+
- A hex string (e.g. '#ff0000')
|
892
|
+
- An rgb/rgba string (e.g. 'rgb(255,0,0)')
|
893
|
+
- An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
|
894
|
+
- An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
|
895
|
+
- A named CSS color.
|
896
|
+
The default is 'rgba(0,0,0,0)' (transparent).
|
897
|
+
reverse : bool , optional
|
898
|
+
If set to True, the vertices are sorted in reverse order (only if vertexKey is set). Otherwise, they are not. Default is False.
|
899
|
+
edgeKeyFwd : str , optional
|
900
|
+
If set, the value at this key in the connecting edge from start vertex to end vertex (forward) will be used instead of the value 1. Default is None. useEdgeIndex and useEdgeLength override this setting.
|
901
|
+
edgeKeyBwd : str , optional
|
902
|
+
If set, the value at this key in the connecting edge from end vertex to start vertex (backward) will be used instead of the value 1. Default is None. useEdgeIndex and useEdgeLength override this setting.
|
903
|
+
bidirKey : bool , optional
|
904
|
+
If set to True or False, this key in the connecting edge will be used to determine is the edge is supposed to be bidirectional or not. If set to None, the input variable bidrectional will be used instead. Default is None
|
905
|
+
bidirectional : bool , optional
|
906
|
+
If set to True, the edges in the graph that do not have a bidireKey in their dictionaries will be treated as being bidirectional. Otherwise, the start vertex and end vertex of the connecting edge will determine the direction. Default is True.
|
907
|
+
useEdgeIndex : bool , optional
|
908
|
+
If set to True, the adjacency matrix values will the index of the edge in Graph.Edges(graph). Default is False. Both useEdgeIndex, useEdgeLength should not be True at the same time. If they are, useEdgeLength will be used.
|
909
|
+
useEdgeLength : bool , optional
|
910
|
+
If set to True, the adjacency matrix values will the length of the edge in Graph.Edges(graph). Default is False. Both useEdgeIndex, useEdgeLength should not be True at the same time. If they are, useEdgeLength will be used.
|
911
|
+
mantissa : int , optional
|
912
|
+
The number of decimal places to round the result to. Default is 6.
|
913
|
+
tolerance : float , optional
|
914
|
+
The desired tolerance. Default is 0.0001.
|
915
|
+
silent : bool, optional
|
916
|
+
If True, suppresses warning messages. Default is False.
|
917
|
+
|
918
|
+
Returns
|
919
|
+
-------
|
920
|
+
plotly.graph_objs._figure.Figure
|
921
|
+
A Plotly table figure containing the adjacency matrix table.
|
922
|
+
"""
|
923
|
+
|
924
|
+
from topologicpy.Topology import Topology
|
925
|
+
from topologicpy.Graph import Graph
|
926
|
+
from topologicpy.Dictionary import Dictionary
|
927
|
+
import plotly.graph_objects as go
|
928
|
+
|
929
|
+
if not Topology.IsInstance(graph, "Graph"):
|
930
|
+
if not silent:
|
931
|
+
print("Plotly.AdjacencyMatrixTable - Error: The input is not a valid Graph. Returning None.")
|
932
|
+
return None
|
933
|
+
|
934
|
+
# Build adjacency matrix
|
935
|
+
adj = Graph.AdjacencyMatrix(graph,
|
936
|
+
vertexKey=vertexKey,
|
937
|
+
reverse=reverse,
|
938
|
+
edgeKeyFwd=edgeKeyFwd,
|
939
|
+
edgeKeyBwd=edgeKeyBwd,
|
940
|
+
bidirKey=bidirKey,
|
941
|
+
bidirectional=bidirectional,
|
942
|
+
useEdgeIndex=useEdgeIndex,
|
943
|
+
useEdgeLength=useEdgeLength,
|
944
|
+
mantissa=mantissa,
|
945
|
+
tolerance=tolerance)
|
946
|
+
|
947
|
+
if adj is None or not isinstance(adj, list) or len(adj) == 0:
|
948
|
+
if not silent:
|
949
|
+
print("Plotly.AdjacencyMatrixTable - Warning: Empty adjacency matrix. Returning None.")
|
950
|
+
return None
|
951
|
+
|
952
|
+
n = len(adj)
|
953
|
+
# Validate squareness
|
954
|
+
if any((not isinstance(row, list) or len(row) != n) for row in adj):
|
955
|
+
if not silent:
|
956
|
+
print("Plotly.AdjacencyMatrixTable - Error: Adjacency matrix must be square. Returning None.")
|
957
|
+
return None
|
958
|
+
|
959
|
+
# Derive labels
|
960
|
+
verts = Graph.Vertices(graph)
|
961
|
+
labels = [Dictionary.ValueAtKey(Topology.Dictionary(v), vertexKey, str(i)) for i, v in enumerate(verts)]
|
962
|
+
if len(labels) > 0 and not vertexKey == None:
|
963
|
+
labels.sort()
|
964
|
+
if reverse == True:
|
965
|
+
labels.reverse()
|
966
|
+
# Build display matrix (strings) while keeping a parallel style mask for diagonal
|
967
|
+
display_matrix = []
|
968
|
+
diag_mask = []
|
969
|
+
for r in range(n):
|
970
|
+
row_vals = []
|
971
|
+
row_diag = []
|
972
|
+
for c in range(n):
|
973
|
+
v = adj[r][c]
|
974
|
+
if v == 0:
|
975
|
+
row_vals.append("0" if showZero else (zeroChar if zeroChar else ""))
|
976
|
+
else:
|
977
|
+
# Keep integers unpadded for clarity; cast others nicely
|
978
|
+
row_vals.append(str(int(v)) if isinstance(v, (int, float)) and float(v).is_integer() else str(v))
|
979
|
+
row_diag.append(r == c)
|
980
|
+
display_matrix.append(row_vals)
|
981
|
+
diag_mask.append(row_diag)
|
982
|
+
|
983
|
+
# Construct header and cells for Plotly Table
|
984
|
+
# Header: blank corner + column labels
|
985
|
+
header_values = [""] + labels
|
986
|
+
|
987
|
+
# Body: first column is row labels, then matrix cells as strings
|
988
|
+
# Plotly Table expects columns as lists; we need to transpose
|
989
|
+
columns = []
|
990
|
+
# Column 0: row labels
|
991
|
+
columns.append(labels)
|
992
|
+
# Subsequent columns: for each c, collect display_matrix[r][c]
|
993
|
+
for c in range(n):
|
994
|
+
columns.append([display_matrix[r][c] for r in range(n)])
|
995
|
+
|
996
|
+
# Flatten cell fill_colors to highlight diagonal subtly.
|
997
|
+
# Plotly Table allows per-cell fillcolor via a 2D list matching the table shape for 'cells'.
|
998
|
+
# Our cells shape is n rows x (n+1) cols (including row label column).
|
999
|
+
|
1000
|
+
fill_colors = []
|
1001
|
+
# Column 0: row labels (no highlight)
|
1002
|
+
fill_colors.append([headerColor] * n)
|
1003
|
+
|
1004
|
+
# Columns 1..n: highlight diagonal where row index r == (column_index-1)
|
1005
|
+
for c in range(1, n + 1):
|
1006
|
+
col_colors = []
|
1007
|
+
for r in range(n):
|
1008
|
+
if diagonalHighlight and r == (c - 1):
|
1009
|
+
col_colors.append(diagonalColor)
|
1010
|
+
elif columns[c][r] == "0" or columns[c][r] == zeroChar:
|
1011
|
+
col_colors.append(zeroColor)
|
1012
|
+
else:
|
1013
|
+
col_colors.append(valueColor)
|
1014
|
+
fill_colors.append(col_colors)
|
1015
|
+
|
1016
|
+
# Minimal line style
|
1017
|
+
line_color = "rgba(0,0,0,0.12)"
|
1018
|
+
# --- Sizing to prevent cropped text ---
|
1019
|
+
# Heuristic widths (pixels)
|
1020
|
+
max_label_len = max(len(str(x)) for x in labels) if labels else 1
|
1021
|
+
row_label_px = max(120, min(320, 8 * max_label_len)) # scale with label length
|
1022
|
+
cell_px = 36 if n <= 30 else (30 if n <= 50 else 24) # narrower cells for very wide matrices
|
1023
|
+
|
1024
|
+
# Adaptive cell font size for many columns
|
1025
|
+
fontSize = max(fontSize, 3)
|
1026
|
+
cell_font_size = fontSize if n <= 35 else (fontSize-1 if n <= 60 else fontSize-2)
|
1027
|
+
|
1028
|
+
# Figure width: row label column + all matrix columns
|
1029
|
+
fig_width = row_label_px + n * cell_px
|
1030
|
+
fig_width = max(600, min(2400, fig_width)) # clamp to reasonable bounds
|
1031
|
+
|
1032
|
+
# Increase row height a bit for readability
|
1033
|
+
cellSize = max(cellSize, 26)
|
1034
|
+
|
1035
|
+
# Column widths in px (Plotly Table accepts pixel widths)
|
1036
|
+
columnwidth = [row_label_px] + [cell_px] * n
|
1037
|
+
fig = go.Figure(
|
1038
|
+
data=[
|
1039
|
+
go.Table(
|
1040
|
+
header=dict(
|
1041
|
+
values=header_values,
|
1042
|
+
align="center",
|
1043
|
+
font=dict(family=fontFamily, size=cell_font_size, color=fontColor),
|
1044
|
+
fill_color=headerColor,
|
1045
|
+
line_color=line_color,
|
1046
|
+
height=cellSize + 4 # a touch taller for the header
|
1047
|
+
),
|
1048
|
+
cells=dict(
|
1049
|
+
values=columns,
|
1050
|
+
align=["right"] + ["center"] * n,
|
1051
|
+
font=dict(family=fontFamily, size=cell_font_size, color=fontColor),
|
1052
|
+
fill_color=fill_colors,
|
1053
|
+
line_color=line_color,
|
1054
|
+
height=cellSize
|
1055
|
+
),
|
1056
|
+
columnorder=list(range(n + 1)),
|
1057
|
+
columnwidth=columnwidth
|
1058
|
+
)
|
1059
|
+
]
|
1060
|
+
)
|
1061
|
+
|
1062
|
+
# Layout: generous margins, white background, optional title
|
1063
|
+
fig.update_layout(
|
1064
|
+
title=dict(text=title, x=0.5, xanchor="center") if title else None,
|
1065
|
+
paper_bgcolor="white",
|
1066
|
+
plot_bgcolor=backgroundColor,
|
1067
|
+
margin=dict(l=20, r=20, t=40 if title else 10, b=20)
|
1068
|
+
)
|
1069
|
+
|
1070
|
+
return fig
|
1071
|
+
|
788
1072
|
@staticmethod
|
789
1073
|
def AdjacencyList(graph, vertexKey=None, reverse=True, tolerance=0.0001):
|
790
1074
|
"""
|
topologicpy/Helper.py
CHANGED
@@ -90,6 +90,53 @@ class Helper:
|
|
90
90
|
|
91
91
|
return sorted(bin_averages)
|
92
92
|
|
93
|
+
def CheckVersion(library: str = None, version: str = None, silent: bool = False):
|
94
|
+
"""
|
95
|
+
Compare an input version with the latest version of a Python library on PyPI.
|
96
|
+
|
97
|
+
Parameters
|
98
|
+
----------
|
99
|
+
library : str
|
100
|
+
The input software library name. Default is None.
|
101
|
+
version : str
|
102
|
+
The input software version number to compare. Default is None.
|
103
|
+
silent : bool , optional
|
104
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
str: A message indicating whether the input version is less than,
|
108
|
+
equal to, or greater than the latest version on PyPI.
|
109
|
+
"""
|
110
|
+
import requests
|
111
|
+
from packaging import version as ver
|
112
|
+
|
113
|
+
try:
|
114
|
+
# Fetch library data from PyPI
|
115
|
+
url = f"https://pypi.org/pypi/{library}/json"
|
116
|
+
response = requests.get(url)
|
117
|
+
response.raise_for_status()
|
118
|
+
|
119
|
+
# Extract the latest version from the JSON response
|
120
|
+
data = response.json()
|
121
|
+
latest_version = data['info']['version']
|
122
|
+
|
123
|
+
# Compare versions using the packaging library
|
124
|
+
if ver.parse(version) < ver.parse(latest_version):
|
125
|
+
return (f"The version that you are using ({version}) is OLDER than the latest version ({latest_version}) from PyPI. Please consider upgrading to the latest version.")
|
126
|
+
elif ver.parse(version) == ver.parse(latest_version):
|
127
|
+
return (f"The version that you are using ({version}) is EQUAL TO the latest version available on PyPI.")
|
128
|
+
else:
|
129
|
+
return (f"The version that you are using ({version}) is NEWER than the latest version ({latest_version}) available from PyPI.")
|
130
|
+
|
131
|
+
except requests.exceptions.RequestException as e:
|
132
|
+
if not silent:
|
133
|
+
print("Helper.CheckVersion - Error: Could not fetch data from PyPI. Returning None")
|
134
|
+
return None
|
135
|
+
except KeyError:
|
136
|
+
if not silent:
|
137
|
+
print("Helper.CheckVersion - Error: Could not fetch data from PyPI. Returning None")
|
138
|
+
return None
|
139
|
+
|
93
140
|
@staticmethod
|
94
141
|
def ClosestMatch(item, listA):
|
95
142
|
"""
|
@@ -788,58 +835,25 @@ class Helper:
|
|
788
835
|
return returnList
|
789
836
|
|
790
837
|
@staticmethod
|
791
|
-
def Version():
|
838
|
+
def Version(check: bool = True, silent: bool = False):
|
792
839
|
"""
|
793
840
|
Returns the current version of the software.
|
794
841
|
|
795
842
|
Parameters
|
796
843
|
----------
|
797
|
-
|
844
|
+
check : bool , optional
|
845
|
+
if set to True, the version number is checked with the latest version on PyPi. Default is True.
|
846
|
+
silent : bool , optional
|
847
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
848
|
+
|
798
849
|
Returns
|
799
850
|
-------
|
800
851
|
str
|
801
|
-
The current version of the software.
|
852
|
+
The current version of the software. Optionally, includes a check with PyPi.
|
802
853
|
|
803
854
|
"""
|
804
855
|
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
def compare_version_with_pypi(library_name, input_version):
|
809
|
-
"""
|
810
|
-
Compare an input version with the latest version of a Python library on PyPI.
|
811
|
-
|
812
|
-
Args:
|
813
|
-
library_name (str): The name of the Python library on PyPI.
|
814
|
-
input_version (str): The version number to compare (e.g., "0.7.58").
|
815
|
-
|
816
|
-
Returns:
|
817
|
-
str: A message indicating whether the input version is less than,
|
818
|
-
equal to, or greater than the latest version on PyPI.
|
819
|
-
"""
|
820
|
-
try:
|
821
|
-
# Fetch library data from PyPI
|
822
|
-
url = f"https://pypi.org/pypi/{library_name}/json"
|
823
|
-
response = requests.get(url)
|
824
|
-
response.raise_for_status()
|
825
|
-
|
826
|
-
# Extract the latest version from the JSON response
|
827
|
-
data = response.json()
|
828
|
-
latest_version = data['info']['version']
|
829
|
-
|
830
|
-
# Compare versions using the packaging library
|
831
|
-
if version.parse(input_version) < version.parse(latest_version):
|
832
|
-
return (f"The version that you are using ({input_version}) is OLDER than the latest version {latest_version} from PyPI. Please consider upgrading to the latest version.")
|
833
|
-
elif version.parse(input_version) == version.parse(latest_version):
|
834
|
-
return (f"The version that you are using ({input_version}) is the latest version available on PyPI.")
|
835
|
-
else:
|
836
|
-
return (f"The version that you are using ({input_version}) is NEWER than the latest version ({latest_version}) available from PyPI.")
|
837
|
-
|
838
|
-
except requests.exceptions.RequestException as e:
|
839
|
-
return f"Error fetching data from PyPI: {e}"
|
840
|
-
except KeyError:
|
841
|
-
return "Error: Unable to find the latest version in the PyPI response."
|
842
|
-
|
843
|
-
current_version = topologicpy.__version__
|
844
|
-
result = compare_version_with_pypi("topologicpy", current_version)
|
856
|
+
result = topologicpy.__version__
|
857
|
+
if check == True:
|
858
|
+
result = Helper.CheckVersion("topologicpy", result, silent=silent)
|
845
859
|
return result
|
topologicpy/Honeybee.py
CHANGED
@@ -14,6 +14,7 @@
|
|
14
14
|
# You should have received a copy of the GNU Affero General Public License along with
|
15
15
|
# this program. If not, see <https://www.gnu.org/licenses/>.
|
16
16
|
|
17
|
+
from __future__ import annotations
|
17
18
|
import os
|
18
19
|
import warnings
|
19
20
|
|
@@ -112,6 +113,530 @@ import json
|
|
112
113
|
import topologic_core as topologic
|
113
114
|
|
114
115
|
class Honeybee:
|
116
|
+
@staticmethod
|
117
|
+
def ByHBJSONDictionary(
|
118
|
+
dictionary,
|
119
|
+
includeRooms: bool = True,
|
120
|
+
includeFaces: bool = True,
|
121
|
+
includeShades: bool = True,
|
122
|
+
includeApertures: bool = True,
|
123
|
+
includeDoors: bool = True,
|
124
|
+
includeOrphanedRooms: bool = True,
|
125
|
+
includeOrphanedFaces: bool = True,
|
126
|
+
includeOrphanedShades: bool = True,
|
127
|
+
includeOrphanedApertures: bool = True,
|
128
|
+
includeOrphanedDoors: bool = True,
|
129
|
+
tolerance: float = 0.0001,
|
130
|
+
silent: bool = False):
|
131
|
+
"""
|
132
|
+
Import an HBJSON model from a python dictionary and return a python dictionary. See: https://github.com/ladybug-tools/honeybee-schema/wiki/1.1-Model-Schema
|
133
|
+
|
134
|
+
Parameters
|
135
|
+
----------
|
136
|
+
dictionary : dict
|
137
|
+
The HBJSON model as a Python dictionary (e.g., loaded via ``json.load``).
|
138
|
+
includeRooms : bool, optional
|
139
|
+
If True, parse rooms and attempt to create one ``Cell`` per room. Default is True.
|
140
|
+
includeFaces : bool, optional
|
141
|
+
If True, include top-level planar faces found outside rooms (e.g., at root "faces"). Default is True.
|
142
|
+
includeShades : bool, optional
|
143
|
+
If True, include context/standalone shades (e.g., ``context_geometry.shades``). Default is True.
|
144
|
+
includeApertures : bool, optional
|
145
|
+
If True, include **room** apertures (e.g., windows) as separate ``Face`` objects (not cut from hosts). Default is True.
|
146
|
+
includeDoors : bool, optional
|
147
|
+
If True, include **room** doors as separate ``Face`` objects (not cut from hosts). Default is True.
|
148
|
+
includeOrphanedRooms : bool, optional
|
149
|
+
If True, include the topology of the room when a room fails to close as a ``Cell``. This may be a ``Shell`` or a ``Cluster``. Default is True.
|
150
|
+
includeOrphanedFaces : bool, optional
|
151
|
+
If True, include planar faces listed at the HBJSON root (e.g., "faces"). Default is True.
|
152
|
+
includeOrphanedShades : bool, optional
|
153
|
+
If True, include shades listed at the HBJSON root (e.g., "orphaned_shades"). Default is True.
|
154
|
+
includeOrphanedApertures : bool, optional
|
155
|
+
If True, include apertures listed at the HBJSON root (e.g., "orphaned_apertures"). Default is True.
|
156
|
+
includeOrphanedDoors : bool, optional
|
157
|
+
If True, include doors listed at the HBJSON root (e.g., "orphaned_doors"). Default is True.
|
158
|
+
tolerance : float , optional
|
159
|
+
The desired tolerance. Default is 0.0001.
|
160
|
+
silent : bool , optional
|
161
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
162
|
+
|
163
|
+
Returns
|
164
|
+
-------
|
165
|
+
dict
|
166
|
+
The created cluster of vertices, edges, faces, and cells.
|
167
|
+
- 'rooms': list of Cells (one per successfully closed room)
|
168
|
+
- 'faces': list of Faces (all faces that make up the rooms)
|
169
|
+
- 'shades': list of Faces (all shade faces)
|
170
|
+
- 'apertures': list of Faces (all apertures, never cut from hosts)
|
171
|
+
- 'doors': list of Faces (all doors, never cut from hosts)
|
172
|
+
- 'orphanedRooms': list of Topologies (context/top-level topologies (e.g. Shells or Clustser) that failed to form a Cell)
|
173
|
+
- 'orphanedFaces': list of Faces (context/top-level faces + host faces of rooms that failed to form a Cell)
|
174
|
+
- 'orphanedShades': list of Faces (context/top-level shade faces that failed to have a parent cell)
|
175
|
+
- 'orphanedApertures': list of Faces (apertures that failed to have a parent face)
|
176
|
+
- 'orphanedDoors': list of Faces (doors that failed to have a parent face)
|
177
|
+
- 'properties': hierarchical dict copied verbatim from HBJSON['properties']
|
178
|
+
"""
|
179
|
+
|
180
|
+
from topologicpy.Vertex import Vertex
|
181
|
+
from topologicpy.Edge import Edge
|
182
|
+
from topologicpy.Wire import Wire
|
183
|
+
from topologicpy.Face import Face
|
184
|
+
from topologicpy.Shell import Shell
|
185
|
+
from topologicpy.Cell import Cell
|
186
|
+
from topologicpy.Cluster import Cluster
|
187
|
+
from topologicpy.Topology import Topology
|
188
|
+
from topologicpy.Dictionary import Dictionary
|
189
|
+
from topologicpy.Helper import Helper
|
190
|
+
from typing import Any, Dict, List, Optional, Tuple
|
191
|
+
|
192
|
+
if not isinstance(dictionary, dict):
|
193
|
+
if not silent:
|
194
|
+
print("Honeybee.ByHBJSONDictionary - Error: The input dictionary parameter is not a valid python dictionary. Returning None.")
|
195
|
+
return None
|
196
|
+
|
197
|
+
# ---------------------- helpers ----------------------
|
198
|
+
def _close(points: List[List[float]]) -> List[List[float]]:
|
199
|
+
if not points:
|
200
|
+
return points
|
201
|
+
p0, pN = points[0], points[-1]
|
202
|
+
if (abs(p0[0]-pN[0]) > tolerance or
|
203
|
+
abs(p0[1]-pN[1]) > tolerance or
|
204
|
+
abs(p0[2]-pN[2]) > tolerance):
|
205
|
+
return points + [p0]
|
206
|
+
return points
|
207
|
+
|
208
|
+
def _V(p: List[float]) -> Vertex:
|
209
|
+
return Vertex.ByCoordinates(float(p[0]), float(p[1]), float(p[2]))
|
210
|
+
|
211
|
+
# Tolerance-filtered wire (your spec)
|
212
|
+
def _wire(points: List[List[float]], tolerance: float = 1e-6) -> Wire:
|
213
|
+
pts = _close(points)
|
214
|
+
verts = [_V(x) for x in pts]
|
215
|
+
edges = [
|
216
|
+
Edge.ByVertices(verts[i], verts[i+1], tolerance=tolerance, silent=True)
|
217
|
+
for i in range(len(verts)-1)
|
218
|
+
if Vertex.Distance(verts[i], verts[i+1]) > tolerance
|
219
|
+
]
|
220
|
+
w = None
|
221
|
+
try:
|
222
|
+
w = Wire.ByEdges(edges, tolerance=tolerance, silent=True)
|
223
|
+
except:
|
224
|
+
w = Topology.SelfMerge(Cluster.ByTopologies(edges), tolerance=tolerance)
|
225
|
+
if w == None:
|
226
|
+
if not silent:
|
227
|
+
print("Honeybee.ByHBSJONDictionary - Error: Could not build wire. Returning None.")
|
228
|
+
return w
|
229
|
+
|
230
|
+
def _face_from_boundary(boundary: List[List[float]]) -> Face:
|
231
|
+
w = _wire(boundary, tolerance)
|
232
|
+
if w:
|
233
|
+
f = Face.ByWire(w, tolerance=tolerance, silent=True)
|
234
|
+
if not f:
|
235
|
+
if not silent:
|
236
|
+
print("Honeybee.ByHBSJONDictionary - Error: Could not build face. Returning the wire")
|
237
|
+
return w
|
238
|
+
return f
|
239
|
+
if not silent:
|
240
|
+
print("Honeybee.ByHBSJONDictionary - Error: Could not build face. Returning None")
|
241
|
+
return None
|
242
|
+
|
243
|
+
def _attach_all(top: Topology, full_py_dict: Dict[str, Any]) -> Topology:
|
244
|
+
# Attach the entire available dict (no filtering)
|
245
|
+
try:
|
246
|
+
keys = list(full_py_dict.keys())
|
247
|
+
values = [full_py_dict[k] for k in keys]
|
248
|
+
d = Dictionary.ByKeysValues(keys, values)
|
249
|
+
return Topology.SetDictionary(top, d)
|
250
|
+
except Exception:
|
251
|
+
return top # be robust to non-serializable values
|
252
|
+
|
253
|
+
def _build_host_face_cut_holes(fobj: Dict[str, Any]) -> Optional[Face]:
|
254
|
+
"""
|
255
|
+
Build host face from outer boundary and cut ONLY explicit 'holes' (NOT apertures/doors).
|
256
|
+
Attach the full fobj dict.
|
257
|
+
"""
|
258
|
+
geom = fobj.get("geometry") or {}
|
259
|
+
boundary = geom.get("boundary") or fobj.get("boundary")
|
260
|
+
holes = geom.get("holes") or fobj.get("holes") or []
|
261
|
+
|
262
|
+
if not boundary or len(boundary) < 3:
|
263
|
+
return None
|
264
|
+
|
265
|
+
hosts = _face_from_boundary(boundary)
|
266
|
+
if Topology.IsInstance(hosts, "face") or Topology.IsInstance(hosts, "wire"):
|
267
|
+
hosts = [hosts]
|
268
|
+
|
269
|
+
for host in hosts:
|
270
|
+
# Subtract explicit hole loops (if any)
|
271
|
+
hole_faces: List[Face] = []
|
272
|
+
for h in holes:
|
273
|
+
if h and len(h) >= 3:
|
274
|
+
hole_faces.append(_face_from_boundary(h))
|
275
|
+
|
276
|
+
if hole_faces:
|
277
|
+
hole_cluster = Cluster.ByTopologies(hole_faces)
|
278
|
+
try:
|
279
|
+
host = Topology.Difference(host, hole_cluster)
|
280
|
+
except Exception as e:
|
281
|
+
if not silent:
|
282
|
+
name = fobj.get("identifier") or fobj.get("name") or "unnamed"
|
283
|
+
print(f"HBJSON Import: Hole cutting failed on face '{name}'. Keeping uncut. Error: {e}")
|
284
|
+
_attach_all(host, fobj)
|
285
|
+
return hosts
|
286
|
+
|
287
|
+
def _aperture_faces_from(fobj: Dict[str, Any], kind: str) -> List[Face]:
|
288
|
+
"""
|
289
|
+
Build separate faces for apertures/doors on a host (DO NOT cut from host).
|
290
|
+
'kind' ∈ {'apertures','doors'}. Attach full dict for each.
|
291
|
+
"""
|
292
|
+
out: List[Face] = []
|
293
|
+
ap_list = fobj.get(kind) or []
|
294
|
+
for ap in ap_list:
|
295
|
+
g = ap.get("geometry") or {}
|
296
|
+
boundary = g.get("boundary") or ap.get("boundary")
|
297
|
+
if not boundary or len(boundary) < 3:
|
298
|
+
continue
|
299
|
+
f = _face_from_boundary(boundary)
|
300
|
+
out.append(_attach_all(f, ap))
|
301
|
+
return out
|
302
|
+
|
303
|
+
def _orphaned_aperture_faces(ap_list: List[Dict[str, Any]]) -> List[Face]:
|
304
|
+
out: List[Face] = []
|
305
|
+
for ap in ap_list or []:
|
306
|
+
g = ap.get("geometry") or {}
|
307
|
+
boundary = g.get("boundary") or ap.get("boundary")
|
308
|
+
if not boundary or len(boundary) < 3:
|
309
|
+
continue
|
310
|
+
f = _face_from_boundary(boundary)
|
311
|
+
out.append(_attach_all(f, ap))
|
312
|
+
return out
|
313
|
+
|
314
|
+
def _room_to_cell_and_apertures(room: Dict[str, Any]) -> Tuple[Optional[Cell], List[Face], List[Face]]:
|
315
|
+
"""
|
316
|
+
Build host faces (cut 'holes' only) and aperture faces for a room.
|
317
|
+
Return (cell_or_none, host_faces, aperture_faces).
|
318
|
+
"""
|
319
|
+
hb_faces = room.get("faces") or room.get("Faces") or []
|
320
|
+
rm_faces: List[Face] = []
|
321
|
+
sh_faces = room.get("shades") or room.get("Shades") or []
|
322
|
+
ap_faces: List[Face] = []
|
323
|
+
dr_faces: List[Face] = []
|
324
|
+
|
325
|
+
|
326
|
+
for fobj in hb_faces:
|
327
|
+
hosts = _build_host_face_cut_holes(fobj)
|
328
|
+
if hosts:
|
329
|
+
rm_faces.extend(hosts)
|
330
|
+
ap_faces.extend(_aperture_faces_from(fobj, "apertures"))
|
331
|
+
dr_faces.extend(_aperture_faces_from(fobj, "doors"))
|
332
|
+
|
333
|
+
# Room Shades
|
334
|
+
for sh in sh_faces:
|
335
|
+
shades = _build_host_face_cut_holes(sh)
|
336
|
+
if shades:
|
337
|
+
sh_faces.extend(shades)
|
338
|
+
# Try to make a Cell. If it fails, we DO NOT return a Shell/Cluster in rooms;
|
339
|
+
# instead we will salvage host faces into the 'faces' bucket.
|
340
|
+
if rm_faces:
|
341
|
+
selectors = []
|
342
|
+
for rm_face in rm_faces:
|
343
|
+
s = Topology.InternalVertex(rm_face)
|
344
|
+
face_d = Topology.Dictionary(rm_face)
|
345
|
+
s = Topology.SetDictionary(s, face_d)
|
346
|
+
selectors.append(s)
|
347
|
+
cell = Cell.ByFaces(Helper.Flatten(rm_faces), tolerance=0.001, silent=True)
|
348
|
+
if Topology.IsInstance(cell, "cell"):
|
349
|
+
cell = _attach_all(cell, room) # attach full room dict
|
350
|
+
else:
|
351
|
+
cell = Shell.ByFaces(Helper.Flatten(rm_faces), tolerance=0.001, silent=True)
|
352
|
+
if not cell:
|
353
|
+
cell = Cluster.ByTopologies(Helper.Flatten(rm_faces), silent=True)
|
354
|
+
if Topology.IsInstance(cell, "topology"):
|
355
|
+
cell = _attach_all(cell, room) # attach full room dict
|
356
|
+
|
357
|
+
if Topology.IsInstance(cell, "Topology"):
|
358
|
+
cell = Topology.TransferDictionariesBySelectors(cell, selectors,tranFaces=True, numWorkers=1)
|
359
|
+
return cell, rm_faces, sh_faces, ap_faces, dr_faces
|
360
|
+
# No host faces -> no cell
|
361
|
+
return None, [], sh_faces, ap_faces, dr_faces
|
362
|
+
|
363
|
+
rooms: List[Cell] = []
|
364
|
+
faces: List[Face] = []
|
365
|
+
shades: List[Face] = []
|
366
|
+
apertures: List[Face] = []
|
367
|
+
doors: List[Face] = []
|
368
|
+
orphaned_rooms: List[Cell] = []
|
369
|
+
orphaned_faces: List[Face] = []
|
370
|
+
orphaned_shades: List[Face] = []
|
371
|
+
orphaned_apertures: List[Face] = []
|
372
|
+
orphaned_doors: List[Face] = []
|
373
|
+
|
374
|
+
# Rooms → Cells (when possible) + collect apertures. If a Cell cannot be made,
|
375
|
+
# the room goes to the orphaned_rooms list.
|
376
|
+
for room in (dictionary.get("rooms") or dictionary.get("Rooms") or []):
|
377
|
+
cell, host_faces, sh_faces, ap_faces, dr_faces = _room_to_cell_and_apertures(room)
|
378
|
+
|
379
|
+
if includeRooms and Topology.IsInstance(cell, "cell"):
|
380
|
+
rooms.append(cell)
|
381
|
+
elif includeOrphanedRooms and Topology.IsInstance(cell, "topology"):
|
382
|
+
orphaned_rooms.append(cell)
|
383
|
+
if cell:
|
384
|
+
if includeFaces and host_faces:
|
385
|
+
faces.extend(host_faces)
|
386
|
+
if includeShades and sh_faces:
|
387
|
+
shades.extend(sh_faces)
|
388
|
+
if includeApertures and ap_faces:
|
389
|
+
apertures.extend(ap_faces)
|
390
|
+
if includeDoors and dr_faces:
|
391
|
+
doors.extend(dr_faces)
|
392
|
+
|
393
|
+
# Explicit orphaned faces → 'orphaned_faces'
|
394
|
+
if includeOrphanedFaces:
|
395
|
+
explicit_orphaned_faces = dictionary.get("orphaned_faces") or dictionary.get("OrphanedFaces") or []
|
396
|
+
for f in explicit_orphaned_faces:
|
397
|
+
hf = _build_host_face_cut_holes(f)
|
398
|
+
if hf:
|
399
|
+
orphaned_faces.extend(hf)
|
400
|
+
# Some files also place planar surfaces at top-level 'faces'
|
401
|
+
for fobj in (dictionary.get("faces") or dictionary.get("Faces") or []):
|
402
|
+
hf = _build_host_face_cut_holes(fobj)
|
403
|
+
if hf:
|
404
|
+
orphaned_faces.extend(hf)
|
405
|
+
|
406
|
+
# Explicit orphaned shades (and/or context shades)
|
407
|
+
if includeOrphanedShades:
|
408
|
+
explicit_orphaned_shades = dictionary.get("orphaned_shades") or dictionary.get("OrphanedShades") or []
|
409
|
+
for s in explicit_orphaned_shades:
|
410
|
+
hf = _build_host_face_cut_holes(s)
|
411
|
+
if hf:
|
412
|
+
orphaned_shades.extend(hf)
|
413
|
+
|
414
|
+
ctx = dictionary.get("context_geometry") or dictionary.get("Context") or {}
|
415
|
+
shade_list = []
|
416
|
+
if isinstance(ctx, dict):
|
417
|
+
shade_list = ctx.get("shades") or ctx.get("Shades") or []
|
418
|
+
elif isinstance(ctx, list):
|
419
|
+
shade_list = ctx
|
420
|
+
for s in shade_list:
|
421
|
+
hf = _build_host_face_cut_holes(s)
|
422
|
+
if hf:
|
423
|
+
orphaned_shades.extend(hf)
|
424
|
+
# Some files might also place planar shade surfaces at top-level 'shades'
|
425
|
+
for fobj in (dictionary.get("shades") or dictionary.get("Shades") or []):
|
426
|
+
hf = _build_host_face_cut_holes(fobj)
|
427
|
+
if hf:
|
428
|
+
orphaned_shades.extend(hf)
|
429
|
+
|
430
|
+
# Explicit orphaned apertures → 'orphaned_apertures'
|
431
|
+
if includeOrphanedApertures:
|
432
|
+
orphaned_ap_list = dictionary.get("orphaned_apertures") or dictionary.get("OrphanedApertures") or []
|
433
|
+
if orphaned_ap_list:
|
434
|
+
orphaned_apertures.extend(_orphaned_aperture_faces(orphaned_ap_list))
|
435
|
+
|
436
|
+
# Explicit orphaned doors → 'orphaned_doors'
|
437
|
+
if includeOrphanedDoors:
|
438
|
+
orphaned_dr_list = dictionary.get("orphaned_doors") or dictionary.get("OrphanedDoors") or []
|
439
|
+
if orphaned_dr_list:
|
440
|
+
orphaned_doors.extend(_orphaned_aperture_faces(orphaned_dr_list)) #You can use the same function as apertures.
|
441
|
+
|
442
|
+
# Properties → hierarchical dict verbatim
|
443
|
+
props_root = dictionary.get("properties") or dictionary.get("Properties") or {}
|
444
|
+
properties = {
|
445
|
+
"radiance": props_root.get("radiance") or props_root.get("Radiance") or {},
|
446
|
+
"energy": props_root.get("energy") or props_root.get("Energy") or {},
|
447
|
+
}
|
448
|
+
|
449
|
+
return {
|
450
|
+
"rooms": rooms,
|
451
|
+
"faces": faces,
|
452
|
+
"shades": shades,
|
453
|
+
"apertures": apertures,
|
454
|
+
"doors": doors,
|
455
|
+
"orphanedRooms": orphaned_rooms,
|
456
|
+
"orphanedFaces": orphaned_faces,
|
457
|
+
"orphanedShades": orphaned_shades,
|
458
|
+
"orphanedApertures": orphaned_apertures,
|
459
|
+
"orphanedDoors": orphaned_doors,
|
460
|
+
"properties": properties
|
461
|
+
}
|
462
|
+
|
463
|
+
@staticmethod
|
464
|
+
def ByHBJSONPath(
|
465
|
+
path: str,
|
466
|
+
includeRooms: bool = True,
|
467
|
+
includeFaces: bool = True,
|
468
|
+
includeShades: bool = True,
|
469
|
+
includeApertures: bool = True,
|
470
|
+
includeDoors: bool = True,
|
471
|
+
includeOrphanedRooms: bool = True,
|
472
|
+
includeOrphanedFaces: bool = True,
|
473
|
+
includeOrphanedShades: bool = True,
|
474
|
+
includeOrphanedApertures: bool = True,
|
475
|
+
includeOrphanedDoors: bool = True,
|
476
|
+
tolerance: float = 0.0001,
|
477
|
+
silent: bool = False):
|
478
|
+
"""
|
479
|
+
Import an HBJSON model from a file path and return a python dictionary. See: https://github.com/ladybug-tools/honeybee-schema/wiki/1.1-Model-Schema
|
480
|
+
|
481
|
+
Parameters
|
482
|
+
----------
|
483
|
+
dictionary : dict
|
484
|
+
The HBJSON model as a Python dictionary (e.g., loaded via ``json.load``).
|
485
|
+
includeRooms : bool, optional
|
486
|
+
If True, parse rooms and attempt to create one ``Cell`` per room. Default is True.
|
487
|
+
includeFaces : bool, optional
|
488
|
+
If True, include top-level planar faces found outside rooms (e.g., at root "faces"). Default is True.
|
489
|
+
includeShades : bool, optional
|
490
|
+
If True, include context/standalone shades (e.g., ``context_geometry.shades``). Default is True.
|
491
|
+
includeApertures : bool, optional
|
492
|
+
If True, include **room** apertures (e.g., windows) as separate ``Face`` objects (not cut from hosts). Default is True.
|
493
|
+
includeDoors : bool, optional
|
494
|
+
If True, include **room** doors as separate ``Face`` objects (not cut from hosts). Default is True.
|
495
|
+
includeOrphanedRooms : bool, optional
|
496
|
+
If True, include the topology of the room when a room fails to close as a ``Cell``. This may be a ``Shell`` or a ``Cluster``. Default is True.
|
497
|
+
includeOrphanedFaces : bool, optional
|
498
|
+
If True, include planar faces listed at the HBJSON root (e.g., "faces"). Default is True.
|
499
|
+
includeOrphanedShades : bool, optional
|
500
|
+
If True, include shades listed at the HBJSON root (e.g., "orphaned_shades"). Default is True.
|
501
|
+
includeOrphanedApertures : bool, optional
|
502
|
+
If True, include apertures listed at the HBJSON root (e.g., "orphaned_apertures"). Default is True.
|
503
|
+
includeOrphanedDoors : bool, optional
|
504
|
+
If True, include doors listed at the HBJSON root (e.g., "orphaned_doors"). Default is True.
|
505
|
+
tolerance : float , optional
|
506
|
+
The desired tolerance. Default is 0.0001.
|
507
|
+
silent : bool , optional
|
508
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
509
|
+
|
510
|
+
Returns
|
511
|
+
-------
|
512
|
+
dict
|
513
|
+
The created cluster of vertices, edges, faces, and cells.
|
514
|
+
- 'rooms': list of Cells (one per successfully closed room)
|
515
|
+
- 'faces': list of Faces (all faces that make up the rooms)
|
516
|
+
- 'shades': list of Faces (all shade faces)
|
517
|
+
- 'apertures': list of Faces (all apertures, never cut from hosts)
|
518
|
+
- 'doors': list of Faces (all doors, never cut from hosts)
|
519
|
+
- 'orphanedRooms': list of Topologies (context/top-level topologies (e.g. Shells or Clustser) that failed to form a Cell)
|
520
|
+
- 'orphanedFaces': list of Faces (context/top-level faces + host faces of rooms that failed to form a Cell)
|
521
|
+
- 'orphanedShades': list of Faces (context/top-level shade faces that failed to have a parent cell)
|
522
|
+
- 'orphanedApertures': list of Faces (apertures that failed to have a parent face)
|
523
|
+
- 'orphanedDoors': list of Faces (doors that failed to have a parent face)
|
524
|
+
- 'properties': hierarchical dict copied verbatim from HBJSON['properties']
|
525
|
+
"""
|
526
|
+
|
527
|
+
import json
|
528
|
+
if not path:
|
529
|
+
if not silent:
|
530
|
+
print("Honeybee.ByHBJSONPath - Error: the input path parameter is not a valid path. Returning None.")
|
531
|
+
return None
|
532
|
+
with open(path) as file:
|
533
|
+
try:
|
534
|
+
hbjson_dict = json.load(file)
|
535
|
+
except:
|
536
|
+
if not silent:
|
537
|
+
print("Honeybee.ByHBJSONPath - Error: Could not open the HBJSON file. Returning None.")
|
538
|
+
return None
|
539
|
+
return Honeybee.ByHBJSONDictionary(hbjson_dict,
|
540
|
+
includeRooms = includeRooms,
|
541
|
+
includeFaces = includeFaces,
|
542
|
+
includeShades = includeShades,
|
543
|
+
includeApertures = includeApertures,
|
544
|
+
includeDoors = includeDoors,
|
545
|
+
includeOrphanedRooms = includeOrphanedRooms,
|
546
|
+
includeOrphanedFaces = includeOrphanedFaces,
|
547
|
+
includeOrphanedShades = includeOrphanedShades,
|
548
|
+
includeOrphanedApertures = includeOrphanedApertures,
|
549
|
+
includeOrphanedDoors = includeOrphanedDoors,
|
550
|
+
tolerance = tolerance,
|
551
|
+
silent = silent)
|
552
|
+
|
553
|
+
@staticmethod
|
554
|
+
def ByHBJSONString(
|
555
|
+
string,
|
556
|
+
includeRooms: bool = True,
|
557
|
+
includeFaces: bool = True,
|
558
|
+
includeShades: bool = True,
|
559
|
+
includeApertures: bool = True,
|
560
|
+
includeDoors: bool = True,
|
561
|
+
includeOrphanedRooms: bool = True,
|
562
|
+
includeOrphanedFaces: bool = True,
|
563
|
+
includeOrphanedShades: bool = True,
|
564
|
+
includeOrphanedApertures: bool = True,
|
565
|
+
includeOrphanedDoors: bool = True,
|
566
|
+
tolerance: float = 0.0001,
|
567
|
+
silent: bool = False):
|
568
|
+
"""
|
569
|
+
Import an HBJSON model from a file path and return a python dictionary. See: https://github.com/ladybug-tools/honeybee-schema/wiki/1.1-Model-Schema
|
570
|
+
|
571
|
+
Parameters
|
572
|
+
----------
|
573
|
+
string : str
|
574
|
+
The HBJSON model as a string.
|
575
|
+
includeRooms : bool, optional
|
576
|
+
If True, parse rooms and attempt to create one ``Cell`` per room. Default is True.
|
577
|
+
includeFaces : bool, optional
|
578
|
+
If True, include top-level planar faces found outside rooms (e.g., at root "faces"). Default is True.
|
579
|
+
includeShades : bool, optional
|
580
|
+
If True, include context/standalone shades (e.g., ``context_geometry.shades``). Default is True.
|
581
|
+
includeApertures : bool, optional
|
582
|
+
If True, include **room** apertures (e.g., windows) as separate ``Face`` objects (not cut from hosts). Default is True.
|
583
|
+
includeDoors : bool, optional
|
584
|
+
If True, include **room** doors as separate ``Face`` objects (not cut from hosts). Default is True.
|
585
|
+
includeOrphanedRooms : bool, optional
|
586
|
+
If True, include the topology of the room when a room fails to close as a ``Cell``. This may be a ``Shell`` or a ``Cluster``. Default is True.
|
587
|
+
includeOrphanedFaces : bool, optional
|
588
|
+
If True, include planar faces listed at the HBJSON root (e.g., "faces"). Default is True.
|
589
|
+
includeOrphanedShades : bool, optional
|
590
|
+
If True, include shades listed at the HBJSON root (e.g., "orphaned_shades"). Default is True.
|
591
|
+
includeOrphanedApertures : bool, optional
|
592
|
+
If True, include apertures listed at the HBJSON root (e.g., "orphaned_apertures"). Default is True.
|
593
|
+
includeOrphanedDoors : bool, optional
|
594
|
+
If True, include doors listed at the HBJSON root (e.g., "orphaned_doors"). Default is True.
|
595
|
+
tolerance : float , optional
|
596
|
+
The desired tolerance. Default is 0.0001.
|
597
|
+
silent : bool , optional
|
598
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
599
|
+
|
600
|
+
Returns
|
601
|
+
-------
|
602
|
+
dict
|
603
|
+
The created cluster of vertices, edges, faces, and cells.
|
604
|
+
- 'rooms': list of Cells (one per successfully closed room)
|
605
|
+
- 'faces': list of Faces (all faces that make up the rooms)
|
606
|
+
- 'shades': list of Faces (all shade faces)
|
607
|
+
- 'apertures': list of Faces (all apertures, never cut from hosts)
|
608
|
+
- 'doors': list of Faces (all doors, never cut from hosts)
|
609
|
+
- 'orphanedRooms': list of Topologies (context/top-level topologies (e.g. Shells or Clustser) that failed to form a Cell)
|
610
|
+
- 'orphanedFaces': list of Faces (context/top-level faces + host faces of rooms that failed to form a Cell)
|
611
|
+
- 'orphanedShades': list of Faces (context/top-level shade faces that failed to have a parent cell)
|
612
|
+
- 'orphanedApertures': list of Faces (apertures that failed to have a parent face)
|
613
|
+
- 'orphanedDoors': list of Faces (doors that failed to have a parent face)
|
614
|
+
- 'properties': hierarchical dict copied verbatim from HBJSON['properties']
|
615
|
+
"""
|
616
|
+
|
617
|
+
if not isinstance(string, str):
|
618
|
+
if not silent:
|
619
|
+
print("Honeybee.ByHBJSONString - Error: The input string parameter is not a valid string. Returning None.")
|
620
|
+
return None
|
621
|
+
hbjson_dict = json.loads(string)
|
622
|
+
if not isinstance(hbjson_dict, dict):
|
623
|
+
if not silent:
|
624
|
+
print("Honeybee.ByHBJSONString - Error: Could not convert the input string into a valid HBJSON dictionary. Returning None.")
|
625
|
+
return None
|
626
|
+
return Honeybee.ByHBJSONDictionary(hbjson_dict,
|
627
|
+
includeRooms = includeRooms,
|
628
|
+
includeFaces = includeFaces,
|
629
|
+
includeShades = includeShades,
|
630
|
+
includeApertures = includeApertures,
|
631
|
+
includeDoors = includeDoors,
|
632
|
+
includeOrphanedRooms = includeOrphanedRooms,
|
633
|
+
includeOrphanedFaces = includeOrphanedFaces,
|
634
|
+
includeOrphanedShades = includeOrphanedShades,
|
635
|
+
includeOrphanedApertures = includeOrphanedApertures,
|
636
|
+
includeOrphanedDoors = includeOrphanedDoors,
|
637
|
+
tolerance = tolerance,
|
638
|
+
silent = silent)
|
639
|
+
|
115
640
|
@staticmethod
|
116
641
|
def ConstructionSetByIdentifier(id):
|
117
642
|
"""
|
@@ -175,7 +700,7 @@ class Honeybee:
|
|
175
700
|
path = path+".hbjson"
|
176
701
|
|
177
702
|
if not overwrite and exists(path):
|
178
|
-
print("
|
703
|
+
print("Honeybee.ExportToHBJSON - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
|
179
704
|
return None
|
180
705
|
f = None
|
181
706
|
try:
|
@@ -184,7 +709,7 @@ class Honeybee:
|
|
184
709
|
else:
|
185
710
|
f = open(path, "x") # Try to create a new File
|
186
711
|
except:
|
187
|
-
print("
|
712
|
+
print("Honeybee.ExportToHBJSON - Error: Could not create a new file at the following location: "+path+". Returning None.")
|
188
713
|
return None
|
189
714
|
if (f):
|
190
715
|
json.dump(model.to_dict(), f, indent=4)
|
topologicpy/Plotly.py
CHANGED
@@ -1447,8 +1447,10 @@ class Plotly:
|
|
1447
1447
|
----------
|
1448
1448
|
matrix : list or numpy.array
|
1449
1449
|
The matrix to display.
|
1450
|
-
|
1451
|
-
The list of categories to use on the X
|
1450
|
+
xCategories : list
|
1451
|
+
The list of categories to use on the X axis.
|
1452
|
+
yCategories : list
|
1453
|
+
The list of categories to use on the Y axis.
|
1452
1454
|
minValue : float , optional
|
1453
1455
|
The desired minimum value to use for the color scale. If set to None, the minmum value found in the input matrix will be used. Default is None.
|
1454
1456
|
maxValue : float , optional
|
topologicpy/Topology.py
CHANGED
@@ -8699,6 +8699,23 @@ class Topology():
|
|
8699
8699
|
if showFigure:
|
8700
8700
|
Plotly.Show(figure=figure, renderer=renderer, camera=camera, center=center, up=up, projection=projection)
|
8701
8701
|
return None
|
8702
|
+
if "ortho" in projection.lower():
|
8703
|
+
camera_settings = dict(eye=dict(x=camera[0], y=camera[1], z=camera[2]),
|
8704
|
+
center=dict(x=center[0], y=center[1], z=center[2]),
|
8705
|
+
up=dict(x=up[0], y=up[1], z=up[2]),
|
8706
|
+
projection=dict(type="orthographic"))
|
8707
|
+
else:
|
8708
|
+
camera_settings = dict(eye=dict(x=camera[0], y=camera[1], z=camera[2]),
|
8709
|
+
center=dict(x=center[0], y=center[1], z=center[2]),
|
8710
|
+
up=dict(x=up[0], y=up[1], z=up[2]),
|
8711
|
+
projection=dict(type="perspective"))
|
8712
|
+
|
8713
|
+
figure.update_layout(
|
8714
|
+
scene_camera = camera_settings,
|
8715
|
+
scene=dict(aspectmode="data"),
|
8716
|
+
autosize=True,
|
8717
|
+
margin=dict(l=40, r=40, t=40, b=40)
|
8718
|
+
)
|
8702
8719
|
return figure
|
8703
8720
|
|
8704
8721
|
@staticmethod
|
topologicpy/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = '0.8.
|
1
|
+
__version__ = '0.8.55'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: topologicpy
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.55
|
4
4
|
Summary: An AI-Powered Spatial Modelling and Analysis Software Library for Architecture, Engineering, and Construction.
|
5
5
|
Author-email: Wassim Jabi <wassim.jabi@gmail.com>
|
6
6
|
License: AGPL v3 License
|
@@ -2,7 +2,7 @@ topologicpy/ANN.py,sha256=gpflv4lFypOW789vO7mSkMLaMF_ZftVOCqCvtGr6-JA,47873
|
|
2
2
|
topologicpy/Aperture.py,sha256=wNn5miB_IrGCBYuQ18HXQYRva20dUC3id4AJCulL7to,2723
|
3
3
|
topologicpy/BVH.py,sha256=JA4bb-9hgMfVZ_syzmSmTL3ueCq-0vMUGMPZxNcawAY,13023
|
4
4
|
topologicpy/CSG.py,sha256=09la1-xzS9vr-WnV7tpJ0I-mkZ-XY0MRSd5iB50Nfgw,15556
|
5
|
-
topologicpy/Cell.py,sha256
|
5
|
+
topologicpy/Cell.py,sha256=ndrfovjwEEQTJ0B5DuthuCBDdVg9-1i_MX8Dtj-wD4E,177330
|
6
6
|
topologicpy/CellComplex.py,sha256=Kbz63rGeE08bJfMXFvB-AptoKHiaCK5OtiV1wz8Y-Fk,68081
|
7
7
|
topologicpy/Cluster.py,sha256=G49AuhJHQ1s819cB5MtVdmAGgkag19IC3dRP1ub1Wh4,58608
|
8
8
|
topologicpy/Color.py,sha256=hzSmgBWhiuYc55RSipkQNIgGtgyhC5BqY8AakNYEK-U,24486
|
@@ -10,29 +10,29 @@ topologicpy/Context.py,sha256=G3CwMvN8Jw2rnQRwB-n4MaQq_wLS0vPimbXKwsdMJ80,3055
|
|
10
10
|
topologicpy/DGL.py,sha256=O7r22ss0tgak4kWaACkyExhR_rZ46O_bqIBpxAcwJkw,138918
|
11
11
|
topologicpy/Dictionary.py,sha256=Z4YQ88tONWd-0X0dENQ8IZqIOa9mbBqhJkTBsHmft2g,44619
|
12
12
|
topologicpy/Edge.py,sha256=DifItuyabFDUFC7CVMlt2DeMFMNaGOqCg43iU9CPP0A,74029
|
13
|
-
topologicpy/EnergyModel.py,sha256=
|
13
|
+
topologicpy/EnergyModel.py,sha256=hB1aiJe45gdDMFm1AhkBr-1djjtXSzn24iRpQMk43-4,57749
|
14
14
|
topologicpy/Face.py,sha256=aX9EcR3JGbLITElhd25J0Z8m9U8KkmbYivGg3oZN-Uw,202296
|
15
|
-
topologicpy/Graph.py,sha256=
|
15
|
+
topologicpy/Graph.py,sha256=Oa0oOrPoOSUGL5fvJYHBH_r6kRZ944wk-P828GyAjk4,705757
|
16
16
|
topologicpy/Grid.py,sha256=3OsBMyHh4w8gpFOTMKHMNTpo62V0CwRNu5cwm87yDUA,18421
|
17
|
-
topologicpy/Helper.py,sha256=
|
18
|
-
topologicpy/Honeybee.py,sha256=
|
17
|
+
topologicpy/Helper.py,sha256=Nr6pyzl0sZm4Cu11wOqoYKu6yYal5N6A9jErXnaZBJc,31765
|
18
|
+
topologicpy/Honeybee.py,sha256=DzaG9wpkJdcDWcjOGXhuN5X0gCqypmZGBa1y5E2MkjU,48964
|
19
19
|
topologicpy/Matrix.py,sha256=bOofT34G3YHu9aMIWx60YHAJga4R0GbDjsZBUD4Hu_k,22706
|
20
20
|
topologicpy/Neo4j.py,sha256=J8jU_mr5-mWC0Lg_D2dMjMlx1rY_eh8ks_aubUuTdWw,22319
|
21
|
-
topologicpy/Plotly.py,sha256=
|
21
|
+
topologicpy/Plotly.py,sha256=kF7JwBMWJQAuGezaJYI6Cq7ErNwEtcKzaExOfdGPIMc,123003
|
22
22
|
topologicpy/Polyskel.py,sha256=oVfM4lqSMPTjnkHfsRU9VI8Blt6Vf0LVPkD9ebz7Wmw,27082
|
23
23
|
topologicpy/PyG.py,sha256=wOsoBFxMgwZYWjj86OMkz_PJuQ02locV_djhSDD6dVc,109644
|
24
24
|
topologicpy/ShapeGrammar.py,sha256=KYsKDLXWdflAcYMAIz84AUF-GMkbTmaBDd2-ovbilqU,23336
|
25
25
|
topologicpy/Shell.py,sha256=ioO4raCJfXtYldQg-adpcLVeJPEA6od6cAA5ro7t6r4,96792
|
26
26
|
topologicpy/Speckle.py,sha256=-eiTqJugd7pHiHpD3pDUcDO6CGhVyPV14HFRzaqEoaw,18187
|
27
27
|
topologicpy/Sun.py,sha256=8S6dhCKfOhUGVny-jEk87Q08anLYMB1JEBKRGCklvbQ,36670
|
28
|
-
topologicpy/Topology.py,sha256=
|
28
|
+
topologicpy/Topology.py,sha256=R5Ac3V_ADDRDZjhpcNvhM3AvDOLU6ORvB3yILyEkxnI,472559
|
29
29
|
topologicpy/Vector.py,sha256=pEC8YY3TeHGfGdeNgvdHjgMDwxGabp5aWjwYC1HSvMk,42236
|
30
30
|
topologicpy/Vertex.py,sha256=0f6HouARKaCuxhdxsUEYi8T9giJycnWhQ8Cn70YILBA,84885
|
31
31
|
topologicpy/Wire.py,sha256=gjgQUGHdBdXUIijgZc_VIW0E39w-smaVhhdl0jF63fQ,230466
|
32
32
|
topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
|
33
|
-
topologicpy/version.py,sha256=
|
34
|
-
topologicpy-0.8.
|
35
|
-
topologicpy-0.8.
|
36
|
-
topologicpy-0.8.
|
37
|
-
topologicpy-0.8.
|
38
|
-
topologicpy-0.8.
|
33
|
+
topologicpy/version.py,sha256=EKCMooHLmkTpTBR1XTlkHxj-YkXZosf7ysuCeCcjiR8,23
|
34
|
+
topologicpy-0.8.55.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
|
35
|
+
topologicpy-0.8.55.dist-info/METADATA,sha256=BIWmEWd275UZuMnl423fCxI3Bv6-vZpot8niPNApQAE,10535
|
36
|
+
topologicpy-0.8.55.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
37
|
+
topologicpy-0.8.55.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
|
38
|
+
topologicpy-0.8.55.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|