topologicpy 0.7.42__py3-none-any.whl → 0.7.43__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/Plotly.py CHANGED
@@ -994,8 +994,8 @@ class Plotly:
994
994
  minValue=None,
995
995
  maxValue=None,
996
996
  title="Confusion Matrix",
997
- xTitle = "Actual",
998
- yTitle = "Predicted",
997
+ xTitle = "Actual Categories",
998
+ yTitle = "Predicted Categories",
999
999
  width=950,
1000
1000
  height=500,
1001
1001
  showScale = True,
@@ -1022,9 +1022,9 @@ class Plotly:
1022
1022
  title : str , optional
1023
1023
  The desired title to display. The default is "Confusion Matrix".
1024
1024
  xTitle : str , optional
1025
- The desired X-axis title to display. The default is "Actual".
1025
+ The desired X-axis title to display. The default is "Actual Categories".
1026
1026
  yTitle : str , optional
1027
- The desired Y-axis title to display. The default is "Predicted".
1027
+ The desired Y-axis title to display. The default is "Predicted Categories".
1028
1028
  width : int , optional
1029
1029
  The desired width of the figure. The default is 950.
1030
1030
  height : int , optional
@@ -1051,7 +1051,11 @@ class Plotly:
1051
1051
  The desired top margin in pixels. The default is 40.
1052
1052
  marginBottom : int , optional
1053
1053
  The desired bottom margin in pixels. The default is 0.
1054
-
1054
+
1055
+ Returns
1056
+ -------
1057
+ plotly.Figure
1058
+ The created plotly figure.
1055
1059
  """
1056
1060
  try:
1057
1061
  import numpy as np
@@ -1265,7 +1269,158 @@ class Plotly:
1265
1269
  fig.update_xaxes( tickvals=xCategories)
1266
1270
  fig.update_yaxes( tickvals=yCategories)
1267
1271
  return fig
1272
+
1273
+ @staticmethod
1274
+ def FigureByCorrelation(actual,
1275
+ predicted,
1276
+ title="Correlation between Actual and Predicted Values",
1277
+ xTitle = "Actual Values",
1278
+ yTitle = "Predicted Values",
1279
+ dotColor = "blue",
1280
+ lineColor = "red",
1281
+ width=800,
1282
+ height=600,
1283
+ theme='default',
1284
+ backgroundColor='rgba(0,0,0,0)',
1285
+ marginLeft=0,
1286
+ marginRight=0,
1287
+ marginTop=40,
1288
+ marginBottom=0):
1289
+ """
1290
+ Returns a Plotly Figure showing the correlation between the input actual and predicted values. Actual values are displayed on the X-Axis, Predicted values are displayed on the Y-Axis.
1291
+
1292
+ Parameters
1293
+ ----------
1294
+ actual : list
1295
+ The actual values to display.
1296
+ predicted : list
1297
+ The predicted values to display.
1298
+ title : str , optional
1299
+ The desired title to display. The default is "Correlation between Actual and Predicted Values".
1300
+ xTitle : str , optional
1301
+ The desired X-axis title to display. The default is "Actual Values".
1302
+ yTitle : str , optional
1303
+ The desired Y-axis title to display. The default is "Predicted Values".
1304
+ dotColor : str , optional
1305
+ The desired color of the dots. This can be any plotly color string and may be specified as:
1306
+ - A hex string (e.g. '#ff0000')
1307
+ - An rgb/rgba string (e.g. 'rgb(255,0,0)')
1308
+ - An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
1309
+ - An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
1310
+ - A named CSS color.
1311
+ The default is 'blue'.
1312
+ lineColor : str , optional
1313
+ The desired color of the best fit line. This can be any plotly color string and may be specified as:
1314
+ - A hex string (e.g. '#ff0000')
1315
+ - An rgb/rgba string (e.g. 'rgb(255,0,0)')
1316
+ - An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
1317
+ - An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
1318
+ - A named CSS color.
1319
+ The default is 'red'.
1320
+ width : int , optional
1321
+ The desired width of the figure. The default is 800.
1322
+ height : int , optional
1323
+ The desired height of the figure. The default is 600.
1324
+ theme : str , optional
1325
+ The plotly color scheme to use. The options are "dark", "light", "default". The default is "default".
1326
+ backgroundColor : str , optional
1327
+ The desired background color. This can be any plotly color string and may be specified as:
1328
+ - A hex string (e.g. '#ff0000')
1329
+ - An rgb/rgba string (e.g. 'rgb(255,0,0)')
1330
+ - An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
1331
+ - An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
1332
+ - A named CSS color.
1333
+ The default is 'rgba(0,0,0,0)' (transparent).
1334
+ marginLeft : int , optional
1335
+ The desired left margin in pixels. The default is 0.
1336
+ marginRight : int , optional
1337
+ The desired right margin in pixels. The default is 0.
1338
+ marginTop : int , optional
1339
+ The desired top margin in pixels. The default is 40.
1340
+ marginBottom : int , optional
1341
+ The desired bottom margin in pixels. The default is 0.
1342
+
1343
+ Returns
1344
+ -------
1345
+ plotly.Figure
1346
+ The created plotly figure.
1268
1347
 
1348
+ """
1349
+
1350
+ import numpy as np
1351
+ import plotly.graph_objs as go
1352
+ from sklearn.linear_model import LinearRegression
1353
+ import plotly.io as pio
1354
+ actual_values = np.array(actual)
1355
+ predicted_values = np.array(predicted)
1356
+
1357
+ # Validate the theme input
1358
+ if theme == 'light':
1359
+ pio.templates.default = "plotly_white"
1360
+ backgroundColor='white'
1361
+ elif theme == 'dark':
1362
+ pio.templates.default = "plotly_dark"
1363
+ backgroundColor='black'
1364
+ else:
1365
+ pio.templates.default = None # Use default Plotly theme
1366
+
1367
+ # Calculate the best-fit line using linear regression
1368
+ regressor = LinearRegression()
1369
+ regressor.fit(np.array(actual_values).reshape(-1, 1), np.array(predicted_values))
1370
+ line = regressor.predict(np.array(actual_values).reshape(-1, 1))
1371
+
1372
+ # Determine the range and tick step
1373
+ combined_values = np.concatenate([actual_values, predicted_values])
1374
+ min_value = np.min(combined_values)
1375
+ max_value = np.max(combined_values)
1376
+ margin = 0.1 * (max_value - min_value)
1377
+ tick_range = [min_value - margin, max_value + margin]
1378
+ tick_step = (max_value - min_value) / 10 # Adjust as needed for a different number of ticks
1379
+
1380
+ # Create the scatter plot for actual vs predicted values
1381
+ scatter_trace = go.Scatter(
1382
+ x=actual_values,
1383
+ y=predicted_values,
1384
+ mode='markers',
1385
+ name='Actual vs. Predicted',
1386
+ marker=dict(color=dotColor)
1387
+ )
1388
+
1389
+ # Create the line of best fit
1390
+ line_trace = go.Scatter(
1391
+ x=actual_values,
1392
+ y=line,
1393
+ mode='lines',
1394
+ name='Best Fit Line',
1395
+ line=dict(color=lineColor)
1396
+ )
1397
+
1398
+ # Create the 45-degree line
1399
+ line_45_trace = go.Scatter(
1400
+ x=tick_range,
1401
+ y=tick_range,
1402
+ mode='lines',
1403
+ name='45-Degree Line',
1404
+ line=dict(color='green', dash='dash')
1405
+ )
1406
+
1407
+ # Combine the traces into a single figure
1408
+ layout = {
1409
+ "title": title,
1410
+ "width": width,
1411
+ "height": height,
1412
+ "xaxis": {"title": xTitle, "range":tick_range, "dtick":tick_step},
1413
+ "yaxis": {"title": yTitle, "range":tick_range, "dtick":tick_step},
1414
+ "showlegend": True,
1415
+ "paper_bgcolor": backgroundColor,
1416
+ "plot_bgcolor": backgroundColor,
1417
+ "margin":dict(l=marginLeft, r=marginRight, t=marginTop, b=marginBottom)
1418
+ }
1419
+
1420
+ fig = go.Figure(data=[scatter_trace, line_trace, line_45_trace], layout=layout)
1421
+
1422
+ return fig
1423
+
1269
1424
  @staticmethod
1270
1425
  def FigureByDataFrame(dataFrame,
1271
1426
  labels=[],
topologicpy/PyG.py CHANGED
@@ -73,10 +73,35 @@ class CustomGraphDataset(Dataset):
73
73
  else:
74
74
  edge_attr = None
75
75
 
76
+
77
+
76
78
  if self.graph_level:
77
- y = torch.tensor([self.graph_df[self.graph_df['graph_id'] == graph_id]['label'].values[0]], dtype=torch.long)
79
+ label_value = self.graph_df[self.graph_df['graph_id'] == graph_id]['label'].values[0]
80
+
81
+ # Check if the label is an integer or a float and cast accordingly
82
+ if isinstance(label_value, int):
83
+ y = torch.tensor([label_value], dtype=torch.long)
84
+ elif isinstance(label_value, float):
85
+ y = torch.tensor([label_value], dtype=torch.float)
86
+ else:
87
+ raise ValueError(f"Unexpected label type: {type(label_value)}. Expected int or float.")
88
+
78
89
  elif self.node_level:
79
- y = torch.tensor(graph_nodes['label'].values, dtype=torch.long)
90
+ label_values = graph_nodes['label'].values
91
+
92
+ # Check if the labels are integers or floats and cast accordingly
93
+ if issubclass(label_values.dtype.type, int):
94
+ y = torch.tensor(label_values, dtype=torch.long)
95
+ elif issubclass(label_values.dtype.type, float):
96
+ y = torch.tensor(label_values, dtype=torch.float)
97
+ else:
98
+ raise ValueError(f"Unexpected label types: {label_values.dtype}. Expected int or float.")
99
+
100
+
101
+ # if self.graph_level:
102
+ # y = torch.tensor([self.graph_df[self.graph_df['graph_id'] == graph_id]['label'].values[0]], dtype=torch.long)
103
+ # elif self.node_level:
104
+ # y = torch.tensor(graph_nodes['label'].values, dtype=torch.long)
80
105
 
81
106
  data = Data(x=x, edge_index=edge_index, y=y)
82
107
  if edge_attr is not None:
@@ -1411,6 +1436,82 @@ class PyG:
1411
1436
 
1412
1437
  return CustomGraphDataset(root=path, node_level=node_level, graph_level=graph_level, node_attr_key=nodeATTRKey, edge_attr_key=edgeATTRKey)
1413
1438
 
1439
+ @staticmethod
1440
+ def DatasetGraphLabels(dataset, graphLabelHeader="label"):
1441
+ """
1442
+ Returns the labels of the graphs in the input dataset
1443
+
1444
+ Parameters
1445
+ ----------
1446
+ dataset : CustomDataset
1447
+ The input dataset
1448
+ graphLabelHeader: str , optional
1449
+ The key string under which the graph labels are stored. The default is "label".
1450
+
1451
+ Returns
1452
+ -------
1453
+ list
1454
+ The list of graph labels.
1455
+ """
1456
+ import torch
1457
+
1458
+ graph_labels = []
1459
+ for g in dataset:
1460
+ # Get the label of the graph
1461
+ label = g.y
1462
+ graph_labels.append(label.item())
1463
+ return graph_labels
1464
+
1465
+ @staticmethod
1466
+ def DatasetSplit(dataset, split=[0.8,0.1,0.1], shuffle=True, randomState=42):
1467
+ """
1468
+ Splits the dataset into three subsets.
1469
+
1470
+ Parameters
1471
+ ----------
1472
+ dataset : CustomDataset
1473
+ The input dataset
1474
+ split: list , optional
1475
+ The list of ratios. This list must be made out of three numbers adding to 1.
1476
+ shuffle: boolean , optional
1477
+ If set to True, the subsets are created from random indices. Otherwise, they are split sequentially. The default is True.
1478
+ randomState : int , optional
1479
+ The random seed to use for reproducibility. The default is 42.
1480
+
1481
+ Returns
1482
+ -------
1483
+ list
1484
+ The list of three subset datasets.
1485
+ """
1486
+
1487
+ import torch
1488
+ from torch.utils.data import random_split, Subset
1489
+ train_ratio, val_ratio, test_ratio = split
1490
+ assert abs(train_ratio + val_ratio + test_ratio - 1.0) < 1e-6, "Ratios must add up to 1."
1491
+
1492
+ # Calculate the number of samples for each split
1493
+ dataset_len = len(dataset)
1494
+ train_len = int(train_ratio * dataset_len)
1495
+ val_len = int(val_ratio * dataset_len)
1496
+ test_len = dataset_len - train_len - val_len # Ensure it adds up correctly
1497
+
1498
+ ## Generate indices for the split
1499
+ indices = list(range(dataset_len))
1500
+ if shuffle:
1501
+ torch.manual_seed(randomState) # For reproducibility
1502
+ indices = torch.randperm(dataset_len).tolist() # Shuffled indices
1503
+
1504
+ # Create splits
1505
+ train_indices = indices[:train_len]
1506
+ val_indices = indices[train_len:train_len + val_len]
1507
+ test_indices = indices[train_len + val_len:train_len + val_len + test_len]
1508
+
1509
+ train_dataset = Subset(dataset, train_indices)
1510
+ val_dataset = Subset(dataset, val_indices)
1511
+ test_dataset = Subset(dataset, test_indices)
1512
+
1513
+ return train_dataset, val_dataset, test_dataset
1514
+
1414
1515
  @staticmethod
1415
1516
  def Optimizer(name="Adam", amsgrad=True, betas=(0.9,0.999), eps=0.000001, lr=0.001, maximize=False, weightDecay=0.0, rho=0.9, lr_decay=0.0):
1416
1517
  """
@@ -1529,11 +1630,11 @@ class PyG:
1529
1630
  ----------
1530
1631
  hparams : HParams
1531
1632
  The input hyperparameters
1532
- trainingDataset : DGLDataset
1633
+ trainingDataset : CustomDataset
1533
1634
  The input training dataset.
1534
- validationDataset : DGLDataset
1635
+ validationDataset : CustomDataset
1535
1636
  The input validation dataset. If not specified, a portion of the trainingDataset will be used for validation according the to the split list as specified in the hyper-parameters.
1536
- testingDataset : DGLDataset
1637
+ testingDataset : CustomDataset
1537
1638
  The input testing dataset. If not specified, a portion of the trainingDataset will be used for testing according the to the split list as specified in the hyper-parameters.
1538
1639
 
1539
1640
  Returns
@@ -1635,13 +1736,13 @@ class PyG:
1635
1736
  import os
1636
1737
 
1637
1738
  if model == None:
1638
- print("DGL.ModelSave - Error: The input model parameter is invalid. Returning None.")
1739
+ print("PyG.ModelSave - Error: The input model parameter is invalid. Returning None.")
1639
1740
  return None
1640
1741
  if path == None:
1641
- print("DGL.ModelSave - Error: The input path parameter is invalid. Returning None.")
1742
+ print("PyG.ModelSave - Error: The input path parameter is invalid. Returning None.")
1642
1743
  return None
1643
1744
  if not overwrite and os.path.exists(path):
1644
- print("DGL.ModelSave - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
1745
+ print("PyG.ModelSave - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
1645
1746
  return None
1646
1747
  if overwrite and os.path.exists(path):
1647
1748
  os.remove(path)
@@ -1893,7 +1994,7 @@ class PyG:
1893
1994
  from sklearn import metrics
1894
1995
  from sklearn.metrics import accuracy_score
1895
1996
  except:
1896
- print("DGL - Installing required scikit-learn (sklearn) library.")
1997
+ print("PyG - Installing required scikit-learn (sklearn) library.")
1897
1998
  try:
1898
1999
  os.system("pip install scikit-learn")
1899
2000
  except:
@@ -1901,19 +2002,19 @@ class PyG:
1901
2002
  try:
1902
2003
  from sklearn import metrics
1903
2004
  from sklearn.metrics import accuracy_score
1904
- print("DGL - scikit-learn (sklearn) library installed correctly.")
2005
+ print("PyG - scikit-learn (sklearn) library installed correctly.")
1905
2006
  except:
1906
- warnings.warn("DGL - Error: Could not import scikit-learn (sklearn). Please try to install scikit-learn manually. Returning None.")
2007
+ warnings.warn("PyG - Error: Could not import scikit-learn (sklearn). Please try to install scikit-learn manually. Returning None.")
1907
2008
  return None
1908
2009
 
1909
2010
  if not isinstance(actual, list):
1910
- print("DGL.ConfusionMatrix - ERROR: The actual input is not a list. Returning None")
2011
+ print("PyG.ConfusionMatrix - ERROR: The actual input is not a list. Returning None")
1911
2012
  return None
1912
2013
  if not isinstance(predicted, list):
1913
- print("DGL.ConfusionMatrix - ERROR: The predicted input is not a list. Returning None")
2014
+ print("PyG.ConfusionMatrix - ERROR: The predicted input is not a list. Returning None")
1914
2015
  return None
1915
2016
  if len(actual) != len(predicted):
1916
- print("DGL.ConfusionMatrix - ERROR: The two input lists do not have the same length. Returning None")
2017
+ print("PyG.ConfusionMatrix - ERROR: The two input lists do not have the same length. Returning None")
1917
2018
  return None
1918
2019
  if normalize:
1919
2020
  cm = np.transpose(metrics.confusion_matrix(y_true=actual, y_pred=predicted, normalize="true"))
@@ -2065,3 +2166,58 @@ class PyG:
2065
2166
  size = len(predicted)
2066
2167
 
2067
2168
  return {"mse": mse, "size": size}
2169
+
2170
+ @staticmethod
2171
+ def Performance(actual, predicted, mantissa: int = 6):
2172
+ """
2173
+ Computes regression model performance measures. This is to be used only with regression not with classification.
2174
+
2175
+ Parameters
2176
+ ----------
2177
+ actual : list
2178
+ The input list of actual values.
2179
+ predicted : list
2180
+ The input list of predicted values.
2181
+ mantissa : int , optional
2182
+ The desired length of the mantissa. The default is 6.
2183
+
2184
+ Returns
2185
+ -------
2186
+ dict
2187
+ The dictionary containing the performance measures. The keys in the dictionary are: 'mae', 'mape', 'mse', 'r', 'r2', 'rmse'.
2188
+ """
2189
+
2190
+ if not isinstance(actual, list):
2191
+ print("PyG.Performance - ERROR: The actual input is not a list. Returning None")
2192
+ return None
2193
+ if not isinstance(predicted, list):
2194
+ print("PyG.Performance - ERROR: The predicted input is not a list. Returning None")
2195
+ return None
2196
+ if not (len(actual) == len(predicted)):
2197
+ print("PyG.Performance - ERROR: The actual and predicted input lists have different lengths. Returning None")
2198
+ return None
2199
+
2200
+ predicted = np.array(predicted)
2201
+ actual = np.array(actual)
2202
+
2203
+ mae = np.mean(np.abs(predicted - actual))
2204
+ mape = np.mean(np.abs((actual - predicted) / actual))*100
2205
+ mse = np.mean((predicted - actual)**2)
2206
+ correlation_matrix = np.corrcoef(predicted, actual)
2207
+ r = correlation_matrix[0, 1]
2208
+ r2 = r**2
2209
+ absolute_errors = np.abs(predicted - actual)
2210
+ mean_actual = np.mean(actual)
2211
+ if mean_actual == 0:
2212
+ rae = None
2213
+ else:
2214
+ rae = np.mean(absolute_errors) / mean_actual
2215
+ rmse = np.sqrt(mse)
2216
+ return {'mae': round(mae, mantissa),
2217
+ 'mape': round(mape, mantissa),
2218
+ 'mse': round(mse, mantissa),
2219
+ 'r': round(r, mantissa),
2220
+ 'r2': round(r2, mantissa),
2221
+ 'rae': round(rae, mantissa),
2222
+ 'rmse': round(rmse, mantissa)
2223
+ }
topologicpy/Shell.py CHANGED
@@ -1056,6 +1056,110 @@ class Shell():
1056
1056
  """
1057
1057
  return shell.IsClosed()
1058
1058
 
1059
+
1060
+ @staticmethod
1061
+ def ParabolicSurface(origin= None, focalLength=0.125, width: float = 1, length: float = 1, uSides: int = 16, vSides: int = 16,
1062
+ direction: list = [0, 0, 1], placement: str ="center", mantissa: int = 6, tolerance: float = 0.0001):
1063
+ """
1064
+ Creates a parabolic surface.
1065
+
1066
+ Parameters
1067
+ ----------
1068
+ origin : topologic_core.Vertex , optional
1069
+ The origin location of the parabolic surface. The default is None which results in the parabolic surface being placed at (0, 0, 0).
1070
+ focalLength : float , optional
1071
+ The focal length of the parabola. The default is 1.
1072
+ width : float , optional
1073
+ The width of the parabolic surface. The default is 1.
1074
+ length : float , optional
1075
+ The length of the parabolic surface. The default is 1.
1076
+ uSides : int , optional
1077
+ The number of sides along the width. The default is 16.
1078
+ vSides : int , optional
1079
+ The number of sides along the length. The default is 16.
1080
+ direction : list , optional
1081
+ The vector representing the up direction of the parabolic surface. The default is [0, 0, 1].
1082
+ placement : str , optional
1083
+ The description of the placement of the origin of the parabolic surface. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
1084
+ mantissa : int , optional
1085
+ The desired length of the mantissa. The default is 6.
1086
+ tolerance : float , optional
1087
+ The desired tolerance. The default is 0.0001.
1088
+
1089
+ Returns
1090
+ -------
1091
+ topologic_core.Shell
1092
+ The created parabolic surface.
1093
+
1094
+ """
1095
+ from topologicpy.Vertex import Vertex
1096
+ from topologicpy.Wire import Wire
1097
+ from topologicpy.Face import Face
1098
+ from topologicpy.Topology import Topology
1099
+
1100
+ def create_triangulated_mesh(vertices, uSides, vSides):
1101
+ faces = []
1102
+
1103
+ # Iterate over the grid of vertices to form triangular faces
1104
+ for i in range(uSides - 1):
1105
+ for j in range(vSides - 1):
1106
+ # Get the indices of the vertices forming the current grid cell
1107
+ v1 = vertices[i * vSides + j]
1108
+ v2 = vertices[i * vSides + (j + 1)]
1109
+ v3 = vertices[(i + 1) * vSides + j]
1110
+ v4 = vertices[(i + 1) * vSides + (j + 1)]
1111
+
1112
+ # Create two triangles for each grid cell
1113
+ # Triangle 1: (v1, v2, v3)
1114
+ wire1 = Wire.ByVertices([v1, v2, v3])
1115
+ face1 = Face.ByWire(wire1)
1116
+ faces.append(face1)
1117
+
1118
+ # Triangle 2: (v3, v2, v4)
1119
+ wire2 = Wire.ByVertices([v3, v2, v4])
1120
+ face2 = Face.ByWire(wire2)
1121
+ faces.append(face2)
1122
+
1123
+ # Create the mesh (Shell) from the list of faces
1124
+ mesh = Shell.ByFaces(faces)
1125
+ return mesh
1126
+
1127
+ if not Topology.IsInstance(origin, "Vertex"):
1128
+ origin = Vertex.Origin()
1129
+
1130
+ x_range = [-width*0.5, width*0.5]
1131
+ y_range = [-length*0.5, length*0.5]
1132
+ # Generate x and y values
1133
+ x_values = [x_range[0] + i * (x_range[1] - x_range[0]) / (uSides - 1) for i in range(uSides)]
1134
+ y_values = [y_range[0] + i * (y_range[1] - y_range[0]) / (vSides - 1) for i in range(vSides)]
1135
+
1136
+ # Create the grid and calculate Z values
1137
+ vertices = []
1138
+
1139
+ for x in x_values:
1140
+ for y in y_values:
1141
+ z = ((x)**2 + (y)**2) / (4 * focalLength)
1142
+ vertices.append(Vertex.ByCoordinates(x, y, z))
1143
+
1144
+ mesh = create_triangulated_mesh(vertices=vertices, uSides=uSides, vSides=vSides)
1145
+ if not placement.lower() == "bottom":
1146
+ x_list = [Vertex.X(v) for v in vertices]
1147
+ y_list = [Vertex.Y(v) for v in vertices]
1148
+ z_list = [Vertex.Z(v) for v in vertices]
1149
+ x_list.sort()
1150
+ y_list.sort()
1151
+ z_list.sort()
1152
+ width = abs(x_list[-1] - x_list[0])
1153
+ length = abs(y_list[-1] - y_list[0])
1154
+ height = abs(z_list[-1] - z_list[0])
1155
+ if placement.lower() == "center":
1156
+ mesh = Topology.Translate(mesh, 0, 0, -height*0.5)
1157
+ elif placement.lower() == "lowerleft":
1158
+ mesh = Topology.Translate(mesh, width*0.5, length*0.5, 0)
1159
+
1160
+ mesh = Topology.Orient(mesh, origin=origin, dirA=[0, 0, 1], dirB=direction, tolerance=tolerance)
1161
+ return mesh
1162
+
1059
1163
  @staticmethod
1060
1164
  def Pie(origin= None, radiusA: float = 0.5, radiusB: float = 0.0, sides: int = 32, rings: int = 1, fromAngle: float = 0.0, toAngle: float = 360.0, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
1061
1165
  """
topologicpy/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.7.42'
1
+ __version__ = '0.7.43'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: topologicpy
3
- Version: 0.7.42
3
+ Version: 0.7.43
4
4
  Summary: An Advanced Spatial Modelling and Analysis Software Library for Architecture, Engineering, and Construction.
5
5
  Author-email: Wassim Jabi <wassim.jabi@gmail.com>
6
6
  License: MIT License
@@ -16,10 +16,10 @@ topologicpy/Helper.py,sha256=i-AfI29NMsZXBaymjilfvxQbuS3wpYbpPw4RWu1YCHs,16358
16
16
  topologicpy/Honeybee.py,sha256=vcBECJlgWVjNNdD9ZmjNik_pA1Y_ZNoOorsQb2CiyGA,21965
17
17
  topologicpy/Matrix.py,sha256=umgR7An919-wGInXJ1wpqnoQ2jCPdyMe2rcWTZ16upk,8079
18
18
  topologicpy/Neo4j.py,sha256=YvtF7RYUMATEZ8iHwFwK_MOxEDyARby2DTI2CCK6-cI,19694
19
- topologicpy/Plotly.py,sha256=qMhBMAYoNMsc-cKdNpqM2p9rqAVXWvBNTzmTKw7iU_0,98963
19
+ topologicpy/Plotly.py,sha256=U6Lo7hyDoStRKQXqlhb2LM-rR_bfBulxetRT2wMBmhI,105391
20
20
  topologicpy/Polyskel.py,sha256=EFsuh2EwQJGPLiFUjvtXmAwdX-A4r_DxP5hF7Qd3PaU,19829
21
- topologicpy/PyG.py,sha256=mDEYYGKv-q1B7GI_J7b3pAbJhF0hqMnITZNMzhHycg4,96097
22
- topologicpy/Shell.py,sha256=etLWt2VsKOYE-2N0InKdQxLDHEZWsp59DeToSggrjF0,79914
21
+ topologicpy/PyG.py,sha256=Dd0fiEbM_KR-sEHKCodmURBFFvckB6j7x4oBcCC5Q2o,102171
22
+ topologicpy/Shell.py,sha256=NZyHYTvV0pXx18AoMbVs8y-7if8teETItv5ZOJq6gzE,84672
23
23
  topologicpy/Speckle.py,sha256=rUS6PCaxIjEF5_fUruxvMH47FMKg-ohcoU0qAUb-yNM,14267
24
24
  topologicpy/Sun.py,sha256=InnKtX8eKwtAgcScuABH6yp0ljmWh5m_fDR4-n3jJMY,36869
25
25
  topologicpy/Topology.py,sha256=5KGQ8jSiURMk9syoMLc3mrIONCfj_pfvCfHTKgPcLOM,366047
@@ -27,9 +27,9 @@ topologicpy/Vector.py,sha256=WQQUbwrg7VKImtxuBUi2i-FRiPT77WlrzLP05gdXKM8,33079
27
27
  topologicpy/Vertex.py,sha256=EQdVYHmW85_pZdHZB3N8pEi0GiadCCkF3z_oqohA7B0,71161
28
28
  topologicpy/Wire.py,sha256=9EJE0Iq3nGz5X7Suy6xxjmuOpfV49By6WH98UAL_7m4,153532
29
29
  topologicpy/__init__.py,sha256=D7ky87CAQMiS2KE6YLvcTLkTgA2PY7rASe6Z23pjp9k,872
30
- topologicpy/version.py,sha256=UWZPE9jgOKgWiu1NjdF3GjtgYjFTqvDvq_t-y5kjb-s,23
31
- topologicpy-0.7.42.dist-info/LICENSE,sha256=BRNw73R2WdDBICtwhI3wm3cxsaVqLTAGuRwrTltcfxs,1068
32
- topologicpy-0.7.42.dist-info/METADATA,sha256=NSPc5BzWU_sOnE--u-EnHbDLze91ki3OV6LRtAyEc1k,10916
33
- topologicpy-0.7.42.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
34
- topologicpy-0.7.42.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
35
- topologicpy-0.7.42.dist-info/RECORD,,
30
+ topologicpy/version.py,sha256=7JOl9BjjPK0ib5M5rAuj9I5xjEoYF6YsFAS_dvAYEzg,23
31
+ topologicpy-0.7.43.dist-info/LICENSE,sha256=BRNw73R2WdDBICtwhI3wm3cxsaVqLTAGuRwrTltcfxs,1068
32
+ topologicpy-0.7.43.dist-info/METADATA,sha256=NCprd1lCZf1afHBHxA5uol5btFYvJFZeiIdoIJtQ5aI,10916
33
+ topologicpy-0.7.43.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
34
+ topologicpy-0.7.43.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
35
+ topologicpy-0.7.43.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (72.1.0)
2
+ Generator: setuptools (72.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5