topologicpy 0.7.71__py3-none-any.whl → 0.7.73__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/BVH.py +9 -7
- topologicpy/CellComplex.py +92 -23
- topologicpy/Cluster.py +1 -1
- topologicpy/Color.py +67 -1
- topologicpy/Dictionary.py +108 -10
- topologicpy/Edge.py +8 -4
- topologicpy/Face.py +1 -1
- topologicpy/Graph.py +114 -6
- topologicpy/Helper.py +111 -0
- topologicpy/Plotly.py +505 -344
- topologicpy/Shell.py +24 -24
- topologicpy/Topology.py +234 -117
- topologicpy/Wire.py +50 -56
- topologicpy/version.py +1 -1
- {topologicpy-0.7.71.dist-info → topologicpy-0.7.73.dist-info}/METADATA +1 -1
- topologicpy-0.7.73.dist-info/RECORD +36 -0
- {topologicpy-0.7.71.dist-info → topologicpy-0.7.73.dist-info}/WHEEL +1 -1
- topologicpy-0.7.71.dist-info/RECORD +0 -36
- {topologicpy-0.7.71.dist-info → topologicpy-0.7.73.dist-info}/LICENSE +0 -0
- {topologicpy-0.7.71.dist-info → topologicpy-0.7.73.dist-info}/top_level.txt +0 -0
topologicpy/BVH.py
CHANGED
@@ -41,6 +41,8 @@ class BVH:
|
|
41
41
|
|
42
42
|
def intersects(self, other):
|
43
43
|
# Check if this AABB intersects with another AABB
|
44
|
+
if other == None:
|
45
|
+
return False
|
44
46
|
return np.all(self.min_point <= other.max_point) and np.all(self.max_point >= other.min_point)
|
45
47
|
|
46
48
|
def contains(self, point):
|
@@ -200,13 +202,13 @@ class BVH:
|
|
200
202
|
cluster = Cluster.ByTopologies(vertices)
|
201
203
|
bb = Topology.BoundingBox(cluster)
|
202
204
|
d = Topology.Dictionary(bb)
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
query_aabb = BVH.AABB(min_point=(
|
205
|
+
x_min = Dictionary.ValueAtKey(d, "xmin")
|
206
|
+
y_min = Dictionary.ValueAtKey(d, "ymin")
|
207
|
+
z_min = Dictionary.ValueAtKey(d, "zmin")
|
208
|
+
x_max = Dictionary.ValueAtKey(d, "zmax")
|
209
|
+
y_max = Dictionary.ValueAtKey(d, "ymax")
|
210
|
+
z_max = Dictionary.ValueAtKey(d, "zmax")
|
211
|
+
query_aabb = BVH.AABB(min_point=(x_min, y_min, z_min), max_point=(x_max, y_max, z_max))
|
210
212
|
return query_aabb
|
211
213
|
|
212
214
|
def Clashes(bvh, query):
|
topologicpy/CellComplex.py
CHANGED
@@ -103,6 +103,8 @@ class CellComplex():
|
|
103
103
|
If set to True, any dictionaries in the cells are transferred to the CellComplex. Otherwise, they are not. The default is False.
|
104
104
|
tolerance : float , optional
|
105
105
|
The desired tolerance. The default is 0.0001.
|
106
|
+
silent : bool , optional
|
107
|
+
If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
|
106
108
|
|
107
109
|
Returns
|
108
110
|
-------
|
@@ -192,7 +194,7 @@ class CellComplex():
|
|
192
194
|
return CellComplex.ByCells(cells, tolerance)
|
193
195
|
|
194
196
|
@staticmethod
|
195
|
-
def ByFaces(faces: list, tolerance: float = 0.0001):
|
197
|
+
def ByFaces(faces: list, tolerance: float = 0.0001, silent: bool = False):
|
196
198
|
"""
|
197
199
|
Creates a cellcomplex by merging the input faces.
|
198
200
|
|
@@ -202,6 +204,8 @@ class CellComplex():
|
|
202
204
|
The input faces.
|
203
205
|
tolerance : float , optional
|
204
206
|
The desired tolerance. The default is 0.0001.
|
207
|
+
silent : bool , optional
|
208
|
+
If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
|
205
209
|
|
206
210
|
Returns
|
207
211
|
-------
|
@@ -213,38 +217,45 @@ class CellComplex():
|
|
213
217
|
from topologicpy.Topology import Topology
|
214
218
|
|
215
219
|
if not isinstance(faces, list):
|
216
|
-
|
220
|
+
if not silent:
|
221
|
+
print("CellComplex.ByFaces - Error: The input faces parameter is not a valid list. Returning None.")
|
217
222
|
return None
|
218
223
|
faces = [x for x in faces if Topology.IsInstance(x, "Face")]
|
219
224
|
if len(faces) < 1:
|
220
|
-
|
225
|
+
if not silent:
|
226
|
+
print("CellComplex.ByFaces - Error: The input faces parameter does not contain any valid faces. Returning None.")
|
221
227
|
return None
|
222
228
|
try:
|
223
229
|
cellComplex = topologic.CellComplex.ByFaces(faces, tolerance, False) # Hook to Core
|
224
230
|
except:
|
225
231
|
cellComplex = None
|
226
232
|
if not cellComplex:
|
227
|
-
|
233
|
+
if not silent:
|
234
|
+
print("CellComplex.ByFaces - Warning: The default method failed. Attempting a workaround.")
|
228
235
|
cellComplex = faces[0]
|
229
236
|
for i in range(1,len(faces)):
|
230
237
|
newCellComplex = None
|
231
238
|
try:
|
232
239
|
newCellComplex = cellComplex.Merge(faces[i], False, tolerance)
|
233
240
|
except:
|
234
|
-
|
241
|
+
if not silent:
|
242
|
+
print("CellComplex.ByFaces - Warning: Failed to merge face #"+str(i)+". Skipping.")
|
235
243
|
if newCellComplex:
|
236
244
|
cellComplex = newCellComplex
|
237
245
|
if not Topology.Type(cellComplex) == Topology.TypeID("CellComplex"):
|
238
|
-
|
246
|
+
if not silent:
|
247
|
+
print("CellComplex.ByFaces - Warning: The input faces do not form a cellcomplex")
|
239
248
|
if Topology.Type(cellComplex) == Topology.TypeID("Cluster"):
|
240
249
|
returnCellComplexes = Cluster.CellComplexes(cellComplex)
|
241
250
|
if len(returnCellComplexes) > 0:
|
242
251
|
return returnCellComplexes[0]
|
243
252
|
else:
|
244
|
-
|
253
|
+
if not silent:
|
254
|
+
print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
|
245
255
|
return None
|
246
256
|
else:
|
247
|
-
|
257
|
+
if not silent:
|
258
|
+
print("CellComplex.ByFaces - Error: Could not create a cellcomplex. Returning None.")
|
248
259
|
return None
|
249
260
|
else:
|
250
261
|
return cellComplex
|
@@ -892,33 +903,33 @@ class CellComplex():
|
|
892
903
|
x.append(Vertex.X(aVertex, mantissa=mantissa))
|
893
904
|
y.append(Vertex.Y(aVertex, mantissa=mantissa))
|
894
905
|
z.append(Vertex.Z(aVertex, mantissa=mantissa))
|
895
|
-
|
896
|
-
|
897
|
-
|
906
|
+
x_min = min(x)
|
907
|
+
y_min = min(y)
|
908
|
+
z_min = min(z)
|
898
909
|
maxX = max(x)
|
899
910
|
maxY = max(y)
|
900
911
|
maxZ = max(z)
|
901
|
-
return [
|
912
|
+
return [x_min, y_min, z_min, maxX, maxY, maxZ]
|
902
913
|
|
903
914
|
def slice(topology, uSides, vSides, wSides):
|
904
|
-
|
905
|
-
centroid = Vertex.ByCoordinates(
|
906
|
-
wOrigin = Vertex.ByCoordinates(Vertex.X(centroid, mantissa=mantissa), Vertex.Y(centroid, mantissa=mantissa),
|
907
|
-
wFace = Face.Rectangle(origin=wOrigin, width=(maxX-
|
915
|
+
x_min, y_min, z_min, maxX, maxY, maxZ = bb(topology)
|
916
|
+
centroid = Vertex.ByCoordinates(x_min+(maxX-x_min)*0.5, y_min+(maxY-y_min)*0.5, z_min+(maxZ-z_min)*0.5)
|
917
|
+
wOrigin = Vertex.ByCoordinates(Vertex.X(centroid, mantissa=mantissa), Vertex.Y(centroid, mantissa=mantissa), z_min)
|
918
|
+
wFace = Face.Rectangle(origin=wOrigin, width=(maxX-x_min)*1.1, length=(maxY-y_min)*1.1)
|
908
919
|
wFaces = []
|
909
|
-
wOffset = (maxZ-
|
920
|
+
wOffset = (maxZ-z_min)/wSides
|
910
921
|
for i in range(wSides-1):
|
911
922
|
wFaces.append(Topology.Translate(wFace, 0,0,wOffset*(i+1)))
|
912
|
-
uOrigin = Vertex.ByCoordinates(
|
913
|
-
uFace = Face.Rectangle(origin=uOrigin, width=(maxZ-
|
923
|
+
uOrigin = Vertex.ByCoordinates(x_min, Vertex.Y(centroid, mantissa=mantissa), Vertex.Z(centroid, mantissa=mantissa))
|
924
|
+
uFace = Face.Rectangle(origin=uOrigin, width=(maxZ-z_min)*1.1, length=(maxY-y_min)*1.1, direction=[1,0,0])
|
914
925
|
uFaces = []
|
915
|
-
uOffset = (maxX-
|
926
|
+
uOffset = (maxX-x_min)/uSides
|
916
927
|
for i in range(uSides-1):
|
917
928
|
uFaces.append(Topology.Translate(uFace, uOffset*(i+1),0,0))
|
918
|
-
vOrigin = Vertex.ByCoordinates(Vertex.X(centroid, mantissa=mantissa),
|
919
|
-
vFace = Face.Rectangle(origin=vOrigin, width=(maxX-
|
929
|
+
vOrigin = Vertex.ByCoordinates(Vertex.X(centroid, mantissa=mantissa), y_min, Vertex.Z(centroid, mantissa=mantissa))
|
930
|
+
vFace = Face.Rectangle(origin=vOrigin, width=(maxX-x_min)*1.1, length=(maxZ-z_min)*1.1, direction=[0,1,0])
|
920
931
|
vFaces = []
|
921
|
-
vOffset = (maxY-
|
932
|
+
vOffset = (maxY-y_min)/vSides
|
922
933
|
for i in range(vSides-1):
|
923
934
|
vFaces.append(Topology.Translate(vFace, 0,vOffset*(i+1),0))
|
924
935
|
all_faces = uFaces+vFaces+wFaces
|
@@ -995,6 +1006,64 @@ class CellComplex():
|
|
995
1006
|
shells = Topology.Shells(cellComplex)
|
996
1007
|
return shells
|
997
1008
|
|
1009
|
+
@staticmethod
|
1010
|
+
def Torus(origin= None, majorRadius: float = 0.5, minorRadius: float = 0.125, uSides: int = 16, vSides: int = 8, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
|
1011
|
+
"""
|
1012
|
+
Creates a torus.
|
1013
|
+
|
1014
|
+
Parameters
|
1015
|
+
----------
|
1016
|
+
origin : topologic_core.Vertex , optional
|
1017
|
+
The origin location of the torus. The default is None which results in the torus being placed at (0, 0, 0).
|
1018
|
+
majorRadius : float , optional
|
1019
|
+
The major radius of the torus. The default is 0.5.
|
1020
|
+
minorRadius : float , optional
|
1021
|
+
The minor radius of the torus. The default is 0.1.
|
1022
|
+
uSides : int , optional
|
1023
|
+
The number of sides along the longitude of the torus. The default is 16.
|
1024
|
+
vSides : int , optional
|
1025
|
+
The number of sides along the latitude of the torus. The default is 8.
|
1026
|
+
direction : list , optional
|
1027
|
+
The vector representing the up direction of the torus. The default is [0, 0, 1].
|
1028
|
+
placement : str , optional
|
1029
|
+
The description of the placement of the origin of the torus. This can be "bottom", "center", or "lowerleft". It is case insensitive. The default is "center".
|
1030
|
+
tolerance : float , optional
|
1031
|
+
The desired tolerance. The default is 0.0001.
|
1032
|
+
|
1033
|
+
Returns
|
1034
|
+
-------
|
1035
|
+
topologic_core.Cell
|
1036
|
+
The created torus.
|
1037
|
+
|
1038
|
+
"""
|
1039
|
+
|
1040
|
+
from topologicpy.Vertex import Vertex
|
1041
|
+
from topologicpy.Wire import Wire
|
1042
|
+
from topologicpy.Face import Face
|
1043
|
+
from topologicpy.Cell import Cell
|
1044
|
+
from topologicpy.Topology import Topology
|
1045
|
+
|
1046
|
+
if not Topology.IsInstance(origin, "Vertex"):
|
1047
|
+
origin = Vertex.ByCoordinates(0, 0, 0)
|
1048
|
+
if not Topology.IsInstance(origin, "Vertex"):
|
1049
|
+
print("Cell.Torus - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
|
1050
|
+
return None
|
1051
|
+
c = Wire.Circle(origin=Vertex.Origin(), radius=minorRadius, sides=vSides, fromAngle=0, toAngle=360, close=False, direction=[0, 1, 0], placement="center")
|
1052
|
+
c = Face.ByWire(c)
|
1053
|
+
c = Topology.Translate(c, abs(majorRadius-minorRadius), 0, 0)
|
1054
|
+
torus = Topology.Spin(c, origin=Vertex.Origin(), triangulate=False, direction=[0, 0, 1], angle=360, sides=uSides, tolerance=tolerance)
|
1055
|
+
if Topology.Type(torus) == Topology.TypeID("Shell"):
|
1056
|
+
faces = Topology.Faces(torus)
|
1057
|
+
torus = CellComplex.ByFaces(faces)
|
1058
|
+
if placement.lower() == "bottom":
|
1059
|
+
torus = Topology.Translate(torus, 0, 0, minorRadius)
|
1060
|
+
elif placement.lower() == "lowerleft":
|
1061
|
+
torus = Topology.Translate(torus, majorRadius, majorRadius, minorRadius)
|
1062
|
+
|
1063
|
+
torus = Topology.Orient(torus, origin=Vertex.Origin(), dirA=[0, 0, 1], dirB=direction)
|
1064
|
+
torus = Topology.Place(torus, originA=Vertex.Origin(), originB=origin)
|
1065
|
+
return torus
|
1066
|
+
|
998
1067
|
@staticmethod
|
999
1068
|
def Vertices(cellComplex) -> list:
|
1000
1069
|
"""
|
topologicpy/Cluster.py
CHANGED
@@ -166,7 +166,7 @@ class Cluster():
|
|
166
166
|
from topologicpy.Topology import Topology
|
167
167
|
from topologicpy.Helper import Helper
|
168
168
|
import inspect
|
169
|
-
|
169
|
+
|
170
170
|
if len(args) == 0:
|
171
171
|
if not silent:
|
172
172
|
print("Cluster.ByTopologies - Error: The input topologies parameter is an empty list. Returning None.")
|
topologicpy/Color.py
CHANGED
@@ -18,6 +18,44 @@ import plotly.colors
|
|
18
18
|
import math
|
19
19
|
|
20
20
|
class Color:
|
21
|
+
|
22
|
+
@staticmethod
|
23
|
+
def AnyToHex(color):
|
24
|
+
"""
|
25
|
+
Converts a color to a hexadecimal color string.
|
26
|
+
|
27
|
+
Parameters
|
28
|
+
----------
|
29
|
+
color : list or str
|
30
|
+
The input color parameter which can be any of RGB, CMYK, CSS Named Color, or Hex
|
31
|
+
|
32
|
+
Returns
|
33
|
+
-------
|
34
|
+
str
|
35
|
+
A hexadecimal color string in the format '#RRGGBB'.
|
36
|
+
"""
|
37
|
+
return_hex = None
|
38
|
+
if isinstance(color, list):
|
39
|
+
if len(color) == 4: # Probably CMYK
|
40
|
+
if all(0 <= x <= 1 for x in color[:4]):
|
41
|
+
return_hex = Color.CMYKToHex(color[:4])
|
42
|
+
elif len(color) == 3:
|
43
|
+
if all(0 <= x <= 255 for x in color[:3]):
|
44
|
+
return_hex = Color.RGBToHex(color[:3])
|
45
|
+
elif isinstance(color, str): # Probably a CSSColor
|
46
|
+
if color.lower() in [x.lower() for x in Color.CSSNamedColors()]:
|
47
|
+
rgb = Color.ByCSSNamedColor(color.lower())
|
48
|
+
return_hex = Color.RGBToHex(rgb)
|
49
|
+
else: # Probably alread a HEX or other Plotly-compatible string
|
50
|
+
return_hex = color
|
51
|
+
|
52
|
+
if not isinstance(return_hex, str):
|
53
|
+
print("Color.AnyToHex - Error: Could not recognize the input parameter. Returning None.")
|
54
|
+
return None
|
55
|
+
|
56
|
+
return return_hex.upper()
|
57
|
+
|
58
|
+
|
21
59
|
@staticmethod
|
22
60
|
def ByCSSNamedColor(color, alpha: float = None):
|
23
61
|
"""
|
@@ -262,6 +300,34 @@ class Color:
|
|
262
300
|
rgbList.append(alpha)
|
263
301
|
return rgbList
|
264
302
|
|
303
|
+
@staticmethod
|
304
|
+
def CMYKToHex(cmyk):
|
305
|
+
"""
|
306
|
+
Convert a CMYK color (list of 4 values) to its hexadecimal representation.
|
307
|
+
|
308
|
+
Parameters
|
309
|
+
----------
|
310
|
+
color : list
|
311
|
+
cmyk (list or tuple): CMYK color values as [C, M, Y, K], each in the range 0 to 1.
|
312
|
+
|
313
|
+
Returns
|
314
|
+
-------
|
315
|
+
str: The hexadecimal color string for Plotly (e.g., '#FFFFFF').
|
316
|
+
"""
|
317
|
+
c, m, y, k = cmyk
|
318
|
+
|
319
|
+
# Convert CMYK to RGB
|
320
|
+
r = 255 * (1 - c) * (1 - k)
|
321
|
+
g = 255 * (1 - m) * (1 - k)
|
322
|
+
b = 255 * (1 - y) * (1 - k)
|
323
|
+
|
324
|
+
# Clamp RGB values to 0-255 range and convert to integers
|
325
|
+
r, g, b = int(round(r)), int(round(g)), int(round(b))
|
326
|
+
|
327
|
+
# Convert RGB to hex format
|
328
|
+
hex_color = "#{:02x}{:02x}{:02x}".format(r, g, b)
|
329
|
+
return hex_color
|
330
|
+
|
265
331
|
@staticmethod
|
266
332
|
def CSSNamedColor(color):
|
267
333
|
"""
|
@@ -355,7 +421,7 @@ class Color:
|
|
355
421
|
"mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
|
356
422
|
"mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navy", "oldlace",
|
357
423
|
"olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen", "paleturquoise",
|
358
|
-
"palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple",
|
424
|
+
"palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", "purple",
|
359
425
|
"red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna",
|
360
426
|
"silver", "skyblue", "slateblue", "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan",
|
361
427
|
"teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen"
|
topologicpy/Dictionary.py
CHANGED
@@ -344,6 +344,97 @@ class Dictionary():
|
|
344
344
|
values.append(pythonDictionary[key])
|
345
345
|
return Dictionary.ByKeysValues(keys, values)
|
346
346
|
|
347
|
+
@staticmethod
|
348
|
+
def Filter(elements, dictionaries, searchType="any", key=None, value=None):
|
349
|
+
"""
|
350
|
+
Filters the input list of dictionaries based on the input parameters.
|
351
|
+
|
352
|
+
Parameters
|
353
|
+
----------
|
354
|
+
elements : list
|
355
|
+
The input list of elements to be filtered according to the input dictionaries.
|
356
|
+
dictionaries : list
|
357
|
+
The input list of dictionaries to be filtered.
|
358
|
+
searchType : str , optional
|
359
|
+
The type of search query to conduct in the topology's dictionary. This can be one of "any", "equal to", "contains", "starts with", "ends with", "not equal to", "does not contain". The default is "any".
|
360
|
+
key : str , optional
|
361
|
+
The dictionary key to search within. The default is None which means it will filter by topology type only.
|
362
|
+
value : str , optional
|
363
|
+
The value to search for at the specified key. The default is None which means it will filter by topology type only.
|
364
|
+
|
365
|
+
Returns
|
366
|
+
-------
|
367
|
+
dict
|
368
|
+
A dictionary of filtered and other elements. The dictionary has two keys:
|
369
|
+
- "filtered" The filtered dictionaries.
|
370
|
+
- "other" The other dictionaries that did not meet the filter criteria.
|
371
|
+
- "filteredIndices" The filtered indices of dictionaries.
|
372
|
+
- "otherIndices" The other indices of dictionaries that did not meet the filter criteria.
|
373
|
+
|
374
|
+
"""
|
375
|
+
|
376
|
+
from topologicpy.Topology import Topology
|
377
|
+
|
378
|
+
def listToString(item):
|
379
|
+
returnString = ""
|
380
|
+
if isinstance(item, list):
|
381
|
+
if len(item) < 2:
|
382
|
+
return str(item[0])
|
383
|
+
else:
|
384
|
+
returnString = item[0]
|
385
|
+
for i in range(1, len(item)):
|
386
|
+
returnString = returnString+str(item[i])
|
387
|
+
return returnString
|
388
|
+
|
389
|
+
filteredDictionaries = []
|
390
|
+
otherDictionaries = []
|
391
|
+
filteredElements = []
|
392
|
+
otherElements = []
|
393
|
+
filteredIndices = []
|
394
|
+
otherIndices = []
|
395
|
+
for i, aDictionary in enumerate(dictionaries):
|
396
|
+
if not Topology.IsInstance(aDictionary, "Dictionary"):
|
397
|
+
continue
|
398
|
+
if value == "" or key == "" or value == None or key == None:
|
399
|
+
filteredDictionaries.append(aDictionary)
|
400
|
+
filteredIndices.append(i)
|
401
|
+
else:
|
402
|
+
if isinstance(value, list):
|
403
|
+
value.sort()
|
404
|
+
value = str(value)
|
405
|
+
value.replace("*",".+")
|
406
|
+
value = value.lower()
|
407
|
+
v = Dictionary.ValueAtKey(aDictionary, key)
|
408
|
+
if v == None:
|
409
|
+
otherDictionaries.append(aDictionary)
|
410
|
+
otherIndices.append(i)
|
411
|
+
otherElements.append(elements[i])
|
412
|
+
else:
|
413
|
+
v = str(v).lower()
|
414
|
+
if searchType.lower() == "equal to":
|
415
|
+
searchResult = (value == v)
|
416
|
+
elif searchType.lower() == "contains":
|
417
|
+
searchResult = (value in v)
|
418
|
+
elif searchType.lower() == "starts with":
|
419
|
+
searchResult = (value == v[0: len(value)])
|
420
|
+
elif searchType.lower() == "ends with":
|
421
|
+
searchResult = (value == v[len(v)-len(value):len(v)])
|
422
|
+
elif searchType.lower() == "not equal to":
|
423
|
+
searchResult = not (value == v)
|
424
|
+
elif searchType.lower() == "does not contain":
|
425
|
+
searchResult = not (value in v)
|
426
|
+
else:
|
427
|
+
searchResult = False
|
428
|
+
if searchResult == True:
|
429
|
+
filteredDictionaries.append(aDictionary)
|
430
|
+
filteredIndices.append(i)
|
431
|
+
filteredElements.append(elements[i])
|
432
|
+
else:
|
433
|
+
otherDictionaries.append(aDictionary)
|
434
|
+
otherIndices.append(i)
|
435
|
+
otherElements.append(elements[i])
|
436
|
+
return {"filteredDictionaries": filteredDictionaries, "otherDictionaries": otherDictionaries, "filteredIndices": filteredIndices, "otherIndices": otherIndices, "filteredElements": filteredElements, "otherElements": otherElements}
|
437
|
+
|
347
438
|
@staticmethod
|
348
439
|
def Keys(dictionary):
|
349
440
|
"""
|
@@ -579,7 +670,7 @@ class Dictionary():
|
|
579
670
|
temp_value = attr.StringValue()
|
580
671
|
topologies = None
|
581
672
|
try:
|
582
|
-
topologies = Topology.ByJSONString(temp_value
|
673
|
+
topologies = Topology.ByJSONString(temp_value)
|
583
674
|
except:
|
584
675
|
topologies = None
|
585
676
|
if isinstance(topologies, list):
|
@@ -654,14 +745,21 @@ class Dictionary():
|
|
654
745
|
if not silent == True:
|
655
746
|
print("Dictionary.ValueAtKey - Error: The input key parameter is not a valid str. Returning None.")
|
656
747
|
return None
|
657
|
-
if
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
748
|
+
if Topology.IsInstance(dictionary, "Dictionary"):
|
749
|
+
dic = Dictionary.PythonDictionary(dictionary)
|
750
|
+
return dic.get(key, None)
|
751
|
+
elif isinstance(dictionary, dict):
|
752
|
+
return dictionary.get(key, None)
|
753
|
+
return None
|
754
|
+
|
755
|
+
# if isinstance(dictionary, dict):
|
756
|
+
# attr = dictionary[key]
|
757
|
+
# elif Topology.IsInstance(dictionary, "Dictionary"):
|
758
|
+
# attr = dictionary.ValueAtKey(key)
|
759
|
+
# else:
|
760
|
+
# return None
|
761
|
+
# return_value = Dictionary._ConvertAttribute(attr)
|
762
|
+
# return return_value
|
665
763
|
|
666
764
|
@staticmethod
|
667
765
|
def Values(dictionary):
|
@@ -693,7 +791,7 @@ class Dictionary():
|
|
693
791
|
for key in keys:
|
694
792
|
try:
|
695
793
|
if isinstance(dictionary, dict):
|
696
|
-
attr = dictionary
|
794
|
+
attr = dictionary.get(key, None)
|
697
795
|
elif Topology.IsInstance(dictionary, "Dictionary"):
|
698
796
|
attr = Dictionary.ValueAtKey(dictionary,key)
|
699
797
|
else:
|
topologicpy/Edge.py
CHANGED
@@ -1143,6 +1143,8 @@ class Edge():
|
|
1143
1143
|
from topologicpy.Vertex import Vertex
|
1144
1144
|
from topologicpy.Topology import Topology
|
1145
1145
|
|
1146
|
+
|
1147
|
+
|
1146
1148
|
def calculate_normal(start_vertex, end_vertex):
|
1147
1149
|
start_vertex = np.array([float(x) for x in start_vertex])
|
1148
1150
|
end_vertex = np.array([float(x) for x in end_vertex])
|
@@ -1150,8 +1152,11 @@ class Edge():
|
|
1150
1152
|
# Calculate the direction vector of the edge
|
1151
1153
|
direction_vector = end_vertex - start_vertex
|
1152
1154
|
|
1153
|
-
#
|
1154
|
-
if np.isclose(direction_vector[
|
1155
|
+
# Check if the edge is vertical (only Z component)
|
1156
|
+
if np.isclose(direction_vector[0], 0) and np.isclose(direction_vector[1], 0):
|
1157
|
+
# Choose an arbitrary perpendicular vector in the X-Y plane, e.g., [1, 0, 0]
|
1158
|
+
normal_vector = np.array([1.0, 0.0, 0.0])
|
1159
|
+
elif np.isclose(direction_vector[2], 0):
|
1155
1160
|
# The edge lies in the X-Y plane; compute a perpendicular in the X-Y plane
|
1156
1161
|
normal_vector = np.array([-direction_vector[1], direction_vector[0], 0.0])
|
1157
1162
|
else:
|
@@ -1162,7 +1167,7 @@ class Edge():
|
|
1162
1167
|
# Check if the normal vector is effectively zero before normalization
|
1163
1168
|
if np.isclose(norm(normal_vector), 0):
|
1164
1169
|
return normal_vector
|
1165
|
-
|
1170
|
+
|
1166
1171
|
# Normalize the normal vector
|
1167
1172
|
normal_vector /= norm(normal_vector)
|
1168
1173
|
return normal_vector
|
@@ -1192,7 +1197,6 @@ class Edge():
|
|
1192
1197
|
|
1193
1198
|
# Calculate the normal line
|
1194
1199
|
normal_line_start, normal_line_end = calculate_normal_line(start_vertex, end_vertex)
|
1195
|
-
|
1196
1200
|
# Create the normal edge in Topologic
|
1197
1201
|
sv = Vertex.ByCoordinates(list(normal_line_start))
|
1198
1202
|
ev = Vertex.ByCoordinates(list(normal_line_end))
|
topologicpy/Face.py
CHANGED
@@ -2566,7 +2566,7 @@ class Face():
|
|
2566
2566
|
internal_face_number = len(face_internal_boundaries)
|
2567
2567
|
for i in range(internal_face_number):
|
2568
2568
|
face_internal_boundary = face_internal_boundaries[i]
|
2569
|
-
internal_vertices =
|
2569
|
+
internal_vertices = Topology.Vertices(face_internal_boundary)
|
2570
2570
|
internal_vertex_number = len(internal_vertices)
|
2571
2571
|
for j in range(internal_vertex_number):
|
2572
2572
|
gmsh.model.geo.addPoint(Vertex.X(internal_vertices[j]), Vertex.Y(internal_vertices[j], mantissa=mantissa), Vertex.Z(internal_vertices[j], mantissa=mantissa), meshSize, current_vertex_number+j+1)
|